【发布时间】:2018-02-18 21:46:34
【问题描述】:
我正在学习基本的缓冲区溢出,并且我有以下 C 代码:
int your_fcn()
{
char buffer[4];
int *ret;
ret = buffer + 8;
(*ret) += 16;
return 1;
}
int main()
{
int mine = 0;
int yours = 0;
yours = your_fcn();
mine = yours + 1;
if(mine > yours)
printf("You lost!\n");
else
printf("You won!\n");
return EXIT_SUCCESS;
}
我的目标是绕过mine = yours + 1;这一行,直接跳到if语句比较,这样我就能“赢”。 main()不能碰,只有your_fcn()可以。
我的方法是用缓冲区溢出覆盖返回地址。所以在这种情况下,我确定返回地址应该是远离buffer 的8 字节,因为缓冲区是4 字节,而EBP 是4 字节。然后我使用gdb 来确定我要跳转到的行是16 距离函数调用的字节。这是 gdb 的结果:
(gdb) disassemble main
Dump of assembler code for function main:
0x0000054a <+0>: lea 0x4(%esp),%ecx
0x0000054e <+4>: and $0xfffffff0,%esp
0x00000551 <+7>: pushl -0x4(%ecx)
0x00000554 <+10>: push %ebp
0x00000555 <+11>: mov %esp,%ebp
0x00000557 <+13>: push %ebx
0x00000558 <+14>: push %ecx
0x00000559 <+15>: sub $0x10,%esp
0x0000055c <+18>: call 0x420 <__x86.get_pc_thunk.bx>
0x00000561 <+23>: add $0x1a77,%ebx
0x00000567 <+29>: movl $0x0,-0xc(%ebp)
0x0000056e <+36>: movl $0x0,-0x10(%ebp)
0x00000575 <+43>: call 0x51d <your_fcn>
0x0000057a <+48>: mov %eax,-0x10(%ebp)
0x0000057d <+51>: mov -0x10(%ebp),%eax
0x00000580 <+54>: add $0x1,%eax
0x00000583 <+57>: mov %eax,-0xc(%ebp)
0x00000586 <+60>: mov -0xc(%ebp),%eax
0x00000589 <+63>: cmp -0x10(%ebp),%eax
0x0000058c <+66>: jle 0x5a2 <main+88>
0x0000058e <+68>: sub $0xc,%esp
0x00000591 <+71>: lea -0x1988(%ebx),%eax
我看到0x00000575 <+43>: call 0x51d <your_fcn> 和0x00000583 <+57>: mov %eax,-0xc(%ebp) 线相距四行,这告诉我应该将ret 偏移16 字节。但是来自 gdb 的地址说明了一些不同的内容。也就是说,函数调用从0x00000575开始,而我要跳转到的那一行在0x00000583,也就是说它们在15字节之外?
无论如何,无论我使用16 字节还是15 字节,我都会收到segmentation fault 错误,但我仍然“失败”。
问题:我做错了什么?为什么 gdb 中给出的地址一次不增加 4 个字节,这里实际发生了什么。如何正确跳转到我想要的行?
澄清:这是在运行 linux Ubuntu 的 VM 上的 x32 机器上完成的。我正在使用命令gcc -fno-stack-protector -z execstack -m32 -g guesser.c -o guesser.o 进行编译,该命令会关闭堆栈保护器并强制进行 x32 编译。
your_fcn() 的 gdb 按要求:
(gdb) disassemble your_fcn
Dump of assembler code for function your_fcn:
0x0000051d <+0>: push %ebp
0x0000051e <+1>: mov %esp,%ebp
0x00000520 <+3>: sub $0x10,%esp
0x00000523 <+6>: call 0x5c3 <__x86.get_pc_thunk.ax>
0x00000528 <+11>: add $0x1ab0,%eax
0x0000052d <+16>: lea -0x8(%ebp),%eax
0x00000530 <+19>: add $0x8,%eax
0x00000533 <+22>: mov %eax,-0x4(%ebp)
0x00000536 <+25>: mov -0x4(%ebp),%eax
0x00000539 <+28>: mov (%eax),%eax
0x0000053b <+30>: lea 0xc(%eax),%edx
0x0000053e <+33>: mov -0x4(%ebp),%eax
0x00000541 <+36>: mov %edx,(%eax)
0x00000543 <+38>: mov $0x1,%eax
0x00000548 <+43>: leave
0x00000549 <+44>: ret
【问题讨论】:
-
既然你在玩汇编程序,你应该诊断你的目标是哪个 CPU,你是在构建 32-but 还是 64-but,可能还有哪些 o/s,因为布局可能不同。如果您包含
-fomit -frame-pointer并且您正在使用 GCC,您可能会得到不同的结果。还有一个很好的优化编译器之夜发现 UB 并安排在您造成任何真正损坏之前重新格式化您的磁盘 - 或者也许您会很幸运,您的编译器会让您的硬盘驱动器活着看到另一天 -
“为什么 gdb 中给出的地址一次不走 4 个字节?” 因为很多处理器都有可变长度指令?
-
call本身不相关,因为您正在覆盖返回地址。因此,您应该计算相对于0x0000057a <+48>的偏移量。假设您在堆栈上找到了正确的位置,这应该意味着9的偏移量。 -
你需要发布
your_func的反汇编。大概你的ret = buffer + 8;的偏移量是错误的。 -
编译器在ebp-4分配
ret,在ebp-8分配buffer,所以返回地址在buffer+12。
标签: c assembly buffer-overflow