You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 21:09:53