2017-01-06 18 views
0

我想了解一些拆解代碼和其中的數學。通過我的計算256 dec = 0x100十六進制。當我掩飾一個char buff[256],我看到它通過sub $0x108,%esp

這裏分配的是C代碼

void vuln(char* arg) { 
    char buf[256]; 
    strcpy(buf, arg); 
} 

這裏是拆卸說明書

push %ebp 
mov %esp, %ebp 
sub $0x108,%esp 
sub $0x8,%esp 
pushl 0x8(%ebp) 
lea -0x108(%ebp),%eax 
push %eax 
call 8048300 <[email protected]> 
add $0x10,%esp 
leave 
ret 

我也不會在年底瞭解add $0x10,%esp 。我假設我們正在從strcpy中刪除返回值,但爲什麼$0x10? strcpy返回一個char *,不應該是$0x8

+1

你忘了在啓用優化的編譯。否則,編譯器將生成垃圾代碼。請注意,您仍可能會看到更多的內存被分配,這通常是出於對齊的原因。 – Jester

回答

3

在本Agner Fog document on calling conventions指出,第5章:

的適用於32位Linux和Mac OS X的Gnu編譯器版本3.x及更高版本使堆棧 的指針在每個函數調用指令處對齊16。

所以,@Anty在他們的答案解釋的那樣,-mpreferred-stack-boundary的默認值是4,拿到2 對齊。

當檢查棧的對齊情況時,採用明確顯示堆棧指針不對齊的約定很有用。
我發明我自己的,即採用如下形式@ M + N(例如@ 16 + 4),並且意味着堆棧指針是在和地址Ñ字節多少(例如比16的倍數少4個字節,如0x000c,0x001c,0x002c等)。
這很有用,因爲堆棧向下增長,所以如果堆棧指針位於@ 16 + 4,那麼在push ebp之後位於@ 16 + 8等等(考慮到對齊的模塊化特性)。

在調用vuln之前,堆棧位於@ 16 + 0(由編譯器強制執行)。
剛剛被調用後,由於返回地址的隱式推送,它是@ 16 + 4

的序言是:

;Every annotation shows the alignment of the stack 
; AFTER the annotated instruction has executed 

push %ebp   ;@16+8 
mov %esp, %ebp 

現在,編譯器必須分配0x100字節的,但希望他們能夠在最佳排列分配,只是在做一個sub $0x108,%esp將導致堆棧指針在@ 16 + 8 + 0x100 = @ 16 + 0x108 === @ 16 + 8
要使它到達下一個16字節的邊界,需要八個字節,因此需要0x108的大小。

sub $0x108,%esp  ;@16+0, Aligned 

接下來它需要執行一個調用,在調用指令中堆棧需要重新對齊。
到目前爲止,但推後參數將不再是。
由於strcpy具有兩個32位的參數,在總共八個字節,則編譯器移動堆棧指針八個字節向下(8 + 8 = 16)尊重對準約束

sub $0x8,%esp    ;@16+8 

pushl 0x8(%ebp)    ;@16+12 
lea -0x108(%ebp),%eax 
push %eax     ;@16+0, Aligned 
call 8048300 <[email protected]> 

現在,C calling convention任務它是清理堆棧的調用者。
推送了兩個參數,因此需要add $0x8,%esp
加上另一個add $0x8,%esp來平衡上面的sub $0x8,%esp
這兩個組合成一個單一的指令。

add $0x10,%esp    ;@16+0, Aligned 

請注意,這與函數的返回值和類型無關。
這是推算這個數字的論據。

最後序言

leave       ;@16+8 
ret       ;@16+4 
+0

謝謝@瑪格麗特布魯姆,我懷疑它不得不與堆棧對齊,但是直到你一步一步地分解它才完全理解。這很棒! –

3

你看到的是堆棧對齊的效果 - 特別是編譯器選項-mpreferred-stack-boundary。在你的情況

-mpreferred-stack-boundary=4 

這意味着對齊到2^4 = 16

所以代碼編譯成

vuln(char*): 
     push ebp 
     mov  ebp, esp 
     sub  esp, 264 
     sub  esp, 8 
     push DWORD PTR [ebp+8] 
     lea  eax, [ebp-264] 
     push eax 
     call strcpy 
     add  esp, 16 
     nop 
     leave 
     ret 

如果將其更改爲

-mpreferred-stack-boundary=2 

它會對齊到4個字節(2^2),您將得到的代碼是

vuln(char*): 
     push ebp 
     mov  ebp, esp 
     sub  esp, 256 
     push DWORD PTR [ebp+8] 
     lea  eax, [ebp-256] 
     push eax 
     call strcpy 
     add  esp, 8 
     nop 
     leave 
     ret 

正如你在第一種情況下看到它是sub esp, 8add esp, 16(消耗多餘的8個字節),而在第二種情況下只是add esp, 8