使用Ptrace读取子进程符号内存时出现输入/输出错误
使用Ptrace读取子进程内存失败:输入/输出错误问题排查
我正尝试用C++程序通过Ptrace读取子进程内存,获取全局变量的值,最终目标是用此方法追踪Bash进程的readline缓冲区,但调用PTRACE_PEEKDATA读取子进程内存时始终失败,返回“输入/输出错误”。
在测试案例中,我创建了一个包含全局变量i的被追踪程序(tracee),希望在子进程启动时读取i的值。所有程序均在VirtualBox虚拟机的Ubuntu 24.04.2 LTS系统上编译运行。
被追踪程序(tracee)
#include <iostream> int i = 10; int main() { while (i < 15) { i++; std::cout << i << '\n'; } return 0; }
符号地址查询
编译该程序后执行nm tracee,得到全局变量i的地址为0x4010,相关输出片段如下:
000000000000038c r __abi_tag 0000000000004040 B __bss_start 0000000000004150 b completed.0 w __cxa_finalize@GLIBC_2.2.5 0000000000004000 D __data_start 0000000000004000 W data_start 00000000000010b0 t deregister_tm_clones 0000000000001120 t __do_global_dtors_aux 0000000000003da8 d __do_global_dtors_aux_fini_array_entry 0000000000004008 D __dso_handle 0000000000003db0 d _DYNAMIC 0000000000004014 D _edata 0000000000004158 B _end 00000000000011b8 T _fini 0000000000001160 t frame_dummy 0000000000003da0 d __frame_dummy_init_array_entry 00000000000020e8 r __FRAME_END__ 0000000000003fb0 d _GLOBAL_OFFSET_TABLE_ w __gmon_start__ 0000000000002008 r __GNU_EH_FRAME_HDR 0000000000004010 D i 0000000000001000 T _init 0000000000002000 R _IO_stdin_used w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable U __libc_start_main@GLIBC_2.34 0000000000001169 T main 00000000000010e0 t register_tm_clones 0000000000001080 T _start 0000000000004018 D __TMC_END__ U _ZNSolsEi@GLIBCXX_3.4 0000000000002004 r _ZNSt8__detail30__integer_to_chars_is_unsignedIjEE 0000000000002005 r _ZNSt8__detail30__integer_to_chars_is_unsignedImEE 0000000000002006 r _ZNSt8__detail30__integer_to_chars_is_unsignedIyEE U _ZSt21ios_base_library_initv@GLIBCXX_3.4.32 0000000000004040 B _ZSt4cout@GLIBCXX_3.4 U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c@GLIBCXX_3.4
追踪程序(tracer)
#include <cstdio> #include <cstdlib> #include <iostream> #include <sys/types.h> #include <sys/ptrace.h> #include <sys/wait.h> #include <sys/ioctl.h> #include <unistd.h> void writeErrorMsg(const char* header) { std::cout << header << ": " << std::error_code(errno, std::generic_category()).message() << '\n'; } int main() { static constexpr char tracee[] {"/home/murpyh/ptrace_test/tracee"}; static constexpr int addr {0x4010}; pid_t childPid {fork()}; if (childPid == 0) { if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1) writeErrorMsg("PTRACE_TRACEME"); execl(tracee, "tracee", nullptr); return 0; } else { int status; waitpid(childPid, &status, WUNTRACED | WCONTINUED); if (!WIFSTOPPED(status)) { std::cout << "Program ended unexpectedly\n"; return 1; } long data {ptrace(PTRACE_PEEKDATA, childPid, (void*)addr, nullptr)}; if (data == -1) { writeErrorMsg("PTRACE_PEEKDATA"); } std::cout << data << '\n'; } return 0; }
(注:我知道此处不应使用C风格转换为void*,但不清楚对应的C++转换方式。)
运行结果
执行追踪程序后得到如下输出:
PTRACE_PEEKDATA: Input/output error -1 11 12 13 14 15
问题细节与已尝试的排查步骤
- 尽管
0x4010是程序内存中的有效位置,PTRACE_PEEKDATA仍返回输入/输出错误。 - 被追踪程序竟执行完毕,按我的理解,被追踪的子进程在调用
execve时应停止执行,直到追踪进程调用PTRACE_CONT或子进程被分离。waitpid已完成说明子进程确实收到了SIGSTOP,但它在未受父进程干预的情况下继续执行(推测是因为父进程调用PTRACE_PEEKDATA后就直接退出了)。
已尝试的排查动作:
- 将
/proc/sys/kernel/yama/ptrace_scope设置为0,排查权限问题,输出无变化。 - 在GDB中启用
detach-on-fork,确保没有其他进程追踪子进程;即使不通过GDB运行追踪程序,结果仍相同。
我是否对Ptrace的工作原理存在误解?
内容的提问来源于stack exchange,提问作者murpyh
相关产品推荐
相关产品推荐

