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

标签 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) 运行 $(python -c 'print "\x41"* 506')

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

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

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

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

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

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

最佳答案

这个问题的变体大约每个月都会被问到。

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

您所做的假设是每个人都使用强制性内存布局,但事实并非如此,对于地址空间随机化或编译器优化等技术更是如此。

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

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

不想告诉你这个,但是:除了指出实际上可能存在缓冲区溢出之外,它只给出了在具有特定编译器版本的特定机器上工作的东西,用特定的编译器编译一段特定的代码特定架构的 libc。结果不能一概而论。

关于c - 与缓冲区溢出数学的基本混淆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41214322/

相关文章:

c - 指示对 Clang 的未对齐访问以实现 ARM 兼容性

数字的 Collat​​z 序列

c - 使用 termios() 替换 EOF?

c - gdb调试整数打印信息

gcc - 如何查找 C 程序中的问题 : Program received signal SIGSEGV, 段错误

windows - 我在哪里可以获得与 Windows Server 兼容的 32 位版本的 IESHIMS.dll 和 GPSVC.dll?

c++ - 为 64 位和 32 位构建创建指针大小的 union

c - 如何输入一个数字并使用 for 循环按顺序计数 10 个以上的数字

c++ - 数组索引越界,但 gdb 报告错误行 - 为什么?

android - 在 Ubuntu 12.04 64 位上运行 Android aapt