我正在学习基本的缓冲区溢出,我有以下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
语句比较,这样我就能赢得#34;。无法触及main()
,只有your_fcn()
可以。
我的方法是用缓冲区溢出覆盖返回地址。所以在这种情况下,我发现返回地址应该距离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
错误,而我仍然会失去&#34;。
问题:我做错了什么?为什么不让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
答案 0 :(得分:3)
x86具有可变长度指令,因此您不能简单地计算指令并乘以4.由于您具有gdb的输出,因此请相信它以确定每条指令的地址。
该函数的返回地址是调用指令后的地址。在显示的代码中,这将是主+ 48。
if
语句从main + 60开始,而不是main + 57。主+ 57的指令将yours+1
存储到mine
。因此,要调整返回地址以返回if语句,您应该添加12(即60 - 48)。
这样做会跳过yours
和mine
的分配。由于它们都被初始化为0,因此它将打印出“你赢了”。