反射式DLL注入的核心动作是DLL自身在内存中手动完成PE加载全过程:解析PE头、修复重定位表、填充导入地址表(IAT)、执行TLS回调,最后跳转DllMain;全程不依赖LoadLibrary、不写磁盘文件、不触发系统加载器。

什么是反射式DLL注入的核心动作
反射式DLL注入的本质,不是靠 Windows 的 LoadLibrary 去加载磁盘上的 DLL,而是把 DLL 的原始字节(PE 映像)直接读入内存,手动完成重定位、导入表解析、TLS 初始化等原本由系统 loader 干的活,最后跳转到其 DllMain 入口。关键在于:整个过程不写入任何临时文件,也不调用 LoadLibrary 或 LoadLibraryEx。
为什么标准 LoadLibrary 不满足“无落地”要求
使用 LoadLibrary 加载 DLL 时,Windows loader 会检查 PE 头、验证签名、可能触发 AMSI 扫描,并且——更重要的是——它默认会从磁盘路径加载。即使你把 DLL 内存块写入目标进程后尝试传路径,也绕不开磁盘 IO 和文件句柄创建,这违反“无文件落地”前提。
- 调用
LoadLibrary时若传入的是内存中伪造的路径(如"C:\fake.dll"),系统会报错ERROR_FILE_NOT_FOUND - 试图用
SetThreadContext+CreateRemoteThread去执行一段 shellcode 调用LoadLibrary,仍需提供合法磁盘路径,无法规避落地 - 部分 EDR 会 Hook
LoadLibrary入口,直接拦截并告警;而反射式注入的入口是自定义 shellcode,更隐蔽
实现反射式注入必须手动处理的三件事
反射式 DLL 必须在自身代码里实现 loader 功能,典型流程包含以下不可省略步骤:
-
重定位(Relocation):DLL 编译时默认基址(
ImageBase)和实际分配地址不同,需遍历.reloc节,修正所有绝对地址引用(如函数指针、全局变量偏移) -
导入表(Import Table)解析:遍历
IMAGE_IMPORT_DESCRIPTOR,对每个依赖 DLL(如kernel32.dll)调用GetModuleHandleA+GetProcAddress获取函数地址,并填入FirstThunk指向的 IAT 表 -
TLS 回调执行(可选但常见):若 DLL 含 TLS 目录(
IMAGE_DIRECTORY_ENTRY_TLS),需按顺序调用每个 TLS 回调函数,否则某些库(如 C 运行时)可能崩溃
漏掉任一环节,DLL 很可能在 DllMain 执行前就访问非法地址或调用空指针——表现为静默退出、访问冲突(0xC0000005),或卡死在初始化阶段。
立即学习“C++免费学习笔记(深入)”;
反射式注入的典型 Shellcode 结构与陷阱
注入端(Injector)需将反射式 loader shellcode + 原始 DLL 数据一起写入目标进程,然后创建远程线程执行 shellcode 起始地址。常见错误包括:
- shellcode 中硬编码了 32/64 位地址(如
0x7FF…),导致跨平台失败;应使用相对寻址或运行时计算偏移 - DLL 数据未按 PE 文件对齐(
SectionAlignment)写入内存,导致重定位项解析错位 - 未正确设置目标内存页权限:
VirtualProtectEx需先设为PAGE_READWRITE写入数据,再改为PAGE_EXECUTE_READ才能执行 shellcode - shellcode 调用的 API(如
GetModuleHandleA)未做延迟加载或手动解析,导致找不到符号——推荐用 hash 替代字符串(如Hash("kernel32.dll GetProcAddr "))规避字符串扫描
真正难的不是写 loader,而是让 loader 在任意目标进程(尤其是低权限、ASLR 开启、CFG 启用)里稳定跑通第一轮重定位和导入解析。很多 PoC 在测试进程里能跑,换到 explorer.exe 或 chrome.exe 就崩,往往卡在 TLS 或 IAT 填充的边界条件上。


















