如何设置 dr7 寄存器的值以便在 x86-64 上创建硬件断点?

2023-12-05

我正在开发一个允许使用的“绑定”库ptrace()在 OCaml 语言中,但我的问题仅涉及ptrace().

所以,现在,我正在尝试编写一小段代码,以便使用以下命令在 Linux x86-64 上创建一个简单的硬件断点ptrace():

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

typedef struct {
    int           dr0_local:    1;
    int           dr0_global:   1;
    int           dr1_local:    1;
    int           dr1_global:   1;
    int           dr2_local:    1;
    int           dr2_global:   1;
    int           dr3_local:    1;
    int           dr3_global:   1;
    int           reserverd:    8;
    break_flag_t  dr0_break:    2;
    data_length_t dr0_len:      2;
    break_flag_t  dr1_break:    2;
    data_length_t dr1_len:      2;
    break_flag_t  dr2_break:    2;
    data_length_t dr2_len:      2;
    break_flag_t  dr3_break:    2;
    data_length_t dr3_len:      2;
} dr7_t;

CAMLprim value ptrace_breakpoint(value ml_pid, value ml_addr)
{
    CAMLparam2(ml_pid, ml_addr);
    dr7_t dr7 = {0};

    dr7.dr0_local = 1;
    dr7.dr0_break = 0; /* break on execution */
    dr7.dr0_len   = 0x03; /* len 4 */

    ptrace(PTRACE_POKEUSER, Int_val(ml_pid), DR_OFFSET(0), (void*)Int64_val(ml_addr));
    ptrace(PTRACE_POKEUSER, Int_val(ml_pid), DR_OFFSET(7), (void*)dr7));
    ptrace(PTRACE_POKEUSER, Int_val(ml_pid), DR_OFFSET(6), (void*)0);
    CAMLreturn0;
}

当我执行这段代码时,我得到了一个Invalid argument。的价值dr7 is 0xc0001。为了找到有效值,我检查了GDB如何使用ptrace通过使用strace:

ptrace(PTRACE_POKEUSER, 6459, offsetof(struct user, u_debugreg), 0x400519) = 0
ptrace(PTRACE_POKEUSER, 6459, offsetof(struct user, u_debugreg) + 56, 0x101) = 0
ptrace(PTRACE_POKEUSER, 6459, offsetof(struct user, u_debugreg) + 48, 0) = 0

因此,GDB将dr7寄存器设置为值0x101。我尝试了这个值并且它有效。因此,我想知道GDB使用的值的含义是什么?是个dr7_t我之前使用的位字段有效吗?

谢谢。

Edit:

感谢 Neitsa,解决方案如下:

typedef struct {
    unsigned int  dr0_local:      1;  
    unsigned int  dr0_global:     1;  
    unsigned int  dr1_local:      1;  
    unsigned int  dr1_global:     1;  
    unsigned int  dr2_local:      1;  
    unsigned int  dr2_global:     1;  
    unsigned int  dr3_local:      1;  
    unsigned int  dr3_global:     1;  
    unsigned int  le:             1;  
    unsigned int  ge:             1;  
    unsigned int  reserved_10:    1;  
    unsigned int  rtm:            1;  
    unsigned int  reserved_12:    1;  
    unsigned int  gd:             1;  
    unsigned int  reserved_14_15: 2;
    break_flag_t  dr0_break:      2;  
    data_length_t dr0_len:        2;  
    break_flag_t  dr1_break:      2;  
    data_length_t dr1_len:        2;  
    break_flag_t  dr2_break:      2;  
    data_length_t dr2_len:        2;  
    break_flag_t  dr3_break:      2;  
    data_length_t dr3_len:        2;  
} dr7_t;

CAMLprim value ptrace_breakpoint(value ml_pid, value ml_addr)
{
    CAMLparam2(ml_pid, ml_addr);
    dr7_t dr7 = {0};

    dr7.dr0_local = 1;
    dr7.le = 1;
    dr7.ge = 1;
    dr7.reserved_10 = 1;

    my_ptrace(PTRACE_POKEUSER, Int_val(ml_pid), DR_OFFSET(0), (void*)Int64_val(ml_addr));
    my_ptrace(PTRACE_POKEUSER, Int_val(ml_pid), DR_OFFSET(7), (void*)dr7));
    my_ptrace(PTRACE_POKEUSER, Int_val(ml_pid), DR_OFFSET(6), (void*)0);
    CAMLreturn0;
}

你的结构看起来不错(我会用unsigned int尽管)。

DR7 bits

几点说明(引自Intel Manual chap 17.2: Debug Registers):

  • Reserved bits (must be set accordingly to the documentation):
    • 位 10 保留但设置为 1。
    • 位 12、14 和 15 被保留,必须设置为 0 (memset整个结构预先为0)。

其他领域:

  • You should还实现位 8 和位 9 并将它们设置为 1

LE 和 GE(本地和全局精确断点启用)标志(位 8、9) — P6 系列处理器稍后不支持此功能 IA-32 处理器和 Intel 64 处理器。 [...],我们建议 如果需要精确的断点,则 LE 和 GE 标志设置为 1

  • 指令的指令断点must长度设置为 1 个字节(这意味着相应的LENn字段必须设置为 0):

指令断点地址的长度规范必须为 1 字节 (LENn 字段设置为 00)。其他操作数大小的代码断点未定义

因此,要在执行时设置断点:

  • 将保留位设置为正确的值
  • 将 DR7.LE 和 DR7.GE 设置为 1
  • 将 DR7.L0(L1、L2、L3)设置为1[本地断点]
  • 确保 DR7.RW/0 (RW/1, RW/2, RW/3) 是0[执行指令时中断]
  • 确保 DR7.LEN0(LEN1、LEN2、LEN3)是0[1字节长度]
  • Set DR0 (1, 2, 3) to the instruction linear address
    • 断点的线性地址[DR0到DR3]must落在指令的第一个字节上。

处理器识别指令断点地址只有当 它指向指令的第一个字节。如果指令有 前缀,断点地址必须指向第一个前缀。


edit

  • 0x101 :
    • bin(0x101) = '0b100000001'
    • DR7.L0 & DR7.LE 设置为 1

从技术上讲,0x701 应该是正确的:

  • 0x701 :
    • bin(0x701) = '0b11100000001'
    • DR7.L0 & DR7.LE & DR7.GE & DR7.bit10 设置为 1
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何设置 dr7 寄存器的值以便在 x86-64 上创建硬件断点? 的相关文章