粉碎堆栈example3.c混乱

时间:2012-10-22 06:25:28

标签: c stack-overflow buffer-overflow disassembly

可以找到文章here

我正在阅读粉碎堆栈并发现自己被卡在example3.c上。

0x80004a3 <main+19>:    call   0x8000470 <function>
0x80004a8 <main+24>:    addl   $0xc,%esp
0x80004ab <main+27>:    movl   $0x1,0xfffffffc(%ebp)
0x80004b2 <main+34>:    movl   0xfffffffc(%ebp),%eax

作者表示我们想要从0x80004a8跳到0x80004b2并且这个跳转是8个字节;作者如何确定这是8个字节?我重新创建了代码并通过objdump发送它,发现它不是8个字节(我在64位机器上,但我确保使用32位编译):

8048452:    e8 b5 ff ff ff          call   804840c <function>
8048457:    c7 44 24 1c 01 00 00    movl   $0x1,0x1c(%esp)
804845e:    00 
804845f:    8b 44 24 1c             mov    0x1c(%esp),%eax
8048463:    89 44 24 04             mov    %eax,0x4(%esp)
8048467:    c7 04 24 18 85 04 08    movl   $0x8048518,(%esp)

作者还说“我们怎么知道在返回地址中添加8?我们用了一个 首先测试值(例如1)“他在哪里使用此测试值?

3 个答案:

答案 0 :(得分:3)

这不是我对文章的解释。我理解它的方式是他想要修改返回地址,以便跳过x = 1;赋值,即他希望function返回到printf将被执行的位置。

正如你在反汇编中看到的那样,赋值是8个字节(c7 44 24 1c 01 00 00 00),因此将返回地址向前移动8个字节将使它超过该指令。至于“我们首先使用了测试值”评论......也许他只是意味着他在反汇编程序中查看代码来计算长度,或者他尝试了不同的偏移量(?)。

答案 1 :(得分:1)

文章中的位移是错误的,它应该是10个字节。调用函数(或执行跳转)时,返回地址设置为等于指令指针+当前指令大小:

ret = IP + Curr_Inst_size

因此,当对函数的调用返回时,指令指针应该等于0x80004a80x80004a3 +调用指令大小):

    0x80004a3 <main+19>:    call   0x8000470 <function>
--> 0x80004a8 <main+24>:    addl   $0xc,%esp            
    0x80004ab <main+27>:    movl   $0x1,0xfffffffc(%ebp)
    0x80004b2 <main+34>:    movl   0xfffffffc(%ebp),%eax

但是,您希望将指令指针设置为0x80004b2以跳过作业,您也不可避免地必须跳过另一条指令(addl $0xc,%esp)才能到达那里或者换句话说,您需要将(0x80004b2-0x80004a8)个字节或10个字节添加到指令指针以跳过这两个指令:

    0x80004a3 <main+19>:    call   0x8000470 <function>
    0x80004a8 <main+24>:    addl   $0xc,%esp            
    0x80004ab <main+27>:    movl   $0x1,0xfffffffc(%ebp)
--> 0x80004b2 <main+34>:    movl   0xfffffffc(%ebp),%eax 

实际指令大小取决于操作数,机器类型等。但在本例中,addl长3个字节,movl长7个字节。您可以检查x86 Instruction Set Reference的确切指令大小,或者您可以编译和反汇编此代码,您将看到这两个指令长度为10个字节:

int main()
{
    asm("addl  $0xc,%esp\n\
         movl  $0x1,0xfffffffc(%ebp)");

}

GDB:

0x08048397 <+3>: 83 c4 0c               add    $0xc,%esp
0x0804839a <+6>: c7 45 fc 01 00 00 00   movl   $0x1,-0x4(%ebp)

在示例3中,还有关于此问题的讨论herehere

答案 2 :(得分:-1)

经过很长时间的思考,我终于解决了这个问题。帮助我解决此问题的资源是Stack smashing code not working on Linux kernel 2.6.38.7... Please help

帮助我解决此问题的最大变化是将disassembly-flavor intel用于gdb

反汇编代码(供参考):

0804840c <function>:
 804840c:   55                      push   ebp
 804840d:   89 e5                   mov    ebp,esp
 804840f:   83 ec 10                sub    esp,0x10
 8048412:   8d 45 f7                lea    eax,[ebp-0x9]
 8048415:   83 c0 14                add    eax,0x14
 8048418:   89 45 fc                mov    DWORD PTR [ebp-0x4],eax
 804841b:   8b 45 fc                mov    eax,DWORD PTR [ebp-0x4]
 804841e:   8b 00                   mov    eax,DWORD PTR [eax]
 8048420:   8d 50 05                lea    edx,[eax+0x5]
 8048423:   8b 45 fc                mov    eax,DWORD PTR [ebp-0x4]
 8048426:   89 10                   mov    DWORD PTR [eax],edx
 8048428:   c9                      leave  
 8048429:   c3                      ret    

0804842a <main>:
 804842a:   55                      push   ebp
 804842b:   89 e5                   mov    ebp,esp
 804842d:   83 e4 f0                and    esp,0xfffffff0
 8048430:   83 ec 20                sub    esp,0x20
 8048433:   c7 44 24 1c 00 00 00    mov    DWORD PTR [esp+0x1c],0x0
 804843a:   00 
 804843b:   c7 44 24 08 03 00 00    mov    DWORD PTR [esp+0x8],0x3
 8048442:   00 
 8048443:   c7 44 24 04 02 00 00    mov    DWORD PTR [esp+0x4],0x2
 804844a:   00 
 804844b:   c7 04 24 01 00 00 00    mov    DWORD PTR [esp],0x1
 8048452:   e8 b5 ff ff ff          call   804840c <function>
 8048457:   c7 44 24 1c 01 00 00    mov    DWORD PTR [esp+0x1c],0x1
 804845e:   00 
 804845f:   8b 44 24 1c             mov    eax,DWORD PTR [esp+0x1c]
 8048463:   89 44 24 04             mov    DWORD PTR [esp+0x4],eax
 8048467:   c7 04 24 18 85 04 08    mov    DWORD PTR [esp],0x8048518
 804846e:   e8 7d fe ff ff          call   80482f0 <printf@plt>
 8048473:   c9                      leave  
 8048474:   c3                      ret    
 8048475:   66 90                   xchg   ax,ax
 8048477:   66 90                   xchg   ax,ax
 8048479:   66 90                   xchg   ax,ax
 804847b:   66 90                   xchg   ax,ax
 804847d:   66 90                   xchg   ax,ax
 804847f:   90                      nop

我对此的理解存在两个问题:

A)我的第一个问题是在ret内找到超出function的字节数。再次;我通过使用intel语法进行反汇编来实现这一点,并发现:

要将ret设置在内存ret中的正确空间,需要在调用函数时将其设置为EIP8048412处的地址空间向下移动到堆栈0x9。因为这是32位代码;要到达ret,我们会为字大小添加额外的0x4个字节。要转到ret,这意味着ret设置为0x9 + 0x4,十进制为13。

这解决了到达ret的第一个问题。

B)第二个问题是跳过0x8048457内存位置。这是通过向(*ret)添加7个字节来完成的,这使得程序跳过并在0x804845e执行00NUL)。这只是效率低下;所以我添加了额外的字节并在堆栈中执行了8个字节;从而导致x = 0;

我发现确切的字节数为8(c7 44 24 1c 01 00 00为7字节),00为一个字节。这解决了我的最后一个问题。

我修改过的C代码:

void function(int a, int b, int c) {
        char buffer1[5];
        int *ret;

        ret = buffer1 + 13; //tried 11, 14, 20, 40, 38, 43
        (*ret) += 8; // tried 5, 8, 12; 8 is correct value!
}

void main() {
        int x;
        x = 0;
        function(1,2,3);
        x = 1;
        printf("%d\n", x);
}