標題: 總結-c語言與匯編 [打印本頁]

作者: 51黑tt    時間: 2016-3-5 17:02
標題: 總結-c語言與匯編
1. c語言的函數(shù)調用與對應的匯編代碼 1.1 調用規(guī)則
比如調用函數(shù) function(parameter1, parameter2, parameter3)
Pascal調用規(guī)則
_cdecl調用規(guī)則
_stdcall調用規(guī)則
PUSH     parameter1
PUSH     parameter2
PUSH     parameter3
CALL      function
PUSH     parameter3
PUSH     parameter2
PUSH     parameter1
CALL      function
ADD       ESP, 0CH
PUSH     parameter3
PUSH     parameter2
PUSH     parameter1
CALL      function
參數(shù)從左到右傳遞壓棧,由被調用函數(shù)清理堆棧 參數(shù)從右到左傳遞壓棧,由調用函數(shù)負責清理堆棧 參數(shù)從右到左傳遞壓棧,由被調用函數(shù)負責清理堆棧
用于Win16平臺
C/C++調用標準
Windows API 使用


1.2 匯編代碼 對調用的方式,舉一個例子,對c語言常用的printf:

printf(“%d", a);
解析成匯編代碼如:
PUSH  a
PUSH  OFFSET String "%d"
CALL  printf
ADD ESP, 8

CALL指令和RET指令
段內調用
對CALL指令來說,其執(zhí)行的步驟一般包括:

對應 的RET指令為:

段間調用
如果是段間函數(shù)調用,則CALL的執(zhí)行過程一般是:


對應的RET指令執(zhí)行步驟:


被調用函數(shù)的執(zhí)行步驟
PUSH  EBP ; 保存當前堆;積bp,以作返回用
MOVE EBP, ESP ; 將當前esp的值賦給ebp,作為新的基址,即進入函數(shù)內部
SUB ESP, 0CCH ; 將esp往下移動一個范圍,開辟一片新的堆棧空間給當前函數(shù)使用
; 這是由于堆棧從高地址往低地址增長,所以,減一個值意味著開辟了
; 新的空間
................. ; 保存其他寄存器的值
.................
................. ; 恢復壓棧的其他寄存器的值
MOVE ESP, EBP ; 恢復esp的值為原來的堆棧棧頂值
POP EBP ; 恢復堆;窞樵肺恢
RET

一般來說,函數(shù)的返回值會放在EAX寄存器中返回。

2. c語言特殊語句塊的匯編代碼
1)For循環(huán)的匯編代碼模板
mov <循環(huán)變量>, <初始值> ; 循環(huán)變量賦初值
jmp B ; 直接跳轉到循環(huán)控制測試部分代碼
A: (改動循環(huán)變量) ; 修改循環(huán)變量值的部分代碼
......
B: cmp <循環(huán)變量>, <限制變量> ; 將循環(huán)變量的值進行測試、跳轉
jge 跳出循環(huán) ; 符合終止條件,則跳出循環(huán)體 ; (注意,這里的jl指令可以是其他的jge等,一具判斷條件而定)
(循環(huán)體代碼) ; 否則,進入循環(huán)體代碼執(zhí)行
...
jmp A ; 循環(huán)體結束的最后,是一個無條件跳轉語句,調回直接
; 修改循環(huán)變量的代碼
2)do循環(huán)的匯編代碼模板
A: (循環(huán)體) ; 直接是循環(huán)體代碼
....
cmp <循環(huán)變量>, <限制變量> ; 判斷是否需要終止循環(huán)
jl <循環(huán)開始處> ; 如果不符合終止條件,直接調回循環(huán)體開始處繼續(xù)執(zhí)行循環(huán)體
; 代碼(注意,這里的jl指令可以是其他的jge等,一具判斷條件而定)


3) while循環(huán)的匯編代碼模板
A: cmp <循環(huán)變量>, <限制變量> ; 先比較循環(huán)變量,是否需要進行循環(huán)
jge B ; 如果滿足停止循環(huán),則直接跳到B,即循環(huán)體后的第一個指令
(循環(huán)體)
……
jmp A ; 循環(huán)體的最后一條指令,是無條件跳轉到循環(huán)控制判斷指令A處
B: (循環(huán)結束了)

4)if-else的匯編代碼模板(待續(xù)...)
理解調用棧最重要的兩點是:棧的結構,EBP寄存器的作用。

首先要認識到這樣兩個事實:
   1、一個函數(shù)調用動作可分解為:零到多個PUSH指令(用于參數(shù)入棧),一個CALL指令。CALL指令內部其實還暗含了一個將返回地址(即CALL指令下一條指令的地址)壓棧的動作。
   2、幾乎任何本地編譯器都會在每個函數(shù)體之前插入類似如下指令:PUSH EBP; MOV EBP ESP;
即,在程序執(zhí)行到一個函數(shù)的真正函數(shù)體時,已有以下數(shù)據(jù)順序入棧:參數(shù),返回地址,EBP。
   由此得到類似如下的棧結構(參數(shù)入棧順序跟調用方式有關,這里以C語言默認的CDECL為例):

+| (棧底方向,高位地址) |
| ....................|
| ....................|
| 參數(shù)3                |
| 參數(shù)2                |
| 參數(shù)1                |
| 返回地址             |
-| 上一層[EBP]         |
| 局部變量1            |
| 局部變量2            |
|.....................|

   補充:棧一直隨著函數(shù)調用的深入,一直想棧頂方向壓下去。每次調用函數(shù)時候,先壓函數(shù)參數(shù)(從右往左順序壓),再壓入函數(shù)調用下條指令的地址(由call完成)。接著進入調用函數(shù)體中先執(zhí)行PUSH EBP; MOV EBP ESP;(一般已經由編譯器加入到函數(shù)頭中了),接著就是吧函數(shù)體中的局部變量壓入棧中。再遇到函數(shù)的調用的嵌套則依此類推。(added by smsong)

  “PUSH EBP”“MOV EBP ESP”這兩條指令實在大有深意:首先將EBP入棧,然后將棧頂指針ESP賦值給EBP!癕OV EBP ESP”這條指令表面上看是用ESP把EBP原來的值覆蓋了,其實不然——因為給EBP賦值之前,原EBP值已被壓棧(位于棧頂),而新的EBP又恰恰指向棧頂。
   此時EBP寄存器就已處于一個很重要的地位,該寄存器中存儲著棧中的一個地址(原EBP入棧后的棧頂),從該地址為基準,向上(棧底方向)能獲取返回地址、參數(shù)值,向下(棧頂方向)能獲取函數(shù)局部變量值,而該地址處又存儲著上一層函數(shù)調用時的EBP值!
   一般而言,ss:[ebp+4]處為返回地址,ss:[ebp+8]處為第一個參數(shù)值(最后一個入棧的參數(shù)值,此處假設其占用4字節(jié)內存),ss:[ebp-4]處為第一個局部變量,ss:[ebp]處為上一層EBP值。
    由于EBP中的地址處總是“上一層函數(shù)調用時的EBP值”,而在每一層函數(shù)調用中,都能通過當時的EBP值“向上(棧底方向)能獲取返回地址、參數(shù)值,向下(棧頂方向)能獲取函數(shù)局部變量值”。
如此形成遞歸,直至到達棧底。這就是函數(shù)調用棧。
編譯器對EBP的使用實在太精妙了。
從當前EBP出發(fā),逐層向上找到任何的EBP是很容易的:
unsigned int _ebp;
__asm _ebp, ebp;
while (not stack bottom)
{
    //...
    _ebp = *(unsigned int*)_ebp;
}
假如要寫一個簡單的調試器的話,注意需在被調試進程(而非當前進程——調試器進程)中讀取內存數(shù)據(jù)。
8個通用寄存器:
      數(shù)據(jù)寄存器:AX,BX,CX,DX
      指針寄存器:SP(堆棧指針),BP(基址指針)
      變址寄存器:SI(原地址),DI(目的地址)
1、通用寄存器
數(shù)據(jù)寄存器,指針寄存器和變址寄存器統(tǒng)稱為通用寄存器。這些寄存器除了各自專門用途外,它們均可用于傳送和暫存數(shù)據(jù),可以保存算術邏輯運算中的操作數(shù)和運算結果。
(1)數(shù)據(jù)寄存器
數(shù)據(jù)寄存器主要用來保存操作數(shù)或運算結果等信息,它們的存在節(jié)省了為存取操作數(shù)所需占用總線和訪問存儲器的時間。
(2)變址和指針寄存器
變址和指針寄存器主要用于存放某個存儲單元地址的偏移,或某組存儲單元地址的偏移,即作為存儲器(短)指針使用。作為通用寄存器,它們可以保存16位算術邏輯運算中的操作數(shù)和運算結果,有時運算結果就是需要的存儲單元地址的偏移。
2、控制寄存器(2個)
(1)指令指針寄存器
8086/8088CPU中的指令指針I(yè)P也是16位的。
指令指針I(yè)P給出接著要執(zhí)行的指令在代碼段中的偏移。
(2)標志寄存器
8086/8088CPU中有一個16位的標志寄存器,包含了9個標志,主要用于反映處理器的狀態(tài)和運算結果的某些特征。6個條件標志+3個方向標志
3、段寄存器(4個)
8086/8088CPU依賴其內部的四個段寄存器實現(xiàn)尋址1M字節(jié)物理地址空間。
8086/8088把1M字節(jié)地址空間分成若干邏輯段,當前使用的段值存放在段寄存器中。
由于8086/8088有這四個段寄存器,所以有四個當前使用段可以直接存取,這四個當前段分別稱為代碼段,數(shù)據(jù)段,堆棧段和附加段。
(1)代碼段
(2)數(shù)據(jù)段
(3)堆棧段
(4)附加段







歡迎光臨 (http://www.torrancerestoration.com/bbs/) Powered by Discuz! X3.1