免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3256 | 回复: 0
打印 上一主题 下一主题

C语言的反汇编代码(BP,SP的关系) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-02-02 18:01 |只看该作者 |倒序浏览
1. 最简单的代码:
//// test1.c
int main(){
    return 1;
}

编译、反汇编:
gcc test1.c
gdb ./a.out
(gdb) disassemble main

0x08048344 :        lea    0x4(%esp),%ecx   ;取出esp寄存器里的值,加上4,将得到值传递给ecx;
0x08048348 :        and    $0xfffffff0,%esp  ;使栈地址16字节对齐
0x0804834b :       pushl  -0x4(%ecx)  ;取出寄存器ecx的值,减去4,即esp的值,将得到的值作为地址,在内存找到该地址对应的值,将其压入栈中。
0x0804834e :      push   %ebp
0x0804834f :      mov    %esp,%ebp  ;创建Stack Frame(栈框架)
0x08048351 :      push   %ecx
0x08048352 :      mov    $0x1,%eax
0x08048357 :      pop    %ecx
0x08048358 :      pop    %ebp
0x08048359 :      lea    -0x4(%ecx),%esp  ;取出ecx 寄存器里的值,减去4,将得到值传递给esp;还原esp的值
0x0804835c :      ret  

常用指令解释:
CALL指令:
用来调用一个函数或过程,此时,下一条指令地址会被压入堆栈,以备返回时能恢复执行下条指令。
RET指令:
用来从一个函数或过程返回,之前CALL保存的下条指令地址会从栈内弹出到EIP寄存器中,程序转到CALL之前下条指令处执行
ENTER指令:
建立当前函数的栈框架,即相当于以下两条指令:
        pushl   %ebp
        movl    %esp,%ebp

LEAVE指令:
释放当前函数或者过程的栈框架,即相当于以下两条指令:
        movl ebp esp
        popl ebp


2. 函数间的调用代码:
假如函数A调用函数B,函数B调用函数C:
///// test2.c
void c(){}

void b(){c();}

void a(){b();}

int main(){
   a();
   return 1;
}
编译、反汇编:
gcc test1.c
gdb ./a.out
(gdb) disassemble main
Dump of assembler code for function main:
0x0804835d :       lea    0x4(%esp),%ecx
0x08048361 :        and    $0xfffffff0,%esp
0x08048364 :        pushl  -0x4(%ecx)
0x08048367 :      push   %ebp
0x08048368 :      mov    %esp,%ebp
0x0804836a :      push   %ecx
0x0804836b :     call   0x8048353
0x08048370 :      mov    $0x1,%eax
0x08048375 :      pop    %ecx
0x08048376 :      pop    %ebp
0x08048377 :      lea    -0x4(%ecx),%esp
0x0804837a :      ret   
End of assembler dump.
(gdb) disassemble a
Dump of assembler code for function a:
0x08048353 :  push   %ebp
0x08048354 :  mov    %esp,%ebp
0x08048356 :  call   0x8048349
0x0804835b :  pop    %ebp
0x0804835c :  ret   
End of assembler dump.
(gdb) disassemble b
Dump of assembler code for function b:
0x08048349 :  push   %ebp
0x0804834a :  mov    %esp,%ebp
0x0804834c :  call   0x8048344
0x08048351 :  pop    %ebp
0x08048352 :  ret   
End of assembler dump.
(gdb) disassemble c
Dump of assembler code for function c:
0x08048344 :  push   %ebp
0x08048345 :  mov    %esp,%ebp
0x08048347 :  pop    %ebp
0x08048348 :  ret   
End of assembler dump.

函数调用栈的状态:
    +-------------------------+----> 高地址
    | EIP (Main函数返回地址)   |
    +-------------------------+
    | EBP (Main函数的EBP)      | --+ 当前函数A的EBPA (即SFP框架指针)
    +-------------------------+   +-->offsetA
    | A 中的局部变量           | --+ 指向函数A新分配的局部变量,局部变量可以通过EBPA-offsetA访问
    +-------------------------+
    | Arg .(函数B的参数)     |   --+ 函数的参数可以由B的EBPB+offsetB访问
   +-------------------------+   +--> offsetB
   | EIP (A函数的返回地址)    |   |
    +-------------------------+ --+
    | EBP (A函数的EBP)        |当前函数B的EBPB (即SFP框架指针)
    +-------------------------+
    | B 中的局部变量          |   
    +-------------------------+   
    | Arg .(函数C的参数)     |   
    +-------------------------+   
    | EIP (B函数的返回地址)   |   
    +-------------------------+  
  | EBP (B函数的EBP)       | --+ 当前函数C的EBPC (即SFP框架指针)
    +-------------------------+
    | C 中的局部变量          |
    | ..........              | 指向函数C新分配的局部变量
+-------------------------+----> 低地址
函数被调用时

    1) EIP/EBP成为新函数栈的边界
    函数被调用时,返回时的EIP首先被压入堆栈;创建栈框架时,上级函数栈的EBP被压入堆栈,与EIP一道行成新函数栈框架的边界
    2) EBP成为栈框架指针SFP,用来指示新函数栈的边界
   
栈框架建立后,EBP指向的栈的内容就是上一级函数栈的EBP,可以想象,通过EBP就可以把层层调用函数的栈都回朔遍历一遍,调试器就是利用这个特性实现 backtrace功能的
    3) ESP总是作为栈指针指向栈顶,用来分配栈空间
    栈分配空间给函数局部变量时的语句通常就是给ESP减去一个常数值,例如,分配一个整型数据就是 ESP-4
    4) 函数的参数传递和局部变量访问可以通过SFPEBP来实现
    由于栈框架指针永远指向当前函数的栈基地址,参数和局部变量访问通常为如下形式:
        +8+xx(%ebp)         ; 函数入口参数的的访问
        -xx(%ebp)           ; 函数局部变量访问

3含局部变量时:
int main(){
  int a = 3;
  int b = 5;
return 1;
}

(gdb) disassemble main
Dump of assembler code for function main:
0x08048344 :        lea    0x4(%esp),%ecx
0x08048348 :        and    $0xfffffff0,%esp
0x0804834b :       pushl  -0x4(%ecx)
0x0804834e :      push   %ebp
0x0804834f :      mov    %esp,%ebp
0x08048351 :      push   %ecx
0x08048352 :      sub    $0x10,%esp
0x08048355 :      movl   $0x3,-0x8(%ebp)  ; a = 3
0x0804835c :      movl   $0x5,-0xc(%ebp) ; b = 5;
0x08048363 :      mov    $0x1,%eax ;return 1;
0x08048368 :      add    $0x10,%esp
0x0804836b :     pop    %ecx
0x0804836c :      pop    %ebp
0x0804836d :     lea    -0x4(%ecx),%esp
0x08048370 :      ret   
End of assembler dump.
通过反汇编代码对程序运行时的寄存器和栈的观察和分析,可以得出局部变量在栈中的访问和分配及释放方式:
        1.局部变量的分配,可以通过esp减去所需字节数
           sub    $0x10,%esp
        2.局部变量的释放,可以通过esp加上已分配的字节
           add    $0x10,%esp      
        3.局部变量的访问,可以通过ebp减去偏移量
           movl   $0x3,-0x8(%ebp)
           
4. 函数调用时有参数
int func(int m, int n)
{
   return m+n;
}

int main(){
  int a = 3;
  int b = 5;
  int c = 0;
  c = func(a, b);
  return c;
}

(gdb) disassemble main
Dump of assembler code for function main:
0x0804834f :        lea    0x4(%esp),%ecx
0x08048353 :        and    $0xfffffff0,%esp
0x08048356 :        pushl  -0x4(%ecx)
0x08048359 :      push   %ebp
0x0804835a :      mov    %esp,%ebp
0x0804835c :      push   %ecx
0x0804835d :     sub    $0x18,%esp
0x08048360 :      movl   $0x3,-0x8(%ebp)  ;a = 3
0x08048367 :      movl   $0x5,-0xc(%ebp)  ;b = 5
0x0804836e :      movl   $0x0,-0x10(%ebp)  ;c = 0;
0x08048375 :      mov    -0xc(%ebp),%eax  
0x08048378 :      mov    %eax,0x4(%esp)  ; n = b
0x0804837c :      mov    -0x8(%ebp),%eax
0x0804837f :      mov    %eax,(%esp)    ; m = a;
0x08048382 :      call   0x8048344   ;func(a,b);
0x08048387 :      mov    %eax,-0x10(%ebp) ; c = func(a, b);
0x0804838a :      mov    -0x10(%ebp),%eax ; return c
0x0804838d :     add    $0x18,%esp
0x08048390 :      pop    %ecx
0x08048391 :      pop    %ebp
0x08048392 :      lea    -0x4(%ecx),%esp
0x08048395 :      ret   
End of assembler dump.
(gdb) disassemble func
Dump of assembler code for function func:
0x08048344 :        push   %ebp
0x08048345 :        mov    %esp,%ebp
0x08048347 :        mov    0xc(%ebp),%eax ; n
0x0804834a :        add    0x8(%ebp),%eax ; m+n
0x0804834d :        pop    %ebp
0x0804834e :      ret   
End of assembler dump.
参数的访问,可以通过ebp加上减去偏移量:
mov    0xc(%ebp),%eax
add     0x8(%ebp),%eax
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/44068/showart_2167734.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP