2.13 堆棧傳參

2.13 堆棧傳參

為什麼學習本節內容?因為我們要學習彙編語言中參數傳遞的方式,而堆棧傳參會使我們工作效率提高。

本節必須掌握的知識點:

掌握參數傳遞的方式

熟練使用堆棧傳參

我們無論學習什麼語言,都離不開函數、參數、參數傳遞、函數調用、返回值等這幾個步驟。在上節中我們介紹了函數及函數調用並簡略的說了一下參數、參數傳遞、返回值。本節將繼續探討參數傳遞。

2.13.1【參數傳遞方式】

參數傳遞的方式有兩種:

1、寄存器傳參

2、堆棧傳參

【寄存器傳參的方式】

在上一節中我們做了一個例子,編寫一個函數,實現任意兩個整數相加,就是用的寄存器傳參的方式介紹的,下面是完整代碼。

MOV ECX ,1

MOV EDX ,2

CALL 0x77068E51 (注:CALL後面跟的地址要根據你自己編寫的函數實際情況來定)

這裡是函數體,實現任意2個整數相加的函數

0x77068E51 ADD ECX,EDX (ECX存儲的數據+EDX存儲的數據的值保存到ECX中)

MOV EAX,ECX (所得到的結果放到EAX中)

RETN

已經寫出來了代碼,可還是不知道什麼是寄存器傳參。我們分析上面代碼來介紹寄存器傳參。

根據題意知,任意兩個整數,這裡知道這兩個整數是存在,根據代碼可知我們假設的這兩個整數分別為1、2,那存在哪裡呢?根據

MOV ECX,1

MOV EDX,2

可知把1保存在了ECX中,把2保存在了EDX中,ECX、EDX大家都知道這是寄存器,把兩個整數保存在了寄存器裡,執行CALL指令後,重點來了。

根據

ADD ECX,EDX

MOV EAX,ECX

這兩行指令我們知道了,實現兩個整數相加的功能,是通過寄存器中保存的數據相互做運算的,運算的結果保存在了EAX中。這是我們自己約定用寄存器傳參的方式。

總結:我們在編寫一個彙編函數時,用寄存器傳參的方式,要提前約定好參與函數功能的寄存器,將參數存入函數約定好的寄存器中,在進行運算。

那麼問題來了,我們知道的寄存器也就那麼幾十個,如果一個函數需要傳遞大量的參數,顯然寄存器是不夠用的,那該怎麼解決哪?需要用到堆棧傳參的方式去解決。

【堆棧傳參的方式

堆棧傳參看到這幾個字肯定能想到將參數保存在堆棧中。那我們該怎麼用哪?同樣用例題介紹堆棧傳參的方式。

例:編寫一個函數,實現任意五個整數相加。

分析:需要將5個參數保存到堆棧中,用CALL指令調用函數。

第一步:在DTDebug.exe軟件中打開飛鴿軟件,如圖2-13-1所示。

2.13 堆棧傳參

第二步:將5個整數壓入堆棧,輸入以下指令,如圖2-13-2所示。

PUSH 1

PUSH 2

PUSH 3

PUSH 4

PUSH 5

2.13 堆棧傳參

第三步:依次按F8將參數壓入堆棧中,如圖2-13-3所示。

2.13 堆棧傳參

按F8執行完看到,堆棧中已經壓入我們傳遞的參數。用CALL調用函數,我們怎麼編寫這個函數哪?

我們的常用指令不允許兩邊都為內存,所以我們通過ESP棧頂指針來運算。

如果這麼寫:MOV EAX,DWORD PTR DS:[ESP],那麼EAX裡的值是什麼?

我們編寫完參數後,調用CALL,會將CALL下一行的指令地址傳入堆棧,這個時候ESP的值就是CALL函數地址,堆棧的變化如圖2-13-4:

2.13 堆棧傳參

圖2-13-4 堆棧圖

第三步:編寫函數,輸入以下指令,如圖2-13-5所示。

這5個參數依次是:ESP+0x14、ESP+0x10、ESP+0xC、ESP+0x8、ESP+0x4

所以我們的函數可以這樣編寫:

MOV EAX,DWORD PTR DS:[ESP+0x14]

ADD EAX,DWORD PTR DS:[ESP+0x10]

ADD EAX,DWORD PTR DS:[ESP+0xC]

ADD EAX,DWORD PTR DS:[ESP+0x8]

ADD EAX,DWORD PTR DS:[ESP+0x4]

RETN

2.13 堆棧傳參

第四步:使用CALL調用函數,輸入CALL 0x77068E4D,如圖2-13-6所示。

2.13 堆棧傳參

第五步:按F7觀察堆棧窗口數據變化,如圖2-13-7所示。

2.13 堆棧傳參

圖2-13-7中,執行完CALL指令,並將CALL指令下一行地址壓入堆棧中,當前黑色定位光標在函數體開始的地址,當前ESP是0x0019FFD8,堆棧窗口中的內存使用是從高地址向低地址使用的,所以ESP+14是參數的開始。我們可以雙擊堆棧窗口中的內存地址,如圖2-13-8所示。

2.13 堆棧傳參

第六步:把EAX寄存器的數據變為0x00000000,依次按F8,查看結果是否正確,如圖2-13-9所示。

2.13 堆棧傳參

1+2+3+4+5=15,轉化成16進製為F。看圖2-13-9中,EAX寄存器的值為0x00000000F,說明運算結果正確,證明我們編寫的函數是正確的。

第七步:按F8執行RETN指令,如圖2-13-10所示。

2.13 堆棧傳參

以上是堆棧傳參的過程,總結:堆棧傳參是將參數壓入堆棧中,函數在運算時通過ESP尋址的方式去查找對應的參數並進行運算。

那麼問題來了,我們的函數執行完了,可是我們的數據還保存在堆棧中,該怎麼解決呢?

下節我們解答這個問題。

下節介紹堆棧平衡。

練習:

1、用匯編編寫一個函數,功能是實現對任意10個整數的加法運算(不考慮溢出),

要求:

1、前兩個參數使用寄存器進程參數傳遞;

2、後8個參數使用堆棧進行傳遞。


分享到:


相關文章: