為了加深對call、retn的了解,我今天用delphi寫了一個小程式,在OD中跟蹤程式執行call及retn時堆棧的變化。
程式很簡單:
procedure summ(b: string);
var
a: string;
begin
a := b;
end;
procedure TForm2.Button3Click(Sender: TObject);
begin
summ('bbbbbbbb');
end;
對應的彙編代碼:
004A4D28 . B8 3C4D4A00 mov eax, 004A4D3C ; ASCII "bbbbbbbb"
004A4D2D . E8 9EFFFFFF call 004A4CD0
004A4D32 . C3 retn
004A4CD0 /$ 55 push ebp
...........
004A4D21 . 59 pop ecx
004A4D22 . 59 pop ecx
004A4D23 . 5D pop ebp
004A4D24 . C3 retn
執行到004A4D2D,esp=0013F630,F7進去後到達004A4CD0 也就是summ的第一句代碼,esp=0013F62C,也就是說執行call xx系統進行了一個壓棧操作!執行到 004A4D24時,esp=0013F62C(和執行到第一句代碼時相等),執行了retn後,esp=0013F630,也就是說retn進行了一個出棧的動作!
另外,函數執行完畢的前後,不管是stdcall還是fastcall,都不保證寄存器不被修改!
總結:
1、執行到函數内的第一個語句時的esp值和到最後一個語句(也就是retn)的esp值是一樣的:
004A4CD0 /$ 55 push ebp ;esp=0013F628
004A4CD1 |. 8BEC mov ebp, esp
004A4CD3 |. 5D pop ebp
004A4CD4 /. C2 0400 retn 4 ;esp=0013F628
2、retn 4表示pop 2次。
3、執行到call xx時的esp值與執行完xx時的esp值不一定相等,對stdcall的尤其是這樣。
粗淺分析,不當之處還請指出。