【问题标题】:Why SIGSEGV while push instruction为什么在推送指令时使用 SIGSEGV
【发布时间】:2017-02-15 07:34:20
【问题描述】:
=> 0x7fffffffeefc:  xor    %eax,%eax
   0x7fffffffeefe:  movabs $0xff978cd091969dd1,%rbx
   0x7fffffffef08:  neg    %rbx
   0x7fffffffef0b:  push   %rbx
   0x7fffffffef0c:  push   %rsp
   0x7fffffffef0d:  pop    %rdi
   0x7fffffffef0e:  mov    $0x3b,%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 会出现分段错误。分段错误后跳转到0x7fffffffef12 而不是0x7fffffffef0c。这是否意味着0x7fffffffef0c 是错误处理程序?

【问题讨论】:

    标签: assembly gdb x86-64 system-calls shellcode


    【解决方案1】:

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

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

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


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


    rex.RB push %r11 后添加也不太可能有用。您是否故意选择了被 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 可能希望它不会返回。

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

    【讨论】:

    • 正如我在回答的最新更新中提到的,我怀疑发生崩溃的代码是实际代码。反汇编一组连续的以空字符结尾的字符串时,您可以获得有趣的指令序列。
    • 这不是代码高尔夫,但我知道他不这样做的一个原因mov $0x3b, %eax 是因为这会在输出字节中编码一个 0x00,并且由于这是 shell 代码,他正在避免 0x00部分说明。这也是他对字符串进行编码并取反的原因,这样字符串上的 nul 终止符就不会出现在生成的二进制文件中。
    • @EirikFuller:哦,好电话,是的,很有可能。 argv[] 和 env[] 在进程入口的 RSP 初始值之上的堆栈上。和 Michael 一样,我开始怀疑这是否是 shellcode,因为这些 RIP 地址看起来像堆栈。
    • 虚假的函数指针取消引用是导致此崩溃的一种可能解释;回溯将提供更多洞察力。
    • 我还怀疑 add %cl,0x4e(%rcx,%rcx,2) push %r11 在堆栈上是垃圾。它包含LINAS 的ASCII 字符。我猜他的原始来源可能在syscall 之后没有代码,所以你看到的是垃圾。他可能不在乎 shell 代码在完成工作后会崩溃。
    【解决方案2】:

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

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

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

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

    【讨论】:

    • 我认为SIGSEGV 的发生是因为0x7fffffffef0c。因为在执行0x7fffffffef0c之后,GDB会提示Program收到信号SIGSEGV。这是不对的?另外我想知道gdb可以在使用nexti时跳过指令而不执行
    • GDB的SIGSEGV的通知是正确的;你假设它发生在指令0x00007fffffffef0c 可能是不正确的。如前所述,使用stepi 而不是nexti 可能会更有意义地指示正在发生的事情。 afterimmediately after 之间的区别在这里很重要。
    • @user4929293:SYSCALL destroys rcx and r11,以及用返回值修改rax。另请参阅x86 tag wiki 的其他链接,以及底部的 gdb 提示。 ni 可能跳过了多条指令,因为 gdb 认为它们来自内联函数或其他东西。 (也许是汇编器宏?)ni 不会下降到 CALL 指令中,但我认为它通常不会跳过其他任何内容。但只需使用si
    • 一个简单的实验表明,nextistepi 对围绕 syscall 指令的指令的工作方式相同(在这两种情况下,gdb 都会在每条指令处停止),但可以想象,这可能会因 gdb 而异版本(我使用的是 7.11.1),或围绕 syscall 的精确说明。我不希望调试符号很重要,而且无论哪种方式它们都不存在于这些指令中。简而言之,我开始怀疑这个答案的开头。无论如何,我相信包含回溯会改善问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-31
    • 2015-12-03
    • 2016-11-12
    • 2013-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多