ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

intentrace底层实现(下):process_vm_readv如何跨进程读取字符串、文件路径与fd映射

2026/8/23 12:06:53 拓冰建站 浏览量
intentrace底层实现(下):process_vm_readv如何跨进程读取字符串、文件路径与fd映射 intentrace底层实现下process_vm_readv如何跨进程读取字符串、文件路径与fd映射【免费下载链接】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 系统调用追踪工具它能拦截进程发出的每个系统调用并把参数推理成人话。本篇聚焦其底层核心能力如何借助process_vm_readv系统调用跨进程读取被追踪进程内存中的字符串、还原文件路径并解析文件描述符fd背后的真实映射。这也是 intentrace 相比普通 strace 多走的那一半路程的关键所在。 为什么必须跨进程读取内存当一个进程调用open(/etc/passwd)时寄存器里存的并不是字符串本身而是一段指向被追踪进程内存的地址。追踪器intentrace 本体运行在另一个进程空间直接解引用这个地址只会得到自己的数据甚至段错误。Linux 为此提供了两个工具系统调用作用特点ptrace(PTRACE_PEEKDATA)按机器字word窥视被追踪进程内存单字读取调用频繁process_vm_readv通过 iovec 批量向量式读取远端内存一次系统调用读多段效率高intentrace 的读取工具集全部集中在src/peeker_poker.rspeek and poke 正是 ptrace 官方文档里描述读写被追踪进程内存的术语并统一封装在peeker_poker模块中由 src/main.rs 声明注册。 peeker_poker跨进程读取的完整工具箱peeker_poker模块提供了一组逐层封装的读取函数从底层字节到上层字符串read_bytes / read_bytes_variable_length ← process_vm_readv批量读原始字节 read_bytes_until_null ← ptrace 逐字读直到遇到 \0 read_words_until_null ← ptrace 逐字读指针数组直到 0 string_from_pointer ← 把远端地址变成可读字符串 get_array_of_strings ← 解析 execve 式的指针数组其中read_bytes的实现在 src/peeker_poker.rs构造一个RemoteIoVec { base, len }描述远端地址 长度再和本地缓冲区一起交给process_vm_readv一次系统调用即可把 N 个字节从目标进程拷入本地。读取以 null 结尾的 C 字符串Linux 内存中的字符串几乎都是 C 风格\0结尾长度未知。read_bytes_until_nullsrc/peeker_poker.rs采用逐字x86-64 下 8 字节轮询策略每轮用ptrace::read读一个字逐字节扫描遇到b\0立即跳出——这样既不会读越界也能精确截断。上层 src/utilities.rs 的string_from_pointer再把字节序列做 UTF-8 容错转换String::from_utf8_lossy失败时返回空串保证追踪器永不因单个参数解析失败而崩溃。 文件路径是如何被还原的系统调用格式化器 src/one_line_formatter.rs 中open、openat、unlink、symlink等几十个文件类系统调用全部通过string_from_pointer(registers[n], tracee_pid)取出路径参数例如 openat 分支先取registers[1]这个远端地址跨进程读出文件名再结合标志位解释 O_CREAT、O_TRUNC 等意图。解析指针数组一次取出所有字符串execve等调用的参数是指针数组char *argv[]数组本身也远端。get_array_of_stringssrc/utilities.rs分两步先用read_words_until_null按字读出指针列表直到遇到 0再对每个指针调用string_from_pointer一次还原出完整的参数字符串数组。相对路径与 AT_FDCWD结合 fd 锚点补全路径openat(dirfd, filename)的filename可能是相对路径此时路径是相对于dirfd这个目录文件描述符的。get_strings_from_dirfd_anchored_filesrc/utilities.rs负责补全dirfd AT_FDCWD通过procfs读取被追踪进程的cwd把相对路径拼到工作目录下其他情况通过fd_from_fd(dirfd)查出该 fd 实际指向的目录路径作为锚点。最终输出形如3 - /usr/bin/google-chrome-stable让相对路径在追踪流中依然可读。 fd 映射文件描述符背后的真实目标追踪流里出现裸数字3意义有限intentrace 会把它解析成3 - 真实路径。parse_as_file_descriptorsrc/utilities.rs基于procfs的/proc/pid/fd符号链接信息区分出多种 fd 目标fd 目标输出示例普通文件3 - /etc/passwd路径按最后一级 dentry 高亮着色TCP/UDP socket5 - 10.0.0.1:443按 inode 反查/proc/net/tcp等Unix socket / pipe4 - Unix Pipe匿名 inode7 - anon_inode:[eventfd]标准流0 - StdIn、1 - StdOut、2 - StdErr配合 src/utilities.rs 的find_fd_for_tracee反查函数intentrace 就能回答这个 fd 到底打开的是什么这就是 fd 映射解析的完整链路。⚙️ 工程细节页边界与部分读取process_vm_readv有一个常被忽略的坑当远端地址落在无效内存区域时内核返回部分读取partial read而不是整段失败且从失败点起不再尝试后续读取。read_bytes_variable_lengthsrc/peeker_poker.rs的注释专门提醒读取未知长度的数据如 C 字符串时应避免单个 iovec 跨 4KiB 页边界必要时把远端读取拆成两段再合并。这也是为什么逐字扫描的read_bytes_until_null在遇到ptrace错误时会直接return Some(data)——把已读到的部分保留下来而不是让整个解析作废。小结intentrace 的意图推理建立在一套严谨的跨进程读取原语之上process_vm_readv负责批量取字节ptrace逐字扫描负责未知长度字符串与指针数组procfs负责 fd 到真实目标的映射。三层能力叠加才让一行openat(3, /usr/lib/...)被还原成以只读方式打开文件这样的可读结论。想继续深入可以从src/peeker_poker.rs入手它只有百余行却是整个意图追踪的基石。【免费下载链接】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),仅供参考