缓冲区溢出数学

时间:2016-12-19 00:26:30

标签: 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;
}

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

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

结果是一个seg错误,表明返回地址被两个41&#s覆盖了一半。

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

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

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

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

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

1 个答案:

答案 0 :(得分:8)

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

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

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

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

如果编译器很聪明,它甚至会注意到argv[1]仅由strcpy访问,它将其复制到缓冲区 - 然后,考虑到什么都不会访问{的地址空间在argv[1]之后{1}}只会不为缓冲区分配任何内容,而只需使用main。由于那是无处使用的,你的&(argv[1])将为空,但对于main(),一个const表达式,因此对main的调用可以替换为将0写入return 0或任何你的平台用于返回值。

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