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
    知识共享,欢迎转载。
  • 相关阅读:
    单例模式
    HashSet、LinkedHashSet、SortedSet、TreeSet
    ArrayList、LinkedList、CopyOnWriteArrayList
    HashMap、Hashtable、LinkedHashMap
    andrew ng machine learning week8 非监督学习
    andrew ng machine learning week7 支持向量机
    andrew ng machine learning week6 机器学习算法理论
    andrew ng machine learning week5 神经网络
    andrew ng machine learning week4 神经网络
    vue组件监听属性变化watch方法报[Vue warn]: Method "watch" has type "object" in the component definition. Did you reference the function correctly?
  • 原文地址:https://www.cnblogs.com/lgh1992314/p/5834667.html
Copyright © 2011-2022 走看看