为什么在推送指令时使用 SIGSEGV

Why SIGSEGV while push instruction

=> 0x7fffffffeefc:  xor    %eax,%eax
   0x7fffffffeefe:  movabs [=10=]xff978cd091969dd1,%rbx
   0x7fffffffef08:  neg    %rbx
   0x7fffffffef0b:  push   %rbx
   0x7fffffffef0c:  push   %rsp
   0x7fffffffef0d:  pop    %rdi
   0x7fffffffef0e:  mov    [=10=]x3b,%al
   0x7fffffffef10:  syscall 
   0x7fffffffef12:  add    %cl,0x4e(%rcx,%rcx,2)
   0x7fffffffef16:  rex.RB push %r11
(gdb) nexti
0x00007fffffffeefe in ?? ()
(gdb) nexti
0x00007fffffffef08 in ?? ()
(gdb) nexti
0x00007fffffffef0b in ?? ()
(gdb) nexti
0x00007fffffffef0c in ?? ()
(gdb) nexti

Program received signal SIGSEGV, Segmentation fault.
0x00007fffffffef12 in ?? ()

我不明白为什么 0x7fffffffef0c 会出现段错误。在分段错误 rip 跳转到 0x7fffffffef12 而不是 0x7fffffffef0c 之后。这是否意味着 0x7fffffffef0c 是错误处理程序?

gdb 似乎跳过了 syscall 指令和一些周围的指令。 SIGSEGV 可能与 0x7fffffffef12 指令中使用的 rcx 寄存器的值有关。如果您希望 gdb 在每条指令处停止而不是继续执行函数调用,那么 stepi 可能比 nexti.

更好

0x7fffffffef12(假定的崩溃位置)处的说明似乎很奇怪;该反汇编中的其他说明也似乎很奇怪。如果我在我自己的系统上的 gdb 会话中查看相同的地址范围,我在该页面的那部分看到的是一堆以 null 结尾的字符串,它们看起来很像我的命令行片段,然后是我的环境。前三个的地址匹配我主框架中argv的前三个元素,argv本身也在那个页面中。

检查您使用 x/s 而不是 x/i 反汇编的地址可能会很有趣。在我的会话中(在主框架中)x/29s argv[0] 显示了该地址范围内的一堆内容。

如果事实证明您的崩溃是在尝试将您的环境视为代码时发生的,那么更有趣的问题可能是该地址范围的分支是如何发生的。如果 gdb 为这次崩溃显示连贯的回溯,那可能会提供一些见解。

GDB 说你在 0x00007fffffffef12 处出错,这是 add %cl,0x4e(%rcx,%rcx,2),正如@Eirik 的回答所解释的那样。

SYSCALL destroys rcx and r11,以及用 return 值修改 rax。

另请参阅 tag wiki 中的其他链接以及底部的 gdb 提示。使用 layout asmlayout reg 可以轻松地跟踪 RIP 并在单步执行时注册值。


ni 可能跳过了多条指令,因为 gdb 认为它们来自内联函数或其他东西。 (也许是一个汇编程序宏?)ni 不会进入 CALL 指令,但我认为它通常不会跳过任何其他指令。但只需使用 si.


rex.RB push %r11 add 之后也不太可能有用。您是否故意选择被 SYSCALL 破坏的两个寄存器?此外,带有 REX.W=0 的 REX 前缀的 PUSH 将作为非法指令出错。 push r/m32 在 64 位模式下不可编码,despite the phrasing of the text in Intel's manual

哦,我认为 SYSCALL 之后的代码只是数据("/bin/sh" 之类的)或者只是垃圾,根本不打算执行,因为 SYS_execve 是 59 (0x3b) 并且OP 可能希望它不会 return.

显然这是 shellcode,所以这解释了 push/pop 而不是 mov %rsp, %rdi 和其他奇怪的东西,大概是为了避免 0x00 字节。可能很高兴提到这一点,所以我们这些错过了 [shell] 标签的人本可以避免浪费大量时间讨论这些奇怪的东西。