您的位置:首页 > 编程语言 > C语言/C++

C++反汇编代码分析——函数调用

2013-05-05 15:56 375 查看
代码如下:

#include "stdlib.h"

int sum(int a,int b,int m,int n)

{

  return a+b;

}

void main()

{

  int result = sum(1,2,3,4);

  system("pause");

}

  有四个参数的sum函数,接着在main方法中调用sum函数。

在debug环境下,单步调试如下:

  11: void main()

  12: {

  00401060 push ebp

  ;保存ebp,执行这句之前,ESP = 0012FF4C EBP = 0012FF88

  ;执行后,ESP = 0012FF48 EBP = 0012FF88,ESP减小,EBP

不变

  00401061 mov ebp,esp

  ;将esp放入ebp中,此时ebp和esp相同,即执行后ESP =

0012FF48 EBP = 0012FF48

  ;原EBP值已经被压栈(位于栈顶),而新的EBP又恰恰指向

栈顶。

  ;此时EBP寄存器就已经处于一个非常重要的地位,该寄存器

中存储着栈中的一个地址(原EBP入栈后的栈顶),

  ;从该地址为基准,向上(栈底方向)能获取返回地址、参数值

(假如main中有参数,“获取参数值”会比较容易理解,

  ;不过在看下边的sum函数调用时会有体会的),向下(栈顶

方向)能获取函数局部变量值,

  ;而该地址处又存储着上一层函数调用时的EBP值!

  00401063 sub esp,44h

  ;把esp往上移动一个范围

  ;等于在栈中空出一片空间来存局部变量

  ;执行这句后ESP = 0012FF04 EBP = 0012FF48

  00401066 push ebx

  00401067 push esi

  00401068 push edi

  ;保存三个寄存器的值

  00401069 lea edi,[ebp-44h]

  ;把ebp-44h加载到edi中,目的是保存局部变量的区域

  0040106C mov ecx,11h

  00401071 mov eax,0CCCCCCCCh

  00401076 rep stos dword ptr [edi]

  ;从ebp-44h开始的区域初始化成全部0CCCCCCCCh,就是

int3断点,初始化局部变量空间

  ;REP ;CX不等于0 ,则重复执行字符串指令

  ;格式: STOS OPRD

  ;功能: 把AL(字节)或AX(字)中的数据存储到DI为目的串地址

指针所寻址的存储器单元中去.指针DI将根据DF的值进行自动

  ;调整. 其中OPRD为目的串符号地址.

  ;以上的语句就是在栈中开辟一块空间放局部变量

  ;然后把这块空间都初始化为0CCCCCCCCh,就是int3断点,

一个中断指令。

  ;因为局部变量不可能被执行,执行了就会出错,这时候发生中

断提示开发者。

  13: int result = sum(1,2,3,4);

  00401078 push 4

  0040107A push 3

  0040107C push 2

  0040107E push 1

;各个参数入栈,注意查看寄存器ESP值的变化

  ;亦可以看到参数入栈的顺序,从右到左

  ;变化为:ESP = 0012FEF8-->ESP = 0012FEF4-->ESP =

0012FEF0-->ESP = 0012FEEC-->ESP = 0012FEE8

  00401080 call @ILT+15(boxer) (00401014)

  ;调用sum函数,可以按F11跟进

  ;注:f10(step over),单步调试,遇到函数调用,直接执行,

不会进入函数内部

  ;f11(step into),单步调试,遇到函数调用,会进入函数内



  ;shift+f11(step out),进入函数内部后,想从函数内部跳出,

用此快捷方式

  ;ctrl+f10(run to cursor),呵呵,看英语注释就应该知道是什

么意思了,不再解释

  00401085 add esp,10h

  ;调用完函数后恢复/释放栈,执行后ESP = 0012FEF8,与sum

函数的参数入栈前的数值一致

  00401088 mov dword ptr [ebp-4],eax

  ;将结果存放在result中,原因详看最后有关ss的注释

  14: system("pause");

  0040108B push offset string "pause" (00422f6c)

  00401090 call system (0040eed0)

  00401095 add esp ,4

  ;有关system(“pause”)的处理,此处不讨论

  15: }

  00401098 pop edi

  00401099 pop esi

  0040109A pop ebx

  ;恢复原来寄存器的值,怎么“吃”进去,怎么“吐”出来

  0040109B add esp,44h

  ;恢复ESP,对应上边的sub esp,44h

  0040109E cmp ebp,esp

  ;检查esp是否正常,不正常就进入下边的call里面debug

  004010A0 call __chkesp (004010b0)

  ;处理可能出现的堆栈异常,如果有的话,就会陷入debug

  004010A5 mov esp,ebp

  004010A7 pop ebp

  ;恢复原来的esp和ebp,让上一个调用函数正常使用

  004010A8 ret

;将返回地址存入eip,转移流程

  ;如果函数有返回值,返回值将放在eax返回(这就是很多软件

给秒杀爆破的原因了,因为eax的返回值是可以改的)

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

  ;以上即是主函数调用的反汇编过程,下边来看调用sum函数

的过程:

  ;上边有说在00401080 call @ILT+15(boxer) (00401014)

这一句处,用f11单步调试,f11后如下句:

  00401014 jmp sum (00401020)

  ;即跳转到sum函数的代码段中,再f11如下:

  6: int sum(int a,int b,int m,int n)

  7: {

  00401020 push ebp

  00401021 mov ebp,esp

  00401023 sub esp,40h

  00401026 push ebx

  00401027 push esi

  00401028 push edi

  00401029 lea edi,[ebp-40h]

  0040102C mov ecx,10h

  00401031 mov eax,0CCCCCCCCh

  00401036 rep stos dword ptr [edi]

  ;可见,上边几乎与主函数调用相同,每一步不再赘述,可对照

上边主函数调用的注释

  8: return a+b;

  00401038 mov eax,dword ptr [ebp+8]

  ;取第一个参数放在eax

  0040103B add eax,dword ptr [ebp+0Ch]

  ;取第二个参数,与eax中的数值相加并存在eax中

  9: }

  0040103E pop edi

  0040103F pop esi

  00401040 pop ebx

  00401041 mov esp,ebp

  00401043 pop ebp

  00401044 ret

  ;收尾操作,比前边只是少了检查esp操作罢了

  有关ss部分的注释:

  ;一般而言,ss:[ebp+4]处为返回地址

  ;ss:[ebp+8]处为第一个参数值(这里是a),ss:[ebp+0Ch]处

为第二个参数(这里是b,这里8+4=12=0Ch)

  ;ss:[ebp-4]处为第一个局部变量(如main中的result),ss:[ebp]

处为上一层EBP值

  ;ebp和函数返回值是32位,所以占4个字节
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: