1、 debug模式下,vc++6.0下斷點執行,按ctrl+f11可檢視彙編**;另外可以用cl /c /fas yourcppfile.cpp命令列在同目錄生成yourcppfile.asm彙編檔案。
2、 push將32位運算元壓入堆疊,esp指向棧頂,故esp減去4(位元組=32位,在64位機器上則是8)。記住:esp為棧頂指標,堆疊越高,這個值越小。由於intel處理器採用小端儲存模式,故當push乙個暫存器(如ebp)的值0012ffb4時,先存低位元組再存高位元組,即堆疊從棧頂向下應該是b4ff1200,當然,採用long型hex解析記憶體時顯示的數值將不變,仍是0012ffb4。
3、 call的本質相當於push+jmp(壓入call語句下一條指令位址,然後跳轉至call指定的函式入口位址),ret的本質相當於pop+jmp(彈出call語句下一條指令位址,然後跳轉該位址)。
4、 對堆疊的操作指令除了push、pop、call、ret以外,還可以是add,sub。在c語言中區域性變數是保持在棧裡,可以用esp – 4*4的方式在堆疊中分配4個4位元組長的整形空間,而不用push4次。
5、 用xor eax,eax來取代mov eax,0,這樣來實現清零的好處是:速度更快,占用位元組更少。
6、 lea取址運算子,但如下語句:lea edi,[ebp - 0cch]與mov edi,ebp - 0cch在語義上是等同的([x]表示儲存器x中的內容),但是後者會導致語法錯誤,原因是mov不支援後乙個運算元為暫存器減去乙個數字的形式
7、 c語言函式呼叫預設方式(_cdcall):呼叫方把引數反序(從右到左)壓入堆疊,被呼叫方把堆疊復原(獲取引數)。這些引數須對齊到機器字長,32、64位cpu分別對齊到4、8個位元組。
8、 右3可知,call和ret並不是函式存在的絕對證據,我們可以自行操作堆疊然後使用jmp絕對跳轉實現函式呼叫。由於高階語言對函式呼叫的規則各不一樣,由此產生了呼叫約定,在windows上有:pascal方式、winapi方式(_stdcall)、c方式(_cdcall)。
9、 pascal函式呼叫方式基本用在16位函式庫中,現在基本不用。
10、_stdcall函式呼叫方式規則(windows api):引數從右到左入棧;被呼叫的函式在返回前自行清理堆疊(可變引數函式呼叫除外)。
11、_cdcall函式呼叫方式規則(c編譯器預設):引數從右到左入棧;函式返回後,呼叫者負責清理堆疊,由此通常生成較大可執行檔案
12、不管何種呼叫方式,返回值都放入eax暫存器中。
13、_cdcall函式:
int myfunction(int a,int b)
彙編後**為:
int myfunction(int a,int b)
0040d44e pop edi
0040d44f pop esi
0040d450 pop ebx ;7)
0040d451 mov esp,ebp
0040d453 pop ebp ;8)
0040d454 ret
它所做的事情有:
1) 儲存ebp(基址暫存器)。儲存之前的esp值,在返回時恢復,使函式對堆疊能夠實現正確操作。
2) 儲存esp到ebp,此時兩者相等,都是函式呼叫以後的棧基址(棧頂)。
3) 在棧中騰出40h的位元組區域用來儲存區域性變數。大小是可變的,由編譯器自動分配。
4) 儲存ebx,esi,edi到堆疊,函式呼叫完後恢復。
5) 初始化區域性變數區域,迴圈10h次,每次賦值4個位元組(共10h * 4 = 40h位元組,與上面分配的位元組區域剛好對應)為0cccccccch,這個值實際上是int3的機器碼,是乙個斷點中斷指令,因區域性變數區域不可能被執行,如果執行了則報錯。
6) 第乙個引數獲取位置為ebp + 雙倍cpu字長,這裡是ebp + 8,後面的依據型別進行偏移。需要說明一點的是,呼叫者在呼叫前將引數倒序壓入堆疊,所有引數壓入以後,在執行call指令時,它會自動將call指令下面的一條指令位址壓入堆疊;此外,進入呼叫的函式以後,第一件事就是壓入ebp到堆疊。由此可以看出,函式當前棧頂(esp指向位置)與最後乙個壓入棧的引數(引數列表的第乙個引數)相隔了兩個cpu字長。這裡是4 * 2 =8個位元組,故為ebp + 8。
7) 恢復ebx,esi,edi。這裡是與入口對應的現場恢復,沒什麼好說的
8) ebp是被呼叫者棧頂指標,其記憶體單元的值是呼叫者棧頂指標。所以,這裡一方面是使esp指向被呼叫者的棧頂(也是呼叫者的棧底),另一方面是恢復呼叫者的棧基址。
9) ret執行函式返回,此時,esp自動加上乙個cpu字長,指向最後乙個被壓入的引數位置。
10) 函式的返回值在eax中,這裡不需要額外操作,如果結果不是在eax,則在返回前一定有mov操作(或其等同效果的操作)
14、對上面的程式執行如下呼叫:
int main(void)
其彙編**為:
int main(void)
0040d416 pop edi
0040d417 pop esi
0040d418 pop ebx
0040d419 add esp,40h
0040d41c cmp ebp,esp
0040d41e call __chkesp (0040d460)
0040d423 mov esp,ebp
0040d425 pop ebp
0040d426 ret
對以上**執行時的記憶體單元資料情況如下表: 操作
堆疊位址
hex資料
描述ebp暫存器……
4)0012fed8
0012ff80
myfunction函式壓入edi
0012ff24
4)0012fedc
01f8bce8
myfunction函式壓入esi
0012ff24
4)0012fee0
7ffdf000
myfunction函式壓入ebx
0012ff24
3)、5)
0012fee4
~ 0012ff20
cccccccc
… cccccccc
myfunction函式壓入預留區域性變數儲存區,全部初始化為0xcccccccch
0012ff24
1)、2)
0012ff24
0012ff80
myfunction函式壓入ebp
0012ff24
h)0012ff28
0040d411
mian函式call呼叫,壓入返回位址
0012ff80
g)0012ff2c
00000001
引數1入棧
0012ff80
d)0012ff30
00000002
引數2入棧
0012ff80
e)0012ff34
01dd0000
main函式壓入edi
0012ff80
d)0012ff38
01f8bce8
main函式壓入esi
0012ff80
c)0012ff3c
7ffdf000
main函式壓入ebx
0012ff80
b)0012ff40
~ 0012ff7c
cccccccc
… cccccccc
main函式壓入預留區域性變數儲存區,全部初始化為0xcccccccch
0012ff80
a)0012ff80
0012ffc0
main函式壓入ebp
0012ff80
函式地用過程中的操作由下往上看,注意call與ret指令執行時的堆疊變化。其中紅色和藍色為ebp暫存器變化情況,灰綠色為引數壓棧。
15、 需要注意的是,以上的彙編**來自於vc++6.0編譯器,在自行寫彙編或者嵌入彙編時要稍做改變。比如說call指令直接寫為call myfunction
組合語言 彙編指令
功能 用來進行資料傳輸。以 mov a,b 為例,相當於a b。具有以下形式 mov 暫存器,資料 mov 暫存器,暫存器 mov 暫存器,記憶體單元 mov 記憶體單元,暫存器 mov 段暫存器,暫存器 功能 用來做加法。以 add a,b 為例,相當於a a b。具有的形式,和 mov 一樣。功...
dx 彙編dec 組合語言 彙編指令
mov 功能 用來進行資料傳輸。以 mov a,b 為例,相當於a b。具有以下形式 mov 暫存器,資料 mov 暫存器,暫存器 mov 暫存器,記憶體單元 mov 記憶體單元,暫存器 mov 段暫存器,暫存器 add功能 用來做加法。以 add a,b 為例,相當於a a b。具有的形式,和 m...
彙編Lea 指令與 Mov 指令
比如你用local在棧上定義了乙個區域性變數localvar,你知道實際的指令是什麼麼?一般都差不多像下面的樣子 push ebp mov esp,ebp sub esp,4 現在棧上就有了4各位元組的空間,這就是你的區域性變數。接下來,你執行mov localvar,4,那麼實際的指令又是什麼?是...