)
子文档 2核心状态机与回调分发src/injlib/injlib.c 上半部injlib.c是驱动最核心的库文件约 600 行代码实现了状态跟踪、DLL 加载判定以及 APC 队列。它不依赖injdrv/main.c中的逻辑而是独立提供 API 供其调用。1. 全局数据结构与链表管理InjInfoListHead一个双向链表头用于挂载所有待注入进程的INJ_INJECTION_INFO。InjDllPath[InjArchitectureMax]全局存储从Settings复制过来的完整 DLL 路径通过RtlDuplicateUnicodeString深拷贝。InjThunk[InjArchitectureMax]指向硬编码的 shellcode 数组InjpThunkX86、InjpThunkX64等。InjCreateInjectionInfo分配并初始化INJ_INJECTION_INFO。注意它允许传入一个已存在的指针用于某些调试场景但通常传入NULL使其内部申请。成功后将节点插入链表尾。2. 进程创建/退出回调InjCreateProcessNotifyRoutineEx该回调原型符合PSCREATEPROCESSNOTIFYROUTINEEX。当CreateInfo非空时表示进程正在创建此时调用InjCreateInjectionInfo(NULL, ProcessId)创建跟踪节点。当CreateInfo为空时表示进程退出调用InjRemoveInjectionInfoByProcessId(ProcessId, TRUE)移除并释放节点。这里隐含了一个关键假设新进程的ntdll.dll尚未加载时进程回调已经触发。实际上Windows 在创建进程对象后、映射ntdll.dll前会调用此回调因此创建节点是安全的。3. 映像加载回调InjLoadImageNotifyRoutine这是最复杂的逻辑所在约 150 行。每次系统映射 DLL 或 EXE 时都会调用。函数首先通过InjFindInjectionInfo(ProcessId)查找节点。若节点不存在或已被标记IsInjected则直接返回。随后检查PsIsProtectedProcess若为受保护进程PPL则输出调试信息并移除节点放弃注入。接下来进入状态机核心步骤一若未满足注入条件!InjCanInject则遍历InjpSystemDlls数组通过RtlxSuffixUnicodeString比对当前加载的FullImageName是否以这些系统 DLL 的路径结尾如\System32\ntdll.dll。若匹配则设置LoadedDlls对应标志位。特别地当加载的是ntdll.dll时通过RtlxFindExportedRoutineByName捕获其LdrLoadDll的地址存入节点供后续注入使用。步骤二若已满足注入条件InjCanInject返回 TRUE则调用InjpQueueApc(KernelMode, ...)将InjpInjectApcNormalRoutine作为 APC 的NormalRoutine排队并以InjectionInfo作为参数。紧接着将IsInjected置为 TRUE避免重复注入。4. Windows 7 的特殊补丁在InjCanInject返回 TRUE 后若检测到InjIsWindows7且为 Wow64 进程且使用 Thunk 方法则会额外判断当前加载的 DLL 是否为kernel32.dll或user32.dll原生和 Wow64 双版本。只有这些 DLL 也加载完成才真正执行注入。这是对 Windows 7 下 Wow64 初始化顺序差异的精准适配。5. RtlxSuffixUnicodeString 与 RtlxFindExportedRoutineByName由于这些 Rtl 函数在旧版 Windows 上可能不导出项目自己实现了等效函数。RtlxSuffixUnicodeString使用RtlCompareUnicodeStrings进行字符串后缀匹配忽略大小写。RtlxFindExportedRoutineByName则通过解析 PE 导出表进行二分查找仅查找函数名不查找序号确保了兼容性。