当调试器可以手动写入同一内​​存时,为什么在尝试执行写入内存的指令时会出现分段错误?

Why do I get a segmentation fault when attempting to execute an instruction writing to memory when the debugger can manually write to the same memory?

这是针对 x86-64 的,是一些堆栈溢出漏洞利用的一部分。

gdb 输出:

=> 0x000055555555e996:  48 89 18    mov    QWORD PTR [rax],rbx

将rbx移动到rax存放的内存地址,简单。由于它在这条指令处出现段错误,让我们看一下。

rax 是什么?

(gdb) i r rax
rax            0x7ffff79f4c80      140737347800192

这段记忆有效吗?

(gdb) x/16b $rax
0x7ffff79f4c80 <_itoa_upper_digits>:    0x30    0x31    0x32    0x33    0x34    0x35    0x36    0x37
0x7ffff79f4c88 <_itoa_upper_digits+8>:  0x38    0x39    0x41    0x42    0x43    0x44    0x45    0x46

好吧,至少我能读书。我可以写吗?

(gdb) set $rbx = 0x4141414141414141
(gdb) set {unsigned long} $rax = $rbx
(gdb) x/16b $rax
0x7ffff79f4c80 <_itoa_upper_digits>:    0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0x7ffff79f4c88 <_itoa_upper_digits+8>:  0x38    0x39    0x41    0x42    0x43    0x44    0x45    0x46

似乎工作正常,并且此设置操作确实似乎确实写入了内存,尝试对已知的无效地址执行相同操作将导致错误。所以看来我可以写入和读取这个内存。

那我们试试执行这条指令吧。还是一样的:

(gdb) x/i $pc
=> 0x55555555e996:  mov    QWORD PTR [rax],rbx
(gdb) si

Program received signal SIGSEGV, Segmentation fault.
(a=<error reading variable: Cannot access memory at address 0x376141366141355d>)
    at test.c:371
371     asm volatile("mov %rbx,(%rax);"
=> 0x000055555555e996:  48 89 18    mov    QWORD PTR [rax],rbx
   0x000055555555e999:  c3  ret    

为什么我刚刚验证我可以在那里写就失败了?

Why does it fail when I've just verified I can write there?

程序不能写在那里,只有GDB可以。

您尝试写入的地址位于 .text 部分,该部分通常 mmap 带有 PROT_READ|PROT_EXEC 并且 没有 PROT_WRITE.

但是,允许 GDB(或正在 ptraceing 的任何进程)写入此类映射。这是 GDB 能够插入断点所必需的(这通常需要重新编写程序指令)。