【发布时间】:2020-10-19 22:08:24
【问题描述】:
我为班级分配编写了一个故意缓冲区溢出的 C 程序。在我的程序中,我有一个 main 函数来接受来自用户的名称作为长度为 50 的字符数组。然后该名称作为长度为 50 的字符数组传递,其中将打印消息 "Hello, user!"。用户替换为用户提供的名称。我不对scanf() 函数进行任何长度检查,而是在遇到换行符之前获取输入。结果,我能够溢出缓冲区,覆盖 main 的返回地址并导致分段错误。
当我使用 GDB 命令反汇编 main 时,我可以看到地址 [ebp - 0x3a] 已加载并推入堆栈以用作 scanf 函数的参数(见下图) .我假设这是缓冲区的开始,直到我将 0x3a 转换为十进制并发现它的值是 58。为什么要为字符缓冲区分配额外的 8 个字节?为什么当我尝试运行此缓冲区溢出时,当缓冲区长度似乎从距 ebp 58 字节和距返回地址 62 字节开始时,只需要 54 个字符即可溢出缓冲区?再次,我计算了使用ebp-0x3a 到返回地址的长度。
代码:
#include <stdio.h>
#include <string.h>
void printHello(char fname[]);
int main() {
char name[50];
printf("Please enter a name to print a hello message!");
scanf("%[^\n]", name);
printHello(name);
return 0;
}
void printHello(char fname[50]){
int strLen = strlen(fname);
printf("Hello, ");
for(int i=0; i<strLen; i++){
printf("%c", fname[i]);
}
printf("!\n");
}
反汇编main函数:
Dump of assembler code for function main:
0x080484fb <+0>: lea ecx,[esp+0x4]
0x080484ff <+4>: and esp,0xfffffff0
0x08048502 <+7>: push DWORD PTR [ecx-0x4]
0x08048505 <+10>: push ebp
0x08048506 <+11>: mov ebp,esp
0x08048508 <+13>: push ecx
0x08048509 <+14>: sub esp,0x44
0x0804850c <+17>: sub esp,0xc
0x0804850f <+20>: push 0x8048640
0x08048514 <+25>: call 0x8048390 <printf@plt>
0x08048519 <+30>: add esp,0x10
0x0804851c <+33>: sub esp,0x8
0x0804851f <+36>: lea eax,[ebp-0x3a]
0x08048522 <+39>: push eax
0x08048523 <+40>: push 0x804866e
0x08048528 <+45>: call 0x80483e0 <__isoc99_scanf@plt>
0x0804852d <+50>: add esp,0x10
0x08048530 <+53>: sub esp,0xc
0x08048533 <+56>: lea eax,[ebp-0x3a]
0x08048536 <+59>: push eax
0x08048537 <+60>: call 0x804854c <printHello>
0x0804853c <+65>: add esp,0x10
0x0804853f <+68>: mov eax,0x0
0x08048544 <+73>: mov ecx,DWORD PTR [ebp-0x4]
0x08048547 <+76>: leave
0x08048548 <+77>: lea esp,[ecx-0x4]
0x0804854b <+80>: ret
End of assembler dump.
【问题讨论】:
-
不相关,在 -O2 优化中对此进行的铿锵反汇编做了一些我觉得很了不起的事情。
printf内部的 for 循环被优化为使用putchar代替。我确实没有看到这一点。 See it here. -
请注意,您不需要覆盖返回地址,只要覆盖
[ebp-0x4](54 字节外)就足够了,它最终会以esp结束,从而导致崩溃。跨度> -
ebp-0x3a是name的地址。额外的 8 个字节是堆栈帧的其余部分,这是 ebp 被推送的地方。 -
@WhozCraig :大多数现代 GCC 和 CLANG 都会对 printf 做同样的事情,如果可以的话,将其解析为
puts或putchar。见:stackoverflow.com/questions/36343733/… -
另外,gcc 处理
main特别是堆栈对齐代码。正常的功能看起来会有所不同。
标签: c assembly x86 buffer-overflow