intentrace底层实现(上):ptrace是如何拦截Linux进程每一次系统调用的

📅 2026/8/23 15:19:19
intentrace底层实现(上):ptrace是如何拦截Linux进程每一次系统调用的
intentrace底层实现上ptrace是如何拦截Linux进程每一次系统调用的【免费下载链接】intentraceintentrace is strace with intent, it goes all the way for you instead of half the way. intentrace is currently in beta项目地址: https://gitcode.com/gh_mirrors/in/intentraceintentrace 是一个带意图的 strace 替代工具它基于 Linux 的 ptrace 机制拦截并记录进程中每一次系统调用再通过推理启发式告诉你每次调用到底想干什么。本文带你深入 intentrace 的底层实现上篇搞懂 ptrace 是如何拦截 Linux 进程的系统调用的。运行效果一眼看懂系统调用追踪 ️先看看它追踪google-chrome启动过程时的输出——每一行都是一次被 ptrace 拦截的系统调用右侧之后是 intentrace 对结果的意图化解读传统 strace 只会打印read(3, 0x7ffd..., 4096) 832这样的原始信息你还需要自己查手册理解每个参数的含义。而 intentrace 会直接解释为从文件描述符 3 读取 832 字节、映射内存失败权限被修改等自然语言结论。双进程架构fork PTRACE_TRACEME 埋下伏笔intentrace 追踪一个新程序时并没有直接运行它而是在 main.rs 中采用经典的fork 双进程结构参考parent()与child_trace_me()函数父进程调用fork()创建子进程自己进入主循环负责拦截子进程先调用ptrace::traceme()即PTRACE_TRACEME声明我被追踪然后exec目标程序。PTRACE_TRACEME的作用是让子进程此后每次进入/退出系统调用时都会自动停下来并把控制权交还父进程。这是 ptrace 拦截体系的第一块基石。 如果目标是已经在运行的进程intentrace 则用-p pid配合ptrace::attach动态附加原理完全相同。ptrace 的核心机制一次系统调用 两次停顿 父进程的主循环src/main.rs中parent()函数围绕一个简单动作展开反复执行ptrace::syscall(pid) → 等待子进程停下来 → 读取寄存器 → 放行PTRACE_SYSCALL让被追踪进程在每次系统调用入口和出口各停顿一次于是父进程拿到两帧快照停顿时机关键寄存器拿到什么入口enteringorig_rax系统调用号如openat、mmap入口rdi rsi rdx r10 r8 r9最多 6 个调用参数出口exitingrax返回值或错误码为什么必须读orig_rax而不是rax因为系统调用返回时rax会被返回值覆盖而orig_rax是内核原样保留的原始调用号是区分进入还是返回的唯一可靠依据。代码中用一个syscall_entering布尔量在两次停顿之间切换状态入口时构造SyscallObject并解析参数出口时读取rax判定成败再PTRACE_SYSCALL放行进程直到进程退出ESRCH结束整个循环。偷看别人内存peek poke 阶段 光有寄存器里的参数还不够——比如openat的参数只是一个指向路径字符串的指针。要解读出真实路径必须读取被追踪进程的内存。intentrace 把这一步封装在 peeker_poker.rs名字致敬 ptrace 文档里读写被追踪进程内存的术语 peek/poke核心是process_vm_readv系统调用read_bytes按定长读取远程内存read_bytes_variable_length读取变长数据如 C 字符串并小心处理跨 4KiB 页边界的半读问题read_bytes_as_struct把读到的字节块直接转成内存结构体。这样像截图中readlink /usr/bin/google-chrome /etc/alternatives/google-chrome这类解析出符号链接真实目标的能力就是靠远程读内存实现的。从原始参数到意图SyscallObject 与骨架映射 ⚙️拦截到一次调用后intentrace 还需要回答两个问题参数是什么类型返回值该怎么解读骨架映射syscall_skeleton_map.rs为每个系统调用登记一份形状描述例如read的返回是字节数或Errnommap的返回是地址或MAP_FAILED。目前覆盖了 380 个 Linux 系统调用中最常用的约 166 个遵循少数调用覆盖绝大多数软件的策略类似 UniKraft 内核的思路调用对象syscall_object.rsSyscallObject把调用号、被追踪 PID、进入/退出状态和结果聚合在一起fill_buffer()负责渲染成终端上那行带颜色的人话输出返回值解析src/return_resolvers.rs、src/utilities.rs把rax解释为成功 N 字节或失败 errno 含义比如EPERM会被翻译成具体的权限说明。这就是 intentrace 与 strace 的本质区别拦截靠 ptrace理解靠推理——它不止展示发生了什么还解释这意味着什么。快速上手构建并运行 intentrace git clone https://gitcode.com/gh_mirrors/in/intentrace cd intentrace cargo build --release几个常用命令intentrace -q ls # -q 屏蔽被追踪程序自身的输出 intentrace -f docker run alpine # -f 追踪 fork 出的子进程 intentrace -Z ls # -Z 只看失败的系统调用 intentrace -C ls # -C 额外打印统计汇总表注意当前 beta 版本仅支持 x86-64 架构多线程程序支持仍在完善中。小结intentrace 的拦截链路可以浓缩为一句话子进程PTRACE_TRACEME自曝 → 父进程PTRACE_SYSCALL双停顿 →getregs读orig_rax和参数寄存器 →process_vm_readv偷读内存还原指针内容 → 骨架映射 返回值解析输出意图。下一篇我们将深入参数解析与意图推理的启发式细节。【免费下载链接】intentraceintentrace is strace with intent, it goes all the way for you instead of half the way. intentrace is currently in beta项目地址: https://gitcode.com/gh_mirrors/in/intentrace创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考