CreateRemoteThread注入DLL在现代Windows上大概率失败,因受PPL、CFG、AMPI等安全机制限制,且Win10 1607起加强签名与权限校验,导致ERROR_ACCESS_DENIED等错误频发。

为什么 CreateRemoteThread 注入 DLL 在现代 Windows 上大概率失败
因为默认情况下,CreateRemoteThread 无法在受保护进程(PPL)、启用 CFG(Control Flow Guard)或开启 AMPI(Arbitrary Code Guard)的进程中执行任意代码;更关键的是,从 Windows 10 1607 起,系统对远程线程创建做了严格签名与权限校验——即使你有 SeDebugPrivilege,目标进程若启用了 PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECK_POLICY 或运行在低完整性级别(如 Edge、Chrome 渲染器),CreateRemoteThread 会直接返回 ERROR_ACCESS_DENIED。
常见错误现象:CreateRemoteThread 返回 NULL,GetLastError() 是 5(拒绝访问)或 998(内存访问无效);DLL 虽然被写入,但线程未启动,LoadLibraryA 地址解析失败。
- 必须以管理员权限运行注入器,并启用
SeDebugPrivilege(否则连OpenProcess都失败) - 目标进程不能是受保护进程(如
csrss.exe、winlogon.exe、带PPL标志的svchost.exe) -
LoadLibraryA的地址需从目标进程的kernel32.dll中解析,不能硬编码(ASLR 下每次加载基址不同) - 64 位进程不能向 32 位进程注入(反之亦然),
IsWow64Process必须校验
如何正确获取 LoadLibraryA 在目标进程中的真实地址
不能用当前进程的 GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA") —— 这个地址在目标进程中极大概率无效。必须在目标进程上下文中解析,最可靠方式是:先用 EnumProcessModules(需 PSAPI)或遍历 PEB 找到 kernel32.dll 在目标进程的加载基址,再手动解析其导出表。
实操建议:
立即学习“C++免费学习笔记(深入)”;
使用 Infinimo AI 设计制作手持产品短视频:Avatar 首帧合成、AI 脚本、图转视频提交与轮询。适用于可购物短视频、Avatar 主导的产品演示和商品 listing 宣传视频。
- 优先使用
EnumProcessModules+GetModuleFileNameEx确认kernel32.dll模块句柄,再调用GetProcAddress(注意:该函数在调用者进程中执行,但传入的是目标进程的模块句柄 —— 实际上 Windows 不允许跨进程传模块句柄,所以这步只是示意;真实做法是读取目标进程内存,解析 PE 头 + 导出表) - 更稳妥的做法:用
ReadProcessMemory读取目标进程的 PEB(偏移0x30在 x64 上是Ldr字段),遍历LDR_DATA_TABLE_ENTRY链表,比对模块名,定位kernel32.dll的DllBase,再解析其IMAGE_EXPORT_DIRECTORY - 简化方案(仅限测试):用
VirtualAllocEx分配可执行内存后,写入一段 shellcode,功能为「调用本进程已知的LoadLibraryA」——但这要求目标进程和注入器架构一致,且绕不过 CFG 检查
注入时容易被 AV/EDR 直接拦截的关键点
几乎所有商用 EDR(如 Defender、CrowdStrike、SentinelOne)都会 Hook CreateRemoteThread、WriteProcessMemory、VirtualAllocEx 等 API,并检测如下行为:
- 目标进程是否为浏览器、Office、微信等高价值进程(白名单外即告警)
-
WriteProcessMemory写入的内容是否包含LoadLibrary、GetProcAddress字符串或典型 shellcode 特征(如0x48, 0x83, 0xEC, 0x28即 sub rsp,40) - 申请的远程内存是否同时具备
PAGE_READWRITE和PAGE_EXECUTE_READ(即PAGE_EXECUTE_READWRITE,非常可疑) - 线程起始地址是否落在
kernel32.dll/ntdll.dll之外的可写内存页中
绕过思路(不保证有效):分阶段申请内存(先 WRITE,再 PROTECT 改为可执行),字符串加密(如 XOR 加密 "mylib.dll"),用 RtlCreateUserThread 替代 CreateRemoteThread(需 Native API,且仍受 PatchGuard 限制)。
替代方案比硬刚 CreateRemoteThread 更实际
如果你的目标只是让代码在第三方进程中运行,而不是执着于“传统 DLL 注入”,以下方法兼容性更高、触发告警概率更低:
-
反射式 DLL 注入(Reflective DLL Injection):把 DLL 自身实现为可重定位 blob,注入器只写入这段二进制,再跳转到其入口点。不依赖
LoadLibrary,规避了导出地址解析问题,也减少对kernel32的显式调用 -
APC 注入(QueueUserAPC):适用于目标进程存在消息循环或处于等待状态(如
WaitForSingleObject)。需先挂起线程,用QueueUserAPC注入,但仅支持 x64 进程中向已挂起的线程投递(x86 限制更多) -
注册表 AppInit_DLLs(仅限桌面应用,Win10 1809+ 默认禁用):修改
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs,但要求 DLL 签名、目标进程启用AppInit(默认关闭) - IE BHO / 浏览器扩展接口:针对特定进程(如 iexplore.exe),走合法扩展机制,完全避开远程线程
真正棘手的从来不是怎么调用 CreateRemoteThread,而是目标进程的缓解策略组合 —— CFG + CET + PPL + EDR Hook,四层叠加下,裸调 API 几乎没有存活空间。动手前先用 Process Explorer 看一眼目标进程的“Protection”列和“Integrity Level”,比写十行注入代码更有用。

















