簡體   English   中英

程序集中的保留堆棧空間似乎與C代碼不匹配

[英]Reserved stack space in assembly doesn' t seem to match the c code

我有以下代碼示例:

int main(int argc, char **argv) {
    char *name[2];
    name[0] = "/bin/sh";
    name[1] = NULL;
    execve(name[0], name, NULL);
    exit(0);
}

拆卸程序會導致類似於以下內容:

1.  0x08048250 <main+0>: push %ebp
2.  0x08048251 <main+1>: mov %esp,%ebp
3.  0x08048253 <main+3>: and $0xfffffff0,%esp
4.  0x08048256 <main+6>: sub $0x20,%esp
5.  0x08048259 <main+9>: movl $0x80a6f68,0x18(%esp)
6.  0x08048261 <main+17>: movl $0x0,0x1c(%esp)
7.  0x08048269 <main+25>: mov 0x18(%esp),%eax
8.  0x0804826d <main+29>: movl $0x0,0x8(%esp)
9.  0x08048275 <main+37>: lea 0x18(%esp),%edx
10. 0x08048279 <main+41>: mov %edx,0x4(%esp)
11. 0x0804827d <main+45>: mov %eax,(%esp)
12. 0x08048280 <main+48>: call 0x804f5c0 <execve>
13. 0x08048285 <main+53>: movl $0x0,(%esp)
14. 0x0804828c <main+60>: call 0x8048af0 <exit>

我試圖了解程序集:

在第4行,減少了stackpointer以便為局部變量分配空間,但是我不明白為什么它保留32個字節(0x20 = 32個字節)? 據我了解,它只需要分配:

  • 指針“名稱”的4個字節
  • 字符串“ / bin / sh”為8個字節;
  • NULL是否還會占用空間? 如果是這樣,它相當於4個字節的0x0對嗎?
  • argc和argv不是這個stackframe的一部分嗎? 它們由主電源調用者推動,因此無需在此堆棧幀中保留。

我也看到一些數據存儲在與堆棧指針偏移的位置,但似乎並沒有使用所有空間。

有人可以解釋一下這個組裝嗎? 我在將C代碼映射到給定的匯編指令時遇到麻煩。 特別是由於保留空間的長度似乎與c代碼不匹配。

我對以下內容特別感興趣:

  • 第3行:這是做什么用的?
  • 第9行:這是做什么的?
  • 第11和13行:(%esp)語法是什么意思?

謝謝!

關於“它只需要分配”:

  • “ 4個字節的指針名稱”-不太正確。 name是一個由兩個指針組成的數組,因此2 * 4 = 8個字節(假設您使用的是32位指針,那是它的樣子)。

  • “字符串“ / bin / sh”為8個字節-不會在堆棧上。它將在二進制文件的其他位置(可能是.rodata段,即只讀數據),因此不會占用任何堆棧空間。

  • “ NULL是否還會占用空間?” -NULL(可能是,除非您有一個錯誤的C編譯器)的值為0。“ value”本身不能占用堆棧空間,但是如果堆棧上有一個值為NULL的變量,則您會發現零。堆棧。

  • “ argc和argv [etc]”-可以說是這些函數調用的堆棧框架的一部分,但是它們是由調用者初始化的,因此不能通過減少%esp來保留。

“似乎並沒有使用所有空間”-由於對齊,通常是正確的。 考慮:

struct {
    char ch;
    int *ptr;
};

對於這種結構情況,我們將有一個字節的char,然后是三個字節的padding,以便正確對齊ptr,然后是四個字節的ptr。 (如果指針是64位,則填充7個字節。)如果在堆棧上分配這些結構之一,則將有3個(或7個)“未使用”的堆棧字節。

但是,在這種情況下,編譯器在堆棧上為初始“ sub ...%esp”指令中的execve()調用的參數創建了空間,這是一個很小的優化。

第3行:對齊:在16字節邊界上對齊%esp。 (對於任何人都足夠。)第4行:創建一些堆棧空間。 其中一些是局部變量。 當將函數調用放在一起時,編譯器會將它的其他一些字節用於臨時空間。

第9行:(有效地)將字符串“ / bin / sh”放入%edx。 該字符串的地址位於二進制文件中的0x80a6f68位置(將.rodata加載到的位置)。 第5行將值0x80a6f68放入(%esp + 0x18)。 第9行將*(%esp + 0x18),即*(char **)0x80a6f68放入%edx。

第11行:(%esp)實際上是指針引用:* esp = eax。 它將eax寄存器的值放入* esp,即堆棧指針正上方的四個字節。 eax的值在第7行中定義。

第13行:將* esp設置為0x00000000。 由於我們隨后調用exit(),因此將exit的第一個參數(即* esp)設置為0。

name是2個指針的數組。 這將占用8或16個字節,具體取決於您的平台是32位還是64位。

字符串/bin/sh完全不在堆上,而是在已初始化的數據段中; 為了完整起見, \\0確實需要空間。

argcargv將作為參數傳遞到堆棧中。

第3行將堆棧指針對齊為16個字節的倍數

第19行實際上是說“讓edx為0x18加上esp的值”; lea是“加載有效地址”; 認為它是一個mov ,而不是執行內存加載,而是返回將從其加載內存的地址。

上線11和13中, (esp)是指該位置的內容通過指向esp

我承認我的匯編器有點生銹。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM