• [ASM C/C++] C函数调用分析


      在执行程序时,操作系统为进程分配一块栈空间来保存函数栈帧,esp寄存器总是指向栈顶。x86平台上这个栈是从高地址向低地址增长的,每次调用一个函数都要分配一个栈帧来保存参数和局部变量,C函数参数是按从右到左的顺序入栈的。各个堆栈桢之间是通过把ebp和eip压栈,而串在一起的。参数和局部变量是以栈帧基址(即ebp)为准+内存偏移量来定位和存取的。

    下面用GCC 和 GDB 做一下分析

    --------------------------

    C源代码

    int TestB(int c, int *d)
    {
        int e = c + (*d);
        *d = e;
        return e;
    }

    int TestA(int a, int *b)
    {
        return TestB(a, b);
    }

    int main(void)
    {
        int i = 5;
        int r = TestA(2, &i);
        return 0;
    }

    ------------------------------

     编译

    abeen@ubuntu:~/Documents/C$ gcc -g -o test test.c

    启动GDB调试
    abeen@ubuntu:~/Documents/C$ gdb test
    GNU gdb (GDB) 7.0-ubuntu
    ...

    显示源码
    (gdb) list TestB
    1    int TestB(int c, int *d)
    2    {
    3        int e = c + (*d);
    4        *d = e;
    5        return e;
    6    }
    7    
    8    int TestA(int a, int *b)
    9    {
    10        return TestB(a, b);

    设置断点,使其在TestB的"*d = e"处停止
    (gdb) b 4
    Breakpoint 1 at 0x80483c5: file test.c, line 4.

    开始运行调试程序
    (gdb) r
    Starting program: /home/abeen/Documents/C/test

    Breakpoint 1, TestB (c=2, d=0xbffff444) at test.c:4
    4        *d = e;

    此时已运到行断点处,查看调用堆栈帧信息
    (gdb) bt
    #0  TestB (c=2, d=0xbffff444) at test.c:4
    #1  0x080483ea in TestA (a=2, b=0xbffff444) at test.c:10
    #2  0x0804840c in main () at test.c:16
    (gdb) info frame
    Stack level 0, frame at 0xbffff420:
     eip = 0x80483c5 in TestB (test.c:4); saved eip 0x80483ea
     called by frame at 0xbffff430
     source language c.
     Arglist at 0xbffff418, args: c=2, d=0xbffff444
     Locals at 0xbffff418, Previous frame's sp is 0xbffff420
     Saved registers:
      ebp at 0xbffff418, eip at 0xbffff41c
    (gdb) info frame 1
    Stack frame at 0xbffff430:
     eip = 0x80483ea in TestA (test.c:10); saved eip 0x804840c
     called by frame at 0xbffff450, caller of frame at 0xbffff420
     source language c.
     Arglist at 0xbffff428, args: a=2, b=0xbffff444
     Locals at 0xbffff428, Previous frame's sp is 0xbffff430
     Saved registers:
      ebp at 0xbffff428, eip at 0xbffff42c
    (gdb) info frame 2
    Stack frame at 0xbffff450:
     eip = 0x804840c in main (test.c:16); saved eip 0x4c6b56
     caller of frame at 0xbffff430
     source language c.
     Arglist at 0xbffff448, args:
     Locals at 0xbffff448, Previous frame's sp is 0xbffff450
     Saved registers:
      ebp at 0xbffff448, eip at 0xbffff44c
    (gdb) p $esp
    $1 = (void *) 0xbffff408

    查看分析得知堆栈帧内存布局范围
    #0  ~~   0xbffff420
    #1 0xbffff420 ~~ 0xbffff430
    #2 0xbffff430 ~~ 0xbffff450 

    查看此时内存情况
    (gdb) x/20xw 0xbffff400
                                                                                            (ebp)        (eip)
    0xbffff400:    0x005efff4    0x08049ff4                              0xbffff418    0x080482c4
    0xbffff410:    0x00c72d20    0x00000007(TestB:e)             0xbffff428    0x080483ea
    0xbffff420:    0x00000002(2再次压栈)    0xbffff444( i地址)    0xbffff448    0x0804840c
    0xbffff430:    0x00000002( 2压栈)    0xbffff444( i地址)         0x0804843b    0x005efff4
    0xbffff440:    0x08048430    0x00000005( i = 5)                 0xbffff4c8    0x004c6b56
                  
    继续执行完(*d = e)
    (gdb) s
    5        return e;
    (gdb) x/20xw 0xbffff400
                                                                                            (ebp)        (eip)
    0xbffff400:    0x005efff4    0x08049ff4                              0xbffff418    0x080482c4
    0xbffff410:    0x00c72d20    0x00000007(TestB:e)             0xbffff428    0x080483ea
    0xbffff420:    0x00000002(2再次压栈)    0xbffff444( i地址)    0xbffff448    0x0804840c
    0xbffff430:    0x00000002( 2压栈)    0xbffff444( i地址)         0x0804843b    0x005efff4
    0xbffff440:    0x08048430    0x00000005( i = 5)                 0xbffff4c8    0x004c6b56
    可以看出,C函数参数是按从右到左的顺序入栈的。堆栈桢之间是通过把ebp和eip压栈,来串在一起的。

     

    后面查看更多参数信息
    (gdb) info local
    e = 7
    (gdb) s
    6    }
    (gdb) s
    TestA (a=2, b=0xbffff444) at test.c:11
    11    }
    (gdb) info local
    No locals.
    (gdb) info arg
    a = 2
    b = 0xbffff444
    (gdb) s
    main () at test.c:17
    17        return 0;
    (gdb) info local
    i = 7
    r = 7
    -----------------------------------------------

    分析参数和局部变量定位

    abeen@ubuntu:~/Documents/C$ gdb a.out

    (gdb) set disassembly-flavor intel //设置反汇编指令格式
    (gdb) disass main
    Dump of assembler code for function main:
    0x080483ec <main+0>:    push   ebp
    0x080483ed <main+1>:    mov    ebp,esp
    0x080483ef <main+3>:    sub    esp,0x18
    0x080483f2 <main+6>:    mov    DWORD PTR [ebp-0x4],0x5
    0x080483f9 <main+13>:    lea    eax,[ebp-0x4]
    0x080483fc <main+16>:    mov    DWORD PTR [esp+0x4],eax
    0x08048400 <main+20>:    mov    DWORD PTR [esp],0x2
    0x08048407 <main+27>:    call   0x80483d2 <TestA>
    0x0804840c <main+32>:    mov    DWORD PTR [ebp-0x8],eax
    0x0804840f <main+35>:    mov    eax,0x0
    0x08048414 <main+40>:    leave  
    0x08048415 <main+41>:    ret    
    End of assembler dump.
    (gdb) disass TestA
    Dump of assembler code for function TestA:
    0x080483d2 <TestA+0>:    push   ebp
    0x080483d3 <TestA+1>:    mov    ebp,esp
    0x080483d5 <TestA+3>:    sub    esp,0x8
    0x080483d8 <TestA+6>:    mov    eax,DWORD PTR [ebp+0xc]
    0x080483db <TestA+9>:    mov    DWORD PTR [esp+0x4],eax
    0x080483df <TestA+13>:    mov    eax,DWORD PTR [ebp+0x8]
    0x080483e2 <TestA+16>:    mov    DWORD PTR [esp],eax
    0x080483e5 <TestA+19>:    call   0x80483b4 <TestB>
    0x080483ea <TestA+24>:    leave  
    0x080483eb <TestA+25>:    ret    
    End of assembler dump.
    (gdb) disass TestB
    Dump of assembler code for function TestB:
    0x080483b4 <TestB+0>:    push   ebp
    0x080483b5 <TestB+1>:    mov    ebp,esp
    0x080483b7 <TestB+3>:    sub    esp,0x10
    0x080483ba <TestB+6>:    mov    eax,DWORD PTR [ebp+0xc]
    0x080483bd <TestB+9>:    mov    eax,DWORD PTR [eax]
    0x080483bf <TestB+11>:    add    eax,DWORD PTR [ebp+0x8]
    0x080483c2 <TestB+14>:    mov    DWORD PTR [ebp-0x4],eax
    0x080483c5 <TestB+17>:    mov    eax,DWORD PTR [ebp+0xc]
    0x080483c8 <TestB+20>:    mov    edx,DWORD PTR [ebp-0x4]
    0x080483cb <TestB+23>:    mov    DWORD PTR [eax],edx
    0x080483cd <TestB+25>:    mov    eax,DWORD PTR [ebp-0x4]
    0x080483d0 <TestB+28>:    leave  
    0x080483d1 <TestB+29>:    ret    
    End of assembler dump.
    (gdb)
    看汇编指令,参数和局部变量是以ebp为基准+偏移量来定位的。最后用eax寄存器返回值。

  • 相关阅读:
    c# 生成随机时间
    HttpWebRequest.ReadWriteTimeout 属性
    如果分配给命令的连接位于本地挂起事务中,ExecuteNonQuery 要求命令拥有事务。命令的 Transaction 属性尚未初始化
    Host 'XXX' is not allowed to connect to this MySQL server 解决方案/如何开启MySQL的远程帐号
    C# winform 获取当前路径
    c# 操作符重载
    为何好男人总被坏女人搞定?【转】
    MySQL,SQLSERVER,ORACLE获取数据库表名及字段名
    病毒加壳技术与脱壳杀毒方法解析
    配置linux DNS
  • 原文地址:https://www.cnblogs.com/abeen/p/1609731.html
Copyright © 2020-2023  润新知