与缓冲区溢出数学的基本混淆

Aus*_*tin 1 c gdb 32bit-64bit

我正在关注Youtube Computerphile缓冲区溢出教程,以了解它是如何工作的.该教程在Kali中说它,我正在运行Kali 64位来测试它(我认为他运行的是32位).

他写了一个这样的简单程序:

#include <stdio.h>
#include <string.h>

int main(int argc, char** argv) {

    char buffer[500];        
    strcpy(buffer, argv[1]);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

然后在GDB中启动程序后,他运行:

(gdb) run $(python -c 'print "\x41" * 506')

结果是一个seg错误,显示返回地址被两个41的一半覆盖.

当我尝试复制它时,我需要将506更改为522以产生相同的结果.所以我的问题是:

  1. 为什么506只运行它时只重写两个字节而不是三个字节?

  2. 为什么我需要写入522个字节来覆盖返回地址中的2个字节?我认为它可能与他有关,可能使用32位而不是64位Kali,但我真的不明白这种差异是如何在数学上加起来的.

  3. 当我这样做时,disassemble main我看到在函数序言是指令之后sub rsp, 0x210,所以看起来缓冲区被分配给528个字节.为什么这个数字特别是(他的替代子0x1f4恰好是500)以及它如何与上面的内容有关,需要大于520字节才能开始重写指令指针?

  4. 在写入[500,520]字节的范围内发生了什么,它超过了缓冲区大小,但还没有写入指令指针的顶部?

Mar*_*ler 8

每个月左右都会询问此问题的变体.

事情很简单:在缓冲区的边界上写入会导致未定义的行为,这可能会可能不会涉及分段错误并覆盖内存中的任何特定结构.

您所做的假设是每个人都使用强制性内存布局,而这种情况根本不是真的,更不用说地址空间随机化或编译器优化等技术.

地狱,为什么main函数存储传统的返回地址?它可能非常好地在系统/编译器/二进制格式特定的启动代码中.

如果编译器是聪明的,它甚至会发现,argv[1]仅通过访问strcpy,它缓冲哪些副本-然后,考虑什么也不会在访问地址空间argv[1]了之后main,就干脆不分配缓冲区任何东西,只是用&(argv[1])代替.并且因为它是无处使用的,所以你main()将是空的但是对于return 0const表达式,因此对main的调用可以替换为写入0 eax或者平台用于返回值的任何内容.

讨厌告诉你这个,但是:除了指出事实上可能存在缓冲区溢出之外,它只提供一些适用于特定机器的东西,它具有特定的编译器版本,用特定的libc编译特定的代码片段.特定的架构.结果不能一概而论.

  • 没有cookie-cutter缓冲区溢出始终有效.随着地址空间的随机化,甚至还有明确的技术可以解决这个问题.你当然可以看一下特定版本中特定编译器及其特定标志生成的特定汇编和内存布局.特定的代码并推断可能的攻击向量,但实际上,这里没有太多的cookie切割;从了解编译器的工作方式,然后能够轻松找出需要更改以触发有害事物的内容,这些都是有条不紊的*知识,你需要学习,而不是复制 (3认同)