【问题标题】:Linux: illegal instruction with RIP in the middle of an LEA instruction in glibcLinux:在 glibc 中的 LEA 指令中间带有 RIP 的非法指令
【发布时间】:2018-04-13 10:29:36
【问题描述】:

在redhat7.2中,由于信号4,非法指令,我的程序在popen函数中得到了核心转储。但是当我调试核心文件时,我发现 rip 寄存器中的非法指令不存在。调试信息如下:

Program terminated with signal 4, Illegal instruction.
#0  0x00007fb2f217b1f4 in popen@@GLIBC_2.2.5 () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.17-105.el7.x86_64 libgcc-4.8.5-4.el7.x86_64 libstdc++-4.8.5-4.el7.x86_64 zlib-1.2.7-15.el7.x86_64
(gdb) disas
Dump of assembler code for function popen@@GLIBC_2.2.5:
   0x00007fb2f217b1d0 <+0>: push   %r12
   0x00007fb2f217b1d2 <+2>: mov    %rsi,%r12
   0x00007fb2f217b1d5 <+5>: push   %rbp
   0x00007fb2f217b1d6 <+6>: mov    %rdi,%rbp
   0x00007fb2f217b1d9 <+9>: mov    $0x100,%edi
   0x00007fb2f217b1de <+14>:    push   %rbx
   0x00007fb2f217b1df <+15>:    sub    $0x10,%rsp
   0x00007fb2f217b1e3 <+19>:    callq  0x7fb2f212d340 <memalign@plt>
   0x00007fb2f217b1e8 <+24>:    test   %rax,%rax
   0x00007fb2f217b1eb <+27>:    mov    %rax,%rbx
   0x00007fb2f217b1ee <+30>:    je     0x7fb2f217b240 <popen@@GLIBC_2.2.5+112>
   0x00007fb2f217b1f0 <+32>:    lea    0xf0(%rax),%rax
   0x00007fb2f217b1f7 <+39>:    xor    %esi,%esi

看最新的两行,0x00007fb2f217b1f4 应该在它们之间,但是它不存在!为什么 rip 寄存器中加载了不存在的指令?

【问题讨论】:

  • 遇到非法指令的地址是0x00007fb2f217b1f4。这正好在lea 0xf0(%rax),%rax 指令的中间,让我觉得是某些东西导致处理器进入随机地址。
  • 也许有人试图通过 ret2reg 或 ret2code 攻击来攻击您的系统。也许他们正在寻找 jmp %rsp 2 字节序列或类似的序列,但您的 glibc 的机器代码与他们开发攻击的系统不同。
  • 您的措辞在技术上是错误的。地址0x00007fb2f217b1f4确实存在,如果你将那里的内存内容反汇编为x86_64指令,它可能会形成一个(或非法操作码),但它不像CPU理解原始指令边界,有lea开始的线索在..f0xor..f7,每条x86指令都有一定的“操作码”,由1-15个字节组成,如果你将CPU指向中间的字节,它会将它们解释为其他操作码,甚至可能(很可能)不小心形成了有效的指令。 lea 有 7B 操作码。
  • 即CPU 确实只看到内存中的正常字节(在开始/结束操作码处没有特殊标记),并且 fetch/decode 单元将推进rip 其最后一次解码当前指令所使用的数量,因此 CPU 将正确推进“每条指令”一旦它确实从有意义的一个开始,但某些机器代码可能会在某个偏移开始处隐藏不同的指令序列,这有时甚至可能被漏洞利用编写者(ROP)滥用,或者在混淆你的代码时,或者当某些错误使您的代码跳转到计划外的地址时。

标签: linux assembly x86 x86-64 crash-dumps


【解决方案1】:

在奇怪地址处执行的最常见方法是让函数覆盖其堆栈上保存的 %rip —— 类似缓冲区溢出的事情可以做得很好,但野指针或未初始化的指针也可以。当函数“返回”时,它会出现在预期之外的地方。有目的的版本是基本缓冲区溢出 &|面向返回的编程攻击。但是不要妄想,直到它被证明是合理的。

要诊断此问题,您应该仔细分析发生此问题的堆栈帧。调试器能否恢复有意义的堆栈跟踪?堆栈是否包含模式数据(例如 ascii 字符串)?

即使调试器不能,尝试反汇编堆栈上的值通常也是值得的;例如,如果 0x123400 在堆栈上,请尝试查看 0x1233f5 - 如果它反汇编为调用指令,您可能会遇到问题。打破intel ref,调用格式太多了。通过一些努力,您可以编写一个 gdb 脚本来自动执行此操作,或者至少确定候选者。

还要查看堆栈的“下方”,因为通常有一些调用帧完好无损,这可能暗示它正在执行的位置。

【讨论】:

  • 您不必手动拆卸。使用 GDB 的 disas *(void**)($rsp-16), +16 反汇编 16 个字节,从 [rsp-16] 加载起始地址(ret 下面的堆栈槽将您带到这条错误指令,假设这条指令而不是之前的指令是实际的跳转目标) .如果你想在不同的点开始反汇编,或者在不同的堆栈值作为基础,添加 +/- 偏移量。
猜你喜欢
  • 2012-02-27
  • 2019-11-06
  • 2020-09-22
  • 2012-02-27
  • 2011-09-13
  • 2011-05-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多