最可靠dll注入方式是远程加载而非直接写shellcode:申请内存→写入dll路径→调用loadlibrarya;需注意权限标志、字符串结尾、wow64指针截断、aslr下动态获取loadlibrarya地址等关键细节。

Windows下用WriteProcessMemory注入DLL最常用但需绕过DEP/ASLR
直接往目标进程内存写shellcode在现代系统上基本会触发DEP或AV拦截,实际工程中更可靠的做法是远程加载DLL。核心步骤是:在目标进程申请内存 → 写入DLL路径字符串 → 创建远程线程调用LoadLibraryA。
常见错误包括:WriteProcessMemory失败(权限不足)、路径字符串没以\0结尾、没处理目标进程是Wow64时的指针截断、LoadLibraryA地址在不同系统版本不一致(应从kernel32.dll中动态获取)。
实操建议:
- 用
OpenProcess时必须带PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE标志,仅PROCESS_ALL_ACCESS在UAC提升后仍可能失败 - 申请内存用
VirtualAllocEx,分配类型选MEM_COMMIT | MEM_RESERVE,保护属性设为PAGE_READWRITE(后续再用VirtualProtectEx改为PAGE_EXECUTE_READ仅当执行shellcode时需要) - 调用
CreateRemoteThread前,确认LoadLibraryA地址来自目标进程上下文中的kernel32.dll,不能硬编码;可用GetModuleHandleA("kernel32.dll")+GetProcAddress在本进程中取,再通过ReadProcessMemory验证目标进程是否映射在同一基址(ASLR开启时大概率不同,需用EnumProcessModules查真实地址)
Linux下ptrace+syscall实现dlopen注入需root且兼容glibc版本
Linux没有“远程线程”概念,主流做法是用ptrace(PTRACE_ATTACH)暂停目标进程,修改其寄存器和栈,注入syscall(SYS_rt_sigreturn)触发用户态函数调用,最终执行dlopen。这要求注入代码本身是位置无关的(PIC),且链接时不能依赖目标进程未加载的符号。
典型失败场景:ptrace被ptrace_scope限制(Ubuntu默认为1)、libc版本不匹配导致dlopen符号解析失败、栈空间估算不足引发SIGSEGV。
实操建议:
- 注入前先检查
/proc/sys/kernel/yama/ptrace_scope,值为0才允许非父子进程trace;否则需临时改写或用sudo - 不要手写汇编调用
dlopen,改用injectso这类成熟工具生成的payload,它会自动处理PLT/GOT重定位和libc符号查找 - 目标进程若为静态链接(如musl),
dlopen不可用,此时只能用LD_PRELOAD环境变量预设(但只对后续fork有效,无法影响已运行主线程)
macOS上task_for_pid已被废弃,仅限调试签名进程且需entitlements
从macOS 10.14开始,task_for_pid默认返回KERN_FAILURE,即使有root权限。唯一合法路径是:目标进程启用get-task-allow entitlement,并由同一开发者证书签名的调试器调用。普通代码注入(如hook函数)必须走dyld插件机制或mach_inject这类利用内核漏洞的方案(不推荐生产环境)。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
容易忽略的点:codesign --entitlements指定的plist必须包含<key>com.apple.security.get-task-allow</key><true></true>,且签名后不能修改二进制,否则签名失效;Xcode中对应设置是“Enable Hardened Runtime”必须关闭,否则entitlements会被忽略。
实操建议:
- 验证签名有效性用
codesign -d --entitlements :- /path/to/binary,输出中必须看到get-task-allow为true - 调试器与目标进程需共用同一Team ID,否则
task_for_pid返回KERN_INVALID_ARGUMENT - 注入后若目标进程启用了
__RESTRICT段(如SIP保护的系统进程),vm_protect修改内存权限会失败,此时只能注入到用户级进程(如自己开发的App)
跨平台通用陷阱:线程局部存储TLS和C++全局构造函数不自动触发
注入DLL或so后,其中定义的__attribute__((constructor))函数或C++全局对象的构造函数不会自动执行——因为动态链接器(ld-linux.so、dyld、Windows loader)只在初始加载时运行这些逻辑。手动dlopen或LoadLibrary绕过了这一流程。
这意味着:依赖TLS变量的代码可能读到未初始化值、单例对象未实例化、std::ios_base::sync_with_stdio等初始化未生效。
实操建议:
- 把初始化逻辑显式封装进一个导出函数(如
InitPlugin()),注入后立即用GetProcAddress/dlsym获取并调用 - 避免在DLL/so的全局作用域使用
thread_local变量,改用pthread_key_create或Windows的TlsAlloc手动管理 - 若必须用C++全局对象,可在DLL入口点
DllMain(Windows)或__attribute__((constructor))(Linux/macOS)中触发一次std::call_once,但注意DllMain里禁止调用LoadLibrary等可能引发死锁的API
注入不是写个memcpy就完事的事。真正卡住人的永远是权限模型细节、运行时环境差异、以及那些“理论上可行但实际被系统策略拦住”的边界情况。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










