基本缓冲区溢出教程

时间:2018-02-18 21:46:34

标签: c assembly buffer-overflow

我正在学习基本的缓冲区溢出,我有以下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个字节,EBP4个字节。然后我使用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  

1 个答案:

答案 0 :(得分:3)

x86具有可变长度指令,因此您不能简单地计算指令并乘以4.由于您具有gdb的输出,因此请相信它以确定每条指令的地址。

该函数的返回地址是调用指令后的地址。在显示的代码中,这将是主+ 48。

if语句从main + 60开始,而不是main + 57。主+ 57的指令将yours+1存储到mine。因此,要调整返回地址以返回if语句,您应该添加12(即60 - 48)。

这样做会跳过yoursmine的分配。由于它们都被初始化为0,因此它将打印出“你赢了”。