php方案 进程注入: 如何利用 ptrace 系统调用,从一个 PHP 进程修改另一个 PHP 进程的运行状态?

张开发
2026/4/21 23:49:09 15 分钟阅读

分享文章

php方案 进程注入: 如何利用 ptrace 系统调用,从一个 PHP 进程修改另一个 PHP 进程的运行状态?
ptrace 进程注入原理 ptrace 是 Linux 调试器的底层基础gdb 就靠它。核心流程附加 → 暂停目标 → 读写内存/寄存器 → 恢复。PHP没有原生 ptrace 绑定但PHP8.0有FFI直接调 libc。---最小可用代码PHPFFI?php// 需要 root 或 CAP_SYS_PTRACE且 ptrace_scope0// sudo sysctl kernel.yama.ptrace_scope0$ffiFFI::cdef( long ptrace(int request, int pid, void *addr, void *data); int waitpid(int pid, int *status, int options); ,libc.so.6);// ptrace 常量x86_64 LinuxconstPTRACE_ATTACH16;constPTRACE_DETACH17;constPTRACE_PEEKDATA2;constPTRACE_POKEDATA5;constPTRACE_CONT7;$pid(int)($argv[1]??die(用法: php inject.php PID\n));// 1. 附加并等目标停下来$ffi-ptrace(PTRACE_ATTACH,$pid,null,null);$ffi-waitpid($pid,null,0);echo已附加到 PID$pid\n;// 2. 从 /proc/$pid/maps 找一块可写内存地址$mapsfile(/proc/$pid/maps);$target_addrnull;foreach($mapsas$line){// 找 rw- 权限的匿名段heap 或 stackif(preg_match(/^([0-9a-f])-[0-9a-f] rw.p/,$line,$m)){$target_addrhexdec($m[1]);break;}}// 3. 读原始值$addr_ptrFFI::cast(void *,$target_addr);$original$ffi-ptrace(PTRACE_PEEKDATA,$pid,$addr_ptr,null);printf(地址 0x%x 原值: 0x%x\n,$target_addr,$original);// 4. 写新值8字节x86_64 word size$new_valFFI::cast(void *,0xdeadbeefcafe1337);$ffi-ptrace(PTRACE_POKEDATA,$pid,$addr_ptr,$new_val);echo已写入新值\n;// 5. 恢复运行 脱离$ffi-ptrace(PTRACE_CONT,$pid,null,null);$ffi-ptrace(PTRACE_DETACH,$pid,null,null);echo已脱离\n;---进阶注入代码执行 光改内存数据不够劲想让目标PHP执行任意代码// 思路找到目标进程里 zend_eval_string 的地址// 然后用 PTRACE_SETREGS 把 RIP 指过去// 1. 从 /proc/$pid/maps 找 libphp.so 基址// 2. 从本进程 dlsym 算 zend_eval_string 偏移// 3. 基址 偏移 目标进程里的函数地址// 4. 在目标进程可写内存里写好参数字符串// 5. PTRACE_GETREGS 保存寄存器// 6. 修改 RIP函数地址, RDI字符串地址System V ABI第一参数// 7. PTRACE_SETREGS 写回// 8. PTRACE_SINGLESTEP 单步执行// 9. 恢复原始寄存器这部分用 Pythonpython-ptrace 库更省事 pip install python-ptrace from ptrace.debugger import PtraceDebugger dbgPtraceDebugger()procdbg.addProcess(pid,False)# False 不是新进程proc.waitSignals()# 读内存dataproc.readBytes(addr,8)# 写内存proc.writeBytes(addr,b\x90*8)# NOP# 改寄存器regsproc.getregs()regs.riptarget_func_addr proc.setregs(regs)proc.cont()dbg.quit()---前提条件 ┌──────────────────┬───────────────────────────────────┐ │ 条件 │ 命令 │ ├──────────────────┼───────────────────────────────────┤ │ 关闭 Yama 限制 │ sysctl kernel.yama.ptrace_scope0│ ├──────────────────┼───────────────────────────────────┤ │ 同UID或 root │ — │ ├──────────────────┼───────────────────────────────────┤ │PHPFFI扩展启用 │ php-m|grepFFI│ └──────────────────┴───────────────────────────────────┘ 实际生产里更常见的方式是直接读写/proc/$pid/mem不需要 ptrace 暂停但需要先 attach 一下或者用 gdb--batch-p$pid-excall zend_eval_string(...)三行搞定。先上一句终极大白话结论 ptrace 就是 Linux 系统自带的「进程遥控器」能把别的正在跑的进程抓住、暂停偷看/篡改它的内存数据甚至强行让它执行你指定的代码PHP靠FFI直接调用系统底层函数就能实现这种进程注入操控。 下面全程大白话把原理、代码、进阶用法全拆透零基础也能懂。 一、ptrace 核心原理人话版 ptrace 是所有 Linux 调试工具比如GDB的底层基础核心就4步操作1.附加盯上目标进程把它“抓住”2.暂停让目标进程立刻停住不动了3.操控偷看它的内存、改它的数据、甚至改它要执行的代码4.恢复松开控制让进程继续正常跑 就像你把正在跑步的人按住翻他口袋、改他手里的东西再放他继续跑。 二、PHP最简 ptrace 代码逐行大白话 这段代码就是用PHPFFI直接调用系统底层的 ptrace 函数实现操控目标进程、篡改内存。 前置要求必须满足不然不让操控1.要用 root 权限 运行2.关闭系统的 ptrace 防护限制Linux 默认不让随便操控别的进程3.PHP开启FFI扩展 代码每一步干啥1.定义系统函数 告诉PHP要调用 Linux 底层的 ptrace进程操控 和 waitpid等进程暂停 函数。2.附加目标进程php$ffi-ptrace(PTRACE_ATTACH,$pid,null,null);→ 对目标PID喊我抓住你了立刻停下 目标进程会瞬间暂停。3.找目标进程的可写内存 读/proc/$pid/maps 这个文件它是目标进程的内存地图里面记了哪块内存能读、能改。 找到一块允许修改的内存区域比如进程的堆、栈。4.偷看内存原值php$original$ffi-ptrace(PTRACE_PEEKDATA,$pid,$addr_ptr,null);→ 读取这块内存原来存的是什么数据。5.篡改内存数据php$ffi-ptrace(PTRACE_POKEDATA,$pid,$addr_ptr,$new_val);→ 把自定义的数据比如0xdeadbeefcafe1337写进这块内存直接篡改目标进程的数据。6.恢复进程脱离控制php$ffi-ptrace(PTRACE_CONT,$pid,null,null);$ffi-ptrace(PTRACE_DETACH,$pid,null,null);→ 放进程继续跑松开遥控器完事。 三、进阶玩法注入代码让目标进程执行任意命令 不只是改数据还要让目标进程主动执行你写的代码比如让目标PHP进程执行一段恶意/测试代码。 大白话思路1.找到PHP内核里的专属执行函数 zend_eval_string 这是PHP自带的、专门用来执行PHP代码的函数。2.算出这个函数在目标进程内存里的地址。3.改目标进程的「指令指针」相当于告诉进程别跑原来的代码了先跳去执行这个函数。4.把你要执行的代码字符串塞进目标进程的内存里。5.让进程单步执行完这段代码再恢复原来的状态神不知鬼不觉。 实际更简单的方式 不用自己写PHP代码算地址用 Python 现成的 ptrace 库几行代码就能搞定比PHP省事。 四、必须满足的前提条件大白话 条件 人话解释 关闭 Yama 限制 Linux 自带防护默认不让随便操控进程要改内核参数放开权限 同用户/root 要么是超级管理员 root要么你和目标进程是同一个用户运行的PHP开启FFIphp.ini 里打开 ffi.enabletrue不然没法调用系统底层函数 五、生产里更常用的简单做法 不用写复杂的PHP/Python 注入代码直接用 gdb3行命令搞定 让目标PHP进程执行任意PHP代码这是业内最常用的极简方式。 六、终极总结超短版1.ptraceLinux 进程遥控器GDB就是靠它实现调试的2.核心流程附加→暂停→改内存/代码→恢复3.PHP用FFI直接调系统函数就能实现进程注入4.进阶可以强行让目标进程执行任意代码5.必须关系统限制拿高权限不然没法操控别的进程

更多文章