2014-07-10 81 views
2

以下代碼包含一個8字節的緩衝區。爲堆棧上的靜態變量分配空間

void vuln() { 
    char buffer[8]; 
    gets(buffer); 
    printf("%s",buffer); 
} 

int main() { 
    vuln(); 
    return 0; 
} 

所以,只有8個字節堆棧有望被保留用於此緩衝液中。但反彙編顯示保留16個字節。

(gdb) 
Dump of assembler code for function vuln: 
    0x000000000040057d <+0>:  push %rbp 
    0x000000000040057e <+1>:  mov %rsp,%rbp 
    0x0000000000400581 <+4>:  sub $0x10,%rsp 
    0x0000000000400585 <+8>:  lea -0x10(%rbp),%rax 
    0x0000000000400589 <+12>: mov %rax,%rdi 
    0x000000000040058c <+15>: callq 0x400480 <[email protected]> 
    0x0000000000400591 <+20>: lea -0x10(%rbp),%rax 
    0x0000000000400595 <+24>: mov %rax,%rsi 
    0x0000000000400598 <+27>: mov $0x400644,%edi 
    0x000000000040059d <+32>: mov $0x0,%eax 
    0x00000000004005a2 <+37>: callq 0x400450 <[email protected]> 
    0x00000000004005a7 <+42>: leaveq 
    0x00000000004005a8 <+43>: retq 
End of assembler dump. 

某些操作將根據自動化腳本中堆棧緩衝區的預期大小執行。但是這削弱了劇本。我可否知道爲什麼16字節被分配給緩衝區,以便我可以將它合併到腳本中?

+0

我用GNU C -O0編譯它,並且在內存中分配了16個字節;與-O6它增長到32個字節。請參閱下面的答案。 – rslemos

+0

您可以添加哪些操作是「基於堆棧上的緩衝區的預期大小執行的」?也許對整個問題有一個答案。 – rslemos

回答

2

x86-64 ELF psABI需要堆棧指針是對準:3.2.2節(「堆棧幀」)說

...輸入參數區域的結尾應在16對齊字節邊界。 換句話說,當控制爲 時,值(%rsp - &8; 8)始終是16的倍數,轉移到函數入口點。堆棧指針%rsp始終指向 最新分配的堆棧幀的末尾。

你的函數在堆棧上分配8個字節,然後調用一個參數函數gets;該函數的一個參數被傳遞到寄存器中,所以爲了保持ABI要求,編譯器必須在進行函數調用之前將堆棧指針向下移動另外的8個字節。

+0

因此,在(%rsp - 8)中,值8取決於什麼? 當我使用[45字節緩衝區](http:// pastebin。com/WndbQcLn),編譯器在堆棧中分配** 48字節**。我無法找到它的數學。 如果我按照我原來的計算結果,48字節是完美的,因爲內存分配在單詞中(4字節),而48是12個單詞。但是在這種情況下** ABI要求**呢? –

+0

8是一個常數。 – zwol

+0

在那個例子中,在進入函數時,堆棧指針對於某些未知的k值有'(k * 16 - 8)'。在'push%rbp'之後,它的值是'(k-1)* 16';在子$ 0x30,%rsp後面,它的值是'(k-4)* 16';然後'調用gets @ plt'隱式地執行'push%rip',在進入'gets'時使堆棧指針'(k-4)* 16 - 8'。這有助於你理解它是如何工作的嗎? – zwol

0

你無法控制編譯器在堆棧上做什麼。您也無法控制堆棧中變量的順序。編譯器還允許引入填充以便更好地對齊內存中的內容(儘管不在內部數組中:這是嚴格禁止的)。在某些情況下,它甚至不會在堆棧上分配變量,而是在寄存器中分配。

如果您對裝配級有特殊要求,請在裝配中進行。

1

作爲@Zack在他的回答中,x86-64 ABI需要16字節的堆棧對齊方式。如果您在x86或x86_64上使用gcc默認情況下堆棧是16字節對齊的。

gcc文檔:

-mpreferred堆疊邊界= NUM​​ 試圖保持對準,以凸起爲num字節邊界的2堆棧邊界。如果未指定-mpreferred-stack-boundary,則缺省值爲4(16字節或128位)。

https://gcc.gnu.org/onlinedocs/gcc/i386-and-x86-64-Options.html

進一步參見文檔,如果SSE被禁用gcc可以對齊堆疊到8個字節(以及因此違反ABI要求)。