如何设置 dr7 寄存器的值以便在 x86-64 上创建硬件断点?
How to set the value of dr7 register in order to create a hardware breakpoint on x86-64?
我正在开发一个允许在 OCaml 语言中使用 ptrace()
的 "binding" 库,但我的问题只涉及 ptrace()
.
所以,现在,我正在尝试编写一小段代码,以便使用 ptrace()
:[=23= 在 Linux x86-64 上创建一个简单的硬件断点]
#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
的值为 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
位域有效吗?
谢谢。
编辑:
感谢 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
)。
几点说明(引自Intel Manual chap 17.2: Debug Registers
):
- 保留位(必须根据文档进行相应设置):
- 第 10 位保留但设置为 1。
- 第 12、14 和 15 位保留,必须设置为 0(
memset
整个结构事先设置为 0)。
其他领域:
- 您应该 也实现第 8 位和第 9 位并将它们设置为 1
LE and GE (local and global exact breakpoint enable) flags (bits 8, 9)
— This feature is not supported in the P6 family processors, later
IA-32 processors, and Intel 64 processors. [...] , we recommend that
the LE and GE flags be set to 1 if exact breakpoints are required
- 指令的指令断点必须的长度设置为1字节(这意味着相应的
LENn
字段必须设置为0):
Instruction breakpoint addresses must have a length specification of 1
byte (the LENn field is set to 00). Code breakpoints for other operand sizes are undefined
因此,要在执行时设置断点:
- 将保留位设置为正确的值
- 将 DR7.LE 和 DR7.GE 设置为 1
- 设置DR7.L0(L1,L2,L3)为1[局部断点]
- 确保 DR7.RW/0 (RW/1, RW/2, RW/3) 是 0 [break on instruction exec ]
- 确保 DR7.LEN0 (LEN1, LEN2, LEN3) 是 0 [1 字节长度]
- 设置DR0(1,2,3)为指令线性地址
- 断点[DR0到DR3]的线性地址必须落在指令的第一个字节。
The processor recognizes an instruction breakpoint address only when
it points to the first byte of an instruction. If the instruction has
prefixes, the breakpoint address must point to the first prefix.
编辑
- 0x101 :
- bin(0x101) = '0b100000001'
- DR7.L0 & DR7.LE 设置为 1
从技术上讲,0x701 应该是正确的:
- 0x701 :
- bin(0x701) = '0b11100000001'
- DR7.L0 & DR7.LE & DR7.GE & DR7.bit10 设置为 1
我正在开发一个允许在 OCaml 语言中使用 ptrace()
的 "binding" 库,但我的问题只涉及 ptrace()
.
所以,现在,我正在尝试编写一小段代码,以便使用 ptrace()
:[=23= 在 Linux x86-64 上创建一个简单的硬件断点]
#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
的值为 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
位域有效吗?
谢谢。
编辑:
感谢 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
)。
几点说明(引自Intel Manual chap 17.2: Debug Registers
):
- 保留位(必须根据文档进行相应设置):
- 第 10 位保留但设置为 1。
- 第 12、14 和 15 位保留,必须设置为 0(
memset
整个结构事先设置为 0)。
其他领域:
- 您应该 也实现第 8 位和第 9 位并将它们设置为 1
LE and GE (local and global exact breakpoint enable) flags (bits 8, 9) — This feature is not supported in the P6 family processors, later IA-32 processors, and Intel 64 processors. [...] , we recommend that the LE and GE flags be set to 1 if exact breakpoints are required
- 指令的指令断点必须的长度设置为1字节(这意味着相应的
LENn
字段必须设置为0):
Instruction breakpoint addresses must have a length specification of 1 byte (the LENn field is set to 00). Code breakpoints for other operand sizes are undefined
因此,要在执行时设置断点:
- 将保留位设置为正确的值
- 将 DR7.LE 和 DR7.GE 设置为 1
- 设置DR7.L0(L1,L2,L3)为1[局部断点]
- 确保 DR7.RW/0 (RW/1, RW/2, RW/3) 是 0 [break on instruction exec ]
- 确保 DR7.LEN0 (LEN1, LEN2, LEN3) 是 0 [1 字节长度]
- 设置DR0(1,2,3)为指令线性地址
- 断点[DR0到DR3]的线性地址必须落在指令的第一个字节。
The processor recognizes an instruction breakpoint address only when it points to the first byte of an instruction. If the instruction has prefixes, the breakpoint address must point to the first prefix.
编辑
- 0x101 :
- bin(0x101) = '0b100000001'
- DR7.L0 & DR7.LE 设置为 1
从技术上讲,0x701 应该是正确的:
- 0x701 :
- bin(0x701) = '0b11100000001'
- DR7.L0 & DR7.LE & DR7.GE & DR7.bit10 设置为 1