zoukankan      html  css  js  c++  java
  • C++反汇编代码分析--函数调用

    推荐阅读:

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

    C++反汇编代码分析–循环结构

    C++反汇编代码分析–偷调函数

    走进内存,走进汇编指令来看C/C++指针

      代码如下:

        #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个字节

    《完》

    Keep it simple!
    作者:N3verL4nd
    知识共享,欢迎转载。
  • 相关阅读:
    mysql基础-01
    Delphi 和键盘有关的API函数(Keyboard Input)
    Delphi System单元-Odd- 判断是否是奇数
    Delphi 键盘API GetKeyState、GetAsyncKeyState -获取键盘 / 按键值Key的状态
    Delphi 全局热键KeyPress 和 热键 API(RegisterHotKey、UnRegisterHotKey、GlobalAddAtom、GlobalDeleteAtom、GlobalFindAtom)
    Delphi XE Android platform uses-permission[2] AndroidManifest.xml 配置
    Delphi XE Android platform uses-permission[1] 权限列表
    Delphi XE Andriod 文件后缀对应MIME类型
    Delphi XE RTL Androidapi 单元
    Delphi XE FMX TFmxObject 类 和 单元
  • 原文地址:https://www.cnblogs.com/lgh1992314/p/5834667.html
Copyright © 2011-2022 走看看