__stack_chk_fail后出现代码异常

Abnormal presence of code after __stack_chk_fail

我有一个 64 位 ELF 二进制文件。我没有它的源代码,不知道它是用什么参数编译的,所以这里不能提供。我拥有的唯一相关信息是源代码是一个 .c 文件(因此没有手工制作的程序集),通过 Makefile 编译。

在使用 IDA 逆向这个二进制文件时,我偶然发现了一个我以前从未见过且绝对无法解释的极其奇怪的结构。这是一个使用 IDA 语法的函数的原始反编译:

mov    rax, [rsp+var_20]
xor    rax, fs:28h
jnz    location
add    rsp, 28h
pop    rbx
pop    rbp
retn

location:
call    __stack_chk_fail
nop     dword ptr [rax]
db      2Eh
nop     word ptr [rax+rax+00000000h]
...then dozens of instructions of normal and functional code

在这里,我们有一个简单的金丝雀检查,如果有效,我们 return,否则调用 __stack_chk_fail。一切都很正常。但是经过这次检查,还是有程序集,而且是功能齐全的代码。

查看 __stack_chk_fail 的手册,我确定这个函数确实退出了程序,并且没有可以继续的边缘情况:

Description

The interface __stack_chk_fail() shall abort the function that called it with a message that a stack overflow has been detected. The program that called the function shall then exit.

我也试过写这个小C程序,寻找重现这个的方法:

#include <stdio.h>
#include <stdlib.h>

int foo()
{
        int a = 3;
        printf("%d\n", a);

        return 0;

        int b = 7;
        printf("%d\n", b);
}

int main()
{
        foo();
        return 0;
}

但是return后面的代码直接被gcc省略了

我的二进制文件似乎也没有受到缓冲区溢出的影响,我可以利用它来控制 rip 并在金丝雀检查后跳转到代码。我还检查了每个 call 并使用 objdump 跳转,这段代码似乎永远不会被调用。

有人能解释一下这是怎么回事吗?这段代码最初是如何生成的?是二进制作者的笑话吗?

我怀疑您正在查看填充,然后是 IDA 没有名称的不相关函数。

为了验证这个假设,我需要以下附加信息:

  • 紧接call __stack_chk_fail.
  • 之后字节的地址
  • 作为调用或跳转指令目标的下一个更高地址。
  • 这两个地址之间字节的原始十六进制转储。
  • 从作为调用或跳转指令目标的下一个更高地址开始的四或五条指令的反汇编。