如何使用LdrInitShimEngineDynamic()捕获进程内DLL加载事件及生效范围
问题
如何在自有应用中使用LdrInitShimEngineDynamic(),让自身.exe的指定函数在以下场景下收到通知:
- 自行通过
LoadLibrary()等方式将DLL动态加载到进程时 - 他人通过
CreateRemoteThread()等方式向当前进程加载DLL时
要求通知触发时机满足: - 发生在
DllMain()及DLL入口点执行之前 - DLL的基重定位与导入表已完成解析
同时需明确:该函数是捕获整个系统的DLL加载事件,还是仅针对当前进程?
实现方案与说明
1. LdrInitShimEngineDynamic()的使用步骤
LdrInitShimEngineDynamic()是ntdll.dll提供的加载器垫片引擎初始化接口,要实现需求需按以下步骤操作:
- 定义自定义垫片回调:编写符合垫片引擎要求的回调函数,原型需匹配加载器的调用规范,示例原型如下:
该函数会在指定时机被加载器调用,参数包含被加载DLL的名称和基地址。VOID WINAPI MyDllLoadCallback(PCUNICODE_STRING DllName, PVOID DllBase); - 获取函数地址:通过
GetProcAddress从ntdll.dll中获取LdrInitShimEngineDynamic的函数指针(该函数未公开在官方头文件中)。 - 初始化并注册垫片:调用
LdrInitShimEngineDynamic,传入自定义垫片的配置(如垫片标识、回调函数指针等),让加载器将我们的垫片纳入其加载流程。
2. 触发时机验证
结合Win7 x64下NT加载器的调用流程(翻译后如下):
LoadLibraryExW() LdrLoadDll() LdrpLoadDll() LdrpLoadDllInternal() LdrpProcessWork() LdrpMapDllFullPath() LdrpMapDllNtfileName() LdrpMapDllWithSectionHandle() LdrpMinimalMapModule() LdrpMapViewOfSection() LdrpCompleteMapModule() LdrpRelocateImage() LdrpProtectAndRelocateImage(); LdrpSetProtection() LdrpRelocateImageWithBias() <-- DLL基重定位完成 LdrpProcessMappedModule() LdrpCorProcessImports() LdrpMapAndSnapDependency() <-- DLL导入表解析完成 LdrpPrepareImportAddressTableForSnap() LdrpPrepareModuleForExecution() LdrpInitializeGraphRecurse() LdrpInitializeNode() LdrpCallInitRoutine() <-- 此处调用自定义垫片,随后执行DllMain
可以看到:
- 垫片回调会在
LdrpCallInitRoutine中被触发,此时DLL的基重定位和导入表解析均已完成 - 触发时机早于
DllMain的执行,完全符合需求
3. 作用范围说明
LdrInitShimEngineDynamic()仅作用于调用该函数的当前进程,无法捕获整个系统的DLL加载事件。它是将垫片注入当前进程的加载器流程中,仅监控当前进程内的所有DLL加载操作——不管是自身调用LoadLibrary的主动加载,还是外部通过CreateRemoteThread注入的被动加载,都会被垫片回调捕获。
内容的提问来源于stack exchange,提问作者George Robinson
相关产品推荐
相关产品推荐

