Linux下用ptrace实现CreateRemoteThread:处理pending调用后的安全性确认
关于用ptrace实现Linux版CreateRemoteThread的安全注入方案分析
你这个思路抓准了Linux下远程线程注入的核心风险点——mmap和clone这类涉及进程地址空间/线程生命周期的系统调用确实不是信号安全的,直接注入很容易引发竞态条件(比如目标进程正在扩容地址空间时,你同时注入新的内存操作,可能导致页表混乱)。你的“先拦截并等待pending的mmap/clone完成再注入”的方向是对的,但还有几个关键细节需要补全,才能确保真正的安全:
一、不要只靠rip判断syscall状态
你提到检查rip前两个字节是否为0x0f 0x05(syscall指令),再通过orig_rax判断syscall号,这个逻辑有漏洞:
- 当
rip指向syscall指令时,线程可能是正要进入内核执行syscall;但如果线程已经处于内核态(可以通过检查cs段寄存器:x86_64下cs=0x10表示内核态,0x33表示用户态),此时它可能正在内核中执行mmap/clone,这时候你无法通过用户态的rip判断状态。 - 正确的做法是:
- 附加目标进程所有线程并逐个暂停;
- 对每个线程,先检查
cs寄存器判断是否处于内核态:- 如果是内核态,读取
orig_rax获取当前正在执行的syscall号,若为9(mmap)或56(clone),则需要等待该线程返回用户态(可以通过PTRACE_CONT后再次捕获SIGTRAP,再检查状态); - 如果是用户态,再检查
rip是否指向syscall指令,同时orig_rax是mmap/clone,此时再进行拦截。
- 如果是内核态,读取
二、拦截syscall的正确操作
当你发现线程正要执行mmap/clone时,把rip改为0xcc(int3断点)的思路可行,但要注意:
- 必须保存原
rip的值(即syscall指令的下一条地址),等线程触发SIGTRAP后,先检查rax寄存器确认syscall执行结果(比如mmap成功会返回有效内存地址,clone成功会返回pid或0); - 确认syscall完成后,把
rip恢复到原下一条指令地址,再让线程继续执行; - 额外注意:部分syscall可能因信号中断返回
-EINTR并自动重启,虽然mmap/clone属于“不会自动重启”的syscall,但仍需处理这种极端情况——如果发现syscall返回错误且需要重启,要再次拦截直到它执行完成。
三、“无pending的mmap/clone”的完整判定标准
你需要确保的是所有线程都处于用户态,且没有任何线程正要执行或正在执行mmap/clone:
- 没有线程处于内核态执行mmap/clone;
- 没有用户态线程的
rip指向mmap/clone的syscall指令;
只有满足这两个条件,目标进程的地址空间和线程状态才是稳定的,此时注入你的mmap(分配注入内存)和clone(创建远程线程)才是安全的。
四、注入过程的线程管控
注入操作全程要保持所有目标线程处于暂停状态:
- 不要在注入中途恢复任何线程的执行,否则可能出现线程访问你正在修改的内存区域,导致进程崩溃;
- 用ptrace让目标线程执行你需要的syscall时,要完整保存并恢复该线程的所有寄存器状态(包括
rax、rdi、rsi等参数寄存器),避免破坏线程原有执行逻辑。
五、注入操作本身的信号安全注意
即使目标进程状态稳定,你注入的mmap和clone也需要符合信号安全要求:
- mmap尽量使用
MAP_ANONYMOUS | MAP_PRIVATE标志,避免涉及文件IO(文件IO相关操作可能触发信号); - clone使用
CLONE_VM | CLONE_FS | CLONE_FILES等必要标志来共享目标进程的地址空间和资源,避免不必要的信号触发。
总的来说,你的方案核心逻辑是正确的,只要补全上述细节,就能大幅降低注入的风险,实现类似CreateRemoteThread的功能。
内容的提问来源于stack exchange,提问作者WednesdayDT
相关产品推荐
相关产品推荐

