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

如何通过父进程配置x86-64调试寄存器实现硬件断点?

硬件断点配置问题:子进程未触发SIGTRAP信号

问题背景

我尝试为x86-64架构的子进程配置DR0、DR6、DR7调试寄存器来设置硬件断点,但恢复子进程后始终收不到SIGTRAP信号。

寄存器配置方案

  • DR0:设置为子进程中需要触发断点的函数地址
  • DR6:置为0,根据资料设置断点/观察点时需要清空该寄存器
  • DR7:设置为0x701,即把reserved_10、dr0_local、LE、GE标志位设为1

遇到的问题是,使用ptrace(PTRACE_POKEUSER, ...)写入user结构体的DR7字段时常返回-1,即便返回0,断点也无法正常工作。

初始代码

#include <stdio.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <inttypes.h>
#include <string.h>

#define DR_OFFSET(dr) ((((struct user *)0)->u_debugreg) + (dr))

pid_t child_pid;

void child_func() {
    printf("CHILD\n");
}

int main() {
    if(child_pid) {
        waitpid(child_pid, NULL, 0);
        struct user_regs_struct regs = {};
        ptrace(PTRACE_GETREGS, child_pid, NULL, &regs);
        printf("BREAKPOINT : RIP -> %llx\n", regs.rip);
        printf("POKE DRO : %lx\n", ptrace(PTRACE_POKEUSER, child_pid, DR_OFFSET(0), (void*)&child_func));
        uint64_t dr7 = 0;
        dr7 |= (uint64_t)pow(2, 0) + (uint64_t)pow(2, 8) + (uint64_t)pow(2, 9) + (uint64_t)pow(2, 10);
        printf("POKE DR7 : %lx\n", ptrace(PTRACE_POKEUSER, child_pid, DR_OFFSET(7), &dr7));
        printf("POKE DR6 : %lx\n", ptrace(PTRACE_POKEUSER, child_pid, DR_OFFSET(6), 0));
        printf("PTRACE_CONT : %lx\n", ptrace(PTRACE_CONT, child_pid, NULL, NULL));
        printf("WAITPID : %x\n", waitpid(child_pid, NULL, 0));
        memset(&regs, 0, sizeof(regs));
        ptrace(PTRACE_GETREGS, child_pid, NULL, &regs);
        printf("BREAKPOINT : RIP -> %llx\n", regs.rip);
        ptrace(PTRACE_CONT, child_pid, NULL, NULL);
    }
    else {
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        __asm__("int3");
        child_func();
    }
    return 0;
}

void __attribute__ ((constructor)) premain() {
    child_pid = fork();
}

初始代码执行结果

我在子进程中设置了软件断点使其暂停,以便操作其user结构体。当DR7写入成功时,执行结果如下:

BREAKPOINT : RIP -> 5651726ec2ee
POKE DRO : 0
POKE DR7 : 0
POKE DR6 : 0
PTRACE_CONT : 0
CHILD
WAITPID : 38af
BREAKPOINT : RIP -> 0

第二个RIP值为0,说明硬件断点未触发。

更新:修复后代码及结果

参考建议后代码已正常工作:

#include <stdio.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <math.h>
#include <sys/wait.h>
#include <inttypes.h>
#include <string.h>

#define DR_OFFSET(dr) ((((struct user *)0)->u_debugreg) + (dr))

pid_t child_pid;

void child_func() {
    printf("CHILD\n");
}

int main() {
    if(child_pid) {
        waitpid(child_pid, NULL, 0);
        struct user_regs_struct regs = {};
        ptrace(PTRACE_GETREGS, child_pid, NULL, &regs);
        printf("BREAKPOINT : RIP -> %llx\n", regs.rip);
        printf("POKE DRO : %lx\n", ptrace(PTRACE_POKEUSER, child_pid, DR_OFFSET(0), (void*)&child_func));
        uint64_t dr7 = 0;
        dr7 |= (uint64_t)pow(2, 0) + (uint64_t)pow(2, 8) + (uint64_t)pow(2, 9) + (uint64_t)pow(2, 10);
        printf("POKE DR7 : %lx\n", ptrace(PTRACE_POKEUSER, child_pid, DR_OFFSET(7), &dr7));
        printf("POKE DR6 : %lx\n", ptrace(PTRACE_POKEUSER, child_pid, DR_OFFSET(6), 0));
        printf("PTRACE_CONT : %lx\n", ptrace(PTRACE_CONT, child_pid, NULL, NULL));
        printf("WAITPID : %x\n", waitpid(child_pid, NULL, 0));
        memset(&regs, 0, sizeof(regs));
        ptrace(PTRACE_GETREGS, child_pid, NULL, &regs);
        printf("BREAKPOINT : RIP -> %llx\n", regs.rip);
        ptrace(PTRACE_CONT, child_pid, NULL, NULL);
    }
    else {
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        __asm__("int3");
        child_func();
    }
    return 0;
}

void __attribute__ ((constructor)) premain() {
    child_pid = fork();
}

执行结果:

BREAKPOINT : RIP -> 5640849712e1
POKE DRO : 0
POKE DR7 : 0
POKE DR6 : 0
PTRACE_CONT : 0
WAITPID : 3cc9
BREAKPOINT : RIP -> 564084971400
CHILD

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 03:00:08