反汇编一个c程序

方式一:使用gcc

gcc编译有四步走,预编译,编译,汇编,连接

使用-S编译选项

 

[c-sharp] view plaincopy
  1. gcc  -S  test.c  

 

会在当前目录下生成test.s的文件,该文件即是相应的汇编程序

 


方式二:使用gdb

首先编译时要是用-g编译选项

 

[c-sharp] view plaincopy
  1. gcc -g ./test.c  -o ./test  

 

接着运行gdb

 

[c-sharp] view plaincopy
  1. gdb ./test--(可执行文件)  

 

 

在gdb中使用 disassemble + frame(帧),即可查看相应代码段的汇编代码

frame通常为一个函数名。

 

方式三:使用objdump

 

命令为

 

[c-sharp] view plaincopy
  1. objdump -d test.o--(目标文件)  

 

或者  

 

[c-sharp] view plaincopy
  1. objdump -d  test--(可执行文件)  

 

 

 

 

 

 

 

一个反汇编代码解释:

(本例使用的GCC的汇编格式,这种格式叫做GAS(GNU ASsembler ,GNU汇编器) )

 

c语言代码如下:

------test.c-------

 

  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3. int static_var = 5;  
  4. int   
  5. fun_ret_int(int a, int b, register int c)  
  6. {  
  7. int d=1;  
  8. return a+b+c+d;  
  9. }  
  10. void fun()  
  11. {  
  12. int i1, i2, i3, i4,i5, i6,i7,i8,i9,i10;  
  13. i1=1;i2=3;i3=5; i4=7;i5=9; i6=11;i7=13;i8=15;i9=17;i10=19;  
  14. int i;  
  15. for(i=11; i< 20;++i);  
  16. i2 = fun_ret_int(1, 2, i1);  
  17. }  
  18. int main(int argc ,char *argv[])  
  19. {  
  20. int i =1;   
  21. int b =2;  
  22. argc = 3;  
  23. char **a=argv;  
  24. fun();  
  25. return 0;  
  26. }  

 

 

现在编译:

gcc  test.c -o test

反汇编 :

objdump -d test

 

 

 

我截取部分代码如下:

 

  1. 08048394 <fun_ret_int>:  
  2.  8048394:   55                   push   %ebp  
  3.  8048395:   89 e5                   mov    %esp,%ebp  
  4.  8048397:   83 ec 10             sub    $0x10,%esp  
  5.  804839a:   8b 4d 10             mov    0x10(%ebp),%ecx  
  6.  804839d:   c7 45 fc 01 00 00 00 movl   $0x1,-0x4(%ebp)  
  7.  80483a4:   8b 45 0c             mov    0xc(%ebp),%eax  
  8.  80483a7:   8b 55 08             mov    0x8(%ebp),%edx  
  9.  80483aa:   8d 04 02             lea    (%edx,%eax,1),%eax  
  10.  80483ad:   01 c8                   add    %ecx,%eax  
  11.  80483af:   03 45 fc             add    -0x4(%ebp),%eax  
  12.  80483b2:   c9                   leave    
  13.  80483b3:   c3                   ret      
  14. 080483b4 <fun>:  
  15.  80483b4:   55                   push   %ebp  
  16.  80483b5:   89 e5                   mov    %esp,%ebp  
  17.  80483b7:   83 ec 3c             sub    $0x3c,%esp  
  18.  80483ba:   c7 45 fc 01 00 00 00 movl   $0x1,-0x4(%ebp)  
  19.  80483c1:   c7 45 f8 03 00 00 00 movl   $0x3,-0x8(%ebp)  
  20.  80483c8:   c7 45 f4 05 00 00 00 movl   $0x5,-0xc(%ebp)  
  21.  80483cf:   c7 45 f0 07 00 00 00 movl   $0x7,-0x10(%ebp)  
  22.  80483d6:   c7 45 ec 09 00 00 00 movl   $0x9,-0x14(%ebp)  
  23.  80483dd:   c7 45 e8 0b 00 00 00 movl   $0xb,-0x18(%ebp)  
  24.  80483e4:   c7 45 e4 0d 00 00 00 movl   $0xd,-0x1c(%ebp)  
  25.  80483eb:   c7 45 e0 0f 00 00 00 movl   $0xf,-0x20(%ebp)  
  26.  80483f2:   c7 45 dc 11 00 00 00 movl   $0x11,-0x24(%ebp)  
  27.  80483f9:   c7 45 d8 13 00 00 00 movl   $0x13,-0x28(%ebp)  
  28.  8048400:   c7 45 d4 01 00 00 00 movl   $0x1,-0x2c(%ebp)  
  29.  8048407:   c7 45 d0 0b 00 00 00 movl   $0xb,-0x30(%ebp)  
  30.  804840e:   eb 04                   jmp    8048414 <fun+0x60>  
  31.  8048410:   83 45 d0 01             addl   $0x1,-0x30(%ebp)  
  32.  8048414:   83 7d d0 13             cmpl   $0x13,-0x30(%ebp)  
  33.  8048418:   7e f6                   jle    8048410 <fun+0x5c>  
  34.  804841a:   8b 45 fc             mov    -0x4(%ebp),%eax  
  35.  804841d:   89 44 24 08             mov    %eax,0x8(%esp)  
  36.  8048421:   c7 44 24 04 02 00 00 movl   $0x2,0x4(%esp)  
  37.  8048428:   00   
  38.  8048429:   c7 04 24 01 00 00 00 movl   $0x1,(%esp)  
  39.  8048430:   e8 5f ff ff ff       call   8048394 <fun_ret_int>  
  40.  8048435:   89 45 f8             mov    %eax,-0x8(%ebp)  
  41.  8048438:   c9                   leave    
  42.  8048439:   c3                   ret      
  43. 0804843a <main>:  
  44.  804843a:   55                   push   %ebp  
  45.  804843b:   89 e5                   mov    %esp,%ebp  
  46.  804843d:   83 ec 10             sub    $0x10,%esp  
  47.  8048440:   c7 45 fc 01 00 00 00 movl   $0x1,-0x4(%ebp)  
  48.  8048447:   c7 45 f8 02 00 00 00 movl   $0x2,-0x8(%ebp)  
  49.  804844e:   c7 45 08 03 00 00 00 movl   $0x3,0x8(%ebp)  
  50.  8048455:   8b 45 0c             mov    0xc(%ebp),%eax  
  51.  8048458:   89 45 f4             mov    %eax,-0xc(%ebp)  
  52.  804845b:   e8 54 ff ff ff       call   80483b4 <fun>  
  53.  8048460:   b8 00 00 00 00       mov    $0x0,%eax  
  54.  8048465:   c9                   leave    
  55.  8048466:   c3                   ret      
  56.  8048467:   90                   nop  
  57.  8048468:   90                   nop  
  58.  8048469:   90                   nop  
  59.  804846a:   90                   nop  
  60.  804846b:   90                   nop  
  61.  804846c:   90                   nop  
  62.  804846d:   90                   nop  
  63.  804846e:   90                   nop  
  64.  804846f:   90                   nop  

 

 

 

现在尝试对其中的一些语句进行分析:

 

在每个函数的开始部分都是:

[c-sharp] view plaincopy
  1. push   %ebp  
  2. mov    %esp,%ebp  
  3. sub    $***,%esp  

 

 

%ebp---是帧寄存器,在函数中就是函数的基址寄存器,指向一个函数的栈底(帧底)。

%esp---是栈寄存器,相当于是整个程序的基址寄存器,始终指向栈顶。

push---入栈操作。

mov ---移动

sub ---减法

 

第一句话 push   %ebp 的意思是%ebp入栈,此时的%ebp保存的是上一个函数的帧起始地址,也即调用该函数的地址。

把%ebp压栈,保存起来,以便返回。

 

第二句  mov    %esp,%ebp 的意思是  把%esp赋值给%ebp,%esp保存的是当前程序的栈顶,也即该函数所占用内存的起始地址。

把%esp赋值给%ebp,也就把%ebp设置成了当前函数的帧起始地址。

 

第三句话sub    $***,%esp,并不会在每个程序中都会出现。可以尝试一下,如果一个函数没有任何局部变量,那么反汇编这句话也就

不会出来。这句话的意思是,把%esp减去一个数。我们知道栈空间是由高到底发展的,所以%esp++,相当于%esp=%esp-1。因为调用了 新函数,而且该函数有局部变量,那么栈空间就变大了,所以要扩展栈空间,也即是修改%esp,让其指向更低的地址。而让%esp减去多少呢?这要看函数占 用多少空间,于其中的局部变量有关,以及他将调用的函数参数有关。其并不计算其参数所占的空间,其参数所占的空间要算在调用它的函数中。

 

 

下面来看看参数的压栈顺序;

 

函数fun中语句:

  1. i2 = fun_ret_int(1, 2, i1);  

 

所对应的汇编如下:

[c-sharp] view plaincopy
  1. 804841a:    8b 45 fc             mov    -0x4(%ebp),%eax    
  2. 804841d:    89 44 24 08             mov    %eax,0x8(%esp)  
  3. 8048421:    c7 44 24 04 02 00 00 movl   $0x2,0x4(%esp)  
  4. 8048428:    00   
  5. 8048429:    c7 04 24 01 00 00 00 movl   $0x1,(%esp)  
  6. 8048430:    e8 5f ff ff ff       call   8048394 <fun_ret_int>  

 

 

从这个汇编中可以看出函数参数的入栈顺序是自左往右的。

 

mov    -0x4(%ebp),%eax  是把i1 放到%eax 寄存器中

mov    %eax,0x8(%esp)   是把%eax压栈,所以这句话是把i1放在距离%esp为8byte的地方,即8~12byte存放的是一个i1(int)

movl   $0x2,0x4(%esp)   距%esp 4~8byte存放的是2(int)

movl   $0x1,(%esp)      距%esp 0~4byte存放的是1(int)

 

因为栈是由高到低的,且%esp始终指向栈顶。所以看出入栈顺序是i1,2,1。正好与c文件中fun函数的参数顺序相反。

自右向左压栈有什么好处呢?第一,由于栈是FILO,所以反方向入栈,那么第一个参数也就距离%esp越近。每次取参数时也就很方便,不用把所有参数占用的空间都计算出来,然后在取。第二,当传递的参数过多时,每次都从栈顶计算,取适当位置的参数,其他便可忽略。

 

via:http://blog.csdn.net/plo154100/article/details/6409778

posted on 2013-05-04 00:25  云化雨  阅读(879)  评论(0编辑  收藏  举报

导航