Windows用户态下常见的DLL注入技术总结
这里总结一下Windows用户态下常见的DLL注入技术,从经典的 CreateRemoteThread 到隐蔽性极高的反射式注入等多种方法,并对比其优缺点与适用场景。
1. 基于消息钩子的注入(SetWindowsHookEx)
在安装消息钩子的时候,Windows会检查消息钩子所在的DLL是否位于目标进程中,如果没有会将DLL加载到目标进程的地址空间中,利用这一特性可以进行DLL注入。
实现步骤:
- 编写一个包含钩子处理函数的DLL。
- 在注入器程序中调用
SetWindowsHookEx安装钩子。 - 当钩子被触发时,系统自动将DLL加载到目标进程。
cpp
// 注入器代码示例
HINSTANCE hDll = LoadLibrary(L"inject.dll");
HOOKPROC hkprc = (HOOKPROC)GetProcAddress(hDll, "HookProc");
SetWindowsHookEx(WH_KEYBOARD, hkprc, hDll, dwTargetThreadId);
优点: 实现简单,系统自动完成加载。
缺点: 需要目标进程有消息循环;注入的DLL会随钩子卸载而卸载。
2. 远程线程注入(CreateRemoteThread + LoadLibrary)
我们可以在目标进程中创建一个线程,让该线程执行LoadLibrary加载我们的DLL。
实现步骤:
- 使用
OpenProcess打开目标进程。 - 使用
VirtualAllocEx在目标进程中分配内存,写入DLL路径。 - 使用
CreateRemoteThread创建远程线程,线程函数为LoadLibraryW。
cpp
// 核心代码示例
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwTargetPid);
LPVOID pRemoteMem = VirtualAllocEx(hProcess, NULL, 0x100, MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProcess, pRemoteMem, szDllPath, wcslen(szDllPath) * 2 + 2, NULL);
LPTHREAD_START_ROUTINE pLoadLibrary = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
CreateRemoteThread(hProcess, NULL, 0, pLoadLibrary, pRemoteMem, 0, NULL);
实际上,还可以使用其它可以创建远程线程的函数创建远程线程,如 NtCreateThreadEx、RtlCreateUserThread,只要创建的远程线程执行的是LoadLibrary就可以了。
优点: 经典方法,兼容性好。
缺点: 容易被安全软件检测;需要目标进程有足够的权限。
3. APC注入(QueueUserAPC)
利用Windows的APC(异步过程调用)调用机制进行注入。
原理: 每个线程都有一个APC队列。当线程进入可告警等待状态(如 SleepEx、WaitForSingleObjectEx)时,会依次执行队列中的APC函数。
实现步骤:
- 打开目标线程,获取线程句柄。
- 在目标进程中分配内存并写入DLL路径。
- 调用
QueueUserAPC将LoadLibraryW作为APC函数加入目标线程的APC队列。
cpp
HANDLE hThread = OpenThread(THREAD_SET_CONTEXT, FALSE, dwThreadId);
QueueUserAPC((PAPCFUNC)pLoadLibrary, hThread, (ULONG_PTR)pRemoteMem);
优点: 不需要创建新线程,隐蔽性较好。
缺点: 依赖目标线程进入可告警等待状态,触发时机不确定。
4. 注册表注入(AppInit_DLLs)
利用Windows的AppInit_DLLs机制或KnownDLLs机制进行注入。
AppInit_DLLs方式:
- 将DLL路径写入注册表
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs。 - 设置
LoadAppInit_DLLs为1。 - 系统启动时,所有加载
user32.dll的进程都会自动加载指定的DLL。
reg
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows]
"AppInit_DLLs"="C:\\inject.dll"
"LoadAppInit_DLLs"=dword:00000001
优点: 无需编写注入器代码,系统自动加载。
缺点: 影响范围大(所有加载user32.dll的进程);Win8+默认禁用;容易被安全软件拦截。
注意: 在Win11中,这个注册表项默认是找不到的,查阅资料发现该功能默认关闭且隐藏,需要手动创建。这种注入方式已经不建议使用。
5. DLL劫持
利用Windows加载DLL的顺序进行DLL注入。
原理: 当程序加载DLL时,系统会按照特定顺序搜索DLL。如果攻击者将恶意DLL放在搜索路径的前面(如程序所在目录),系统会优先加载恶意DLL。
搜索顺序(无SafeDllSearchMode时):
- 程序所在目录
- 当前目录
- 系统目录(System32)
- 16位系统目录
- Windows目录
- PATH环境变量中的目录
常见劫持目标: 程序缺失的DLL、已知的DLL劫持漏洞(如 version.dll、dbghelp.dll 等)。
实现方式:
- 分析目标程序加载了哪些DLL。
- 编写一个同名的代理DLL,导出与原DLL相同的函数。
- 在代理函数中调用原DLL的对应函数(转发或转发加载),同时执行恶意代码。
cpp
// 代理DLL示例 - 转发LoadLibrary到原DLL
#pragma comment(linker, "/EXPORT:SomeFunction=original_dll.SomeFunction")
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
// 执行恶意代码
MessageBox(NULL, L"Injected!", L"DLL Hijack", MB_OK);
}
return TRUE;
}
优点: 隐蔽性较好,不需要远程线程。
缺点: 依赖目标程序的DLL加载行为;需要找到合适的劫持目标。
6. 反射式DLL注入
不调用LoadLibrary,而是编写一段shellcode模仿该函数的行为,在目标进程中执行shellcode进行DLL注入。
原理: 手动解析DLL文件格式,在目标进程的内存中完成DLL的加载(包括重定位、导入表处理、TLS回调等),而不调用系统API。
实现步骤:
- 将DLL完整读入内存。
- 编写反射加载器(shellcode),实现:
- 解析DLL的PE结构
- 在目标进程分配内存
- 修复重定位表
- 解析并加载依赖的DLL
- 处理导入表
- 调用DLL入口点
- 将shellcode和DLL数据写入目标进程。
- 通过
CreateRemoteThread等执行shellcode。
优点: 不会修改PEB中那三个与DLL列表相关的字段(InLoadOrderModuleList、InMemoryOrderModuleList、InInitializationOrderModuleList),隐蔽性极高。
缺点: 实现复杂,需要深入理解PE结构;兼容性需要仔细处理。
7. 导入表注入
通过修改目标PE文件的导入表(Import Table),在程序启动时强制加载指定的DLL。
原理: PE文件的导入表中记录了程序运行时需要加载的DLL及其导入函数。通过向导入表中添加新的DLL条目,可以使程序在启动时自动加载恶意DLL。
实现步骤:
- 解析目标PE文件的导入表结构。
- 在导入表末尾添加新的
IMAGE_IMPORT_DESCRIPTOR条目。 - 添加对应的DLL名称字符串和导入函数名称/序号。
- 保存修改后的PE文件。
cpp
// 伪代码示意
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = ...; // 定位到导入表
// 在末尾添加新条目
pImportDesc[newEntry].OriginalFirstThunk = ...;
pImportDesc[newEntry].Name = RVA of "inject.dll";
pImportDesc[newEntry].FirstThunk = ...;
优点: 持久化注入,每次启动都会加载;不需要运行注入器。
缺点: 需要修改磁盘文件;容易被完整性校验检测;需要处理数字签名问题。
各技术对比总结
| 注入技术 | 是否需要远程线程 | 持久性 | 隐蔽性 | 实现复杂度 | 适用场景 |
|---|---|---|---|---|---|
| SetWindowsHookEx | 否(系统自动加载) | 低 | 中 | 低 | 有消息循环的GUI进程 |
| CreateRemoteThread + LoadLibrary | 是 | 低 | 低 | 低 | 通用注入,兼容性最好 |
| QueueUserAPC | 否(利用APC队列) | 低 | 中高 | 中 | 目标线程处于可告警等待状态时 |
| 注册表注入(AppInit_DLLs) | 否 | 高 | 中 | 低 | 全局注入,Win7及以下系统 |
| DLL劫持 | 否 | 高 | 高 | 中 | 针对特定程序,持久化 |
| 反射式DLL注入 | 是 | 低 | 极高 | 高 | 需要绕过PEB检测的场景 |
| 导入表注入 | 否 | 高 | 中 | 高 | 持久化注入,修改磁盘文件 |