我正在学习基本的缓冲区溢出,以下是我的 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
语句的比较部分,以便我能够“获胜”。只有your_fcn()
可以更改,不能更改main()
。我的方法是通过缓冲区溢出覆盖返回地址。在这种情况下,我确定返回地址应该离
buffer
有8
个字节的距离,因为buffer
是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
call
本身并不重要,因为您正在覆盖返回地址。因此,您应该相对于0x0000057a <+48>
计算偏移量。假设您已经在堆栈上找到了正确的位置,那么这应该意味着一个偏移量为9
。 - Jesteryour_func
的反汇编。可能你的ret = buffer + 8;
偏移量是错误的。 - Jesterret
分配在ebp-4处,将buffer
分配在ebp-8处,因此返回地址位于buffer+12处。 - prl