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個字節
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章