子文档 4:高级注入技法(Thunkless + Reparse)
1. Thunkless 方法(InjpInjectX64NoThunk)
此方法仅当InjMethodThunkless且编译为 x64 时启用。代码位于injlib.c中。
它不再分配可执行 shellcode,而是将用户 APC 的NormalRoutine直接设定为InjectionInfo->LdrLoadDllRoutineAddress(即 x64ntdll.dll中的LdrLoadDll地址)。参数传递:
NormalContext = NULLSystemArgument1 = NULLSystemArgument2 = DllPath(指向在用户空间分配的UNICODE_STRING)
关键设计是利用 x64 的KiUserApcDispatcher行为:
- 该函数在调用
NormalRoutine前,会将RSP(指向CONTEXT结构)放入R9。 - 在 x64 调用约定中,
R9是第 4 个参数寄存器。 - 因此
LdrLoadDll被调用时,隐含的第 4 个参数BaseAddress指向了CONTEXT结构。 LdrLoadDll写入BaseAddress时,会覆写CONTEXT.P1Home,而该字段在 APC 分发后续流程(调用ZwContinue前)不再被使用,因此安全。
这种方法的精妙之处在于零可执行内存分配,完美绕过 CFG 针对低 4GB 地址的执行限制。
2. Reparse 微过滤器(src/injlib/reparse.c)
该文件约 400 行,实现了基于 Minifilter 的文件路径重解析。其入口为SimRepInitialize,但此函数并非由DriverEntry直接调用,而是在InjInitialize中判断InjMethod == InjMethodWow64LogReparse时调用。
SimRepInitialize做了两件重要的事:
- 注册表注入:通过
SimRepInitializeRegistry在驱动服务项下创建Instances\Reparse键,写入Altitude = "370040"和Flags = 0。这使得fltmgr能加载该过滤器。 - FltRegisterFilter:注册
FLT_REGISTRATION,其中InstanceSetup、InstanceQueryTeardown被简单实现(直接返回成功),而OperationRegistration仅针对IRP_MJ_CREATE注册了预处理回调SimRepPreCreate。
3. SimRepPreCreate 的重解析逻辑
每次文件打开时,此回调会获取FLT_FILE_NAME_INFORMATION。它调用SimRepCompareMapping检查路径是否以\Windows\System32\wow64log.dll结尾。若是,则调用SimRepMungeName构造新路径。
SimRepMungeName会分配新缓冲区,将旧路径中的wow64log.dll替换为NewName(即injdll的路径)。注意,NewName在SimRepInitialize中被初始化为InjDllPath[InjArchitectureNative].Buffer + 2,加 2 是为了跳过盘符前缀(如C:),保留\Windows\...部分。
最后,调用IoReplaceFileObjectName直接替换FILE_OBJECT中的名称,并返回STATUS_REPARSE给 I/O 管理器。I/O 管理器收到STATUS_REPARSE后会重新发起打开请求,此时路径已被替换,从而加载了 injdll。
4. 为什么不直接用符号链接?
符号链接作用于命名空间层级,而此处需要精确拦截特定 DLL 的加载路径。使用 Minifilter 可以做到进程级别的精准拦截,且不污染全局命名空间。此外,IoReplaceFileObjectName直接修改FILE_OBJECT是内核态特有的“偷梁换柱”手段,用户态无法实现。