2012-10-22 110 views
9

文章可以找到here粉碎堆棧example3.c混淆

我正在閱讀粉碎堆棧,發現自己被卡在example3.c上。

0x80004a3 <main+19>: call 0x8000470 <function> 
0x80004a8 <main+24>: addl $0xc,%esp 
0x80004ab <main+27>: movl $0x1,0xfffffffc(%ebp) 
0x80004b2 <main+34>: movl 0xfffffffc(%ebp),%eax 

作者指出,我們要跳過從0x80004a80x80004b2,而這跳是8個字節;作者如何確定這是8字節?我已經重新創建的代碼,把它通過objdump,發現它不是8個字節(我是一個64位計算機上,但我確信使用的是32位編譯):

8048452: e8 b5 ff ff ff   call 804840c <function> 
8048457: c7 44 24 1c 01 00 00 movl $0x1,0x1c(%esp) 
804845e: 00 
804845f: 8b 44 24 1c    mov 0x1c(%esp),%eax 
8048463: 89 44 24 04    mov %eax,0x4(%esp) 
8048467: c7 04 24 18 85 04 08 movl $0x8048518,(%esp) 

筆者也說「 我們如何知道要在返回地址中添加8?我們首先使用了 測試值(例如1)「他在哪裏使用此測試值?

+0

由用戶請求移動到StackOverflow的。 (+1,很好的問題。) –

+0

如果你減去地址就像在那篇文章中,你會得到10個字節:)檢查我的答案結束時的鏈接,偏移量是錯誤的。 – iabdalkader

+1

有一個回答說它現在已經被修改了。這是最新的[修訂](https://avicoder.me/2016/02/01/smashsatck-revived/) – NathanOliver

回答

-1

經過很多時間和思考,我終於解決了這個問題。幫助我解決這個問題的資源是Stack smashing code not working on Linux kernel 2.6.38.7... Please help

幫助我解決這個問題的最大變化是使用disassembly-flavor intelgdb

反彙編代碼(僅供參考):

0804840c <function>: 
804840c: 55      push ebp 
804840d: 89 e5     mov ebp,esp 
804840f: 83 ec 10    sub esp,0x10 
8048412: 8d 45 f7    lea eax,[ebp-0x9] 
8048415: 83 c0 14    add eax,0x14 
8048418: 89 45 fc    mov DWORD PTR [ebp-0x4],eax 
804841b: 8b 45 fc    mov eax,DWORD PTR [ebp-0x4] 
804841e: 8b 00     mov eax,DWORD PTR [eax] 
8048420: 8d 50 05    lea edx,[eax+0x5] 
8048423: 8b 45 fc    mov eax,DWORD PTR [ebp-0x4] 
8048426: 89 10     mov DWORD PTR [eax],edx 
8048428: c9      leave 
8048429: c3      ret  

0804842a <main>: 
804842a: 55      push ebp 
804842b: 89 e5     mov ebp,esp 
804842d: 83 e4 f0    and esp,0xfffffff0 
8048430: 83 ec 20    sub esp,0x20 
8048433: c7 44 24 1c 00 00 00 mov DWORD PTR [esp+0x1c],0x0 
804843a: 00 
804843b: c7 44 24 08 03 00 00 mov DWORD PTR [esp+0x8],0x3 
8048442: 00 
8048443: c7 44 24 04 02 00 00 mov DWORD PTR [esp+0x4],0x2 
804844a: 00 
804844b: c7 04 24 01 00 00 00 mov DWORD PTR [esp],0x1 
8048452: e8 b5 ff ff ff   call 804840c <function> 
8048457: c7 44 24 1c 01 00 00 mov DWORD PTR [esp+0x1c],0x1 
804845e: 00 
804845f: 8b 44 24 1c    mov eax,DWORD PTR [esp+0x1c] 
8048463: 89 44 24 04    mov DWORD PTR [esp+0x4],eax 
8048467: c7 04 24 18 85 04 08 mov DWORD PTR [esp],0x8048518 
804846e: e8 7d fe ff ff   call 80482f0 <[email protected]> 
8048473: c9      leave 
8048474: c3      ret  
8048475: 66 90     xchg ax,ax 
8048477: 66 90     xchg ax,ax 
8048479: 66 90     xchg ax,ax 
804847b: 66 90     xchg ax,ax 
804847d: 66 90     xchg ax,ax 
804847f: 90      nop 

有兩個問題,我的理解這個:

A)我的第一個問題是找到的字節超限retfunction量。再次;我這樣做是通過使用英特爾的語法拆卸和發現:

要在內存中的正確空間ret需要被設置爲EIP的函數被調用時設置ret8048412的地址空間向下移動堆棧0x9。因爲這是32位的代碼;要到達ret,我們然後爲字大小增加一個額外的0x4字節。要得到ret這意味着ret設置爲0x9 + 0x4,它是十進制的13。

這解決了第一個到ret的問題。

B)第二個問題是跳過0x8048457內存位置。這通過向(*ret)添加7個字節來完成,這使得程序跳過並且在0x804845e處執行,其爲00NUL)。這只是低效的;所以我添加了額外的字節並在堆棧中執行了8個字節;從而導致x = 0;

我發現字節的確切數量是8(c7 44 24 1c 01 00 00是7個字節)並且00是一個字節。這解決了我最後的問題。

我的改性的C代碼:

void function(int a, int b, int c) { 
     char buffer1[5]; 
     int *ret; 

     ret = buffer1 + 13; //tried 11, 14, 20, 40, 38, 43 
     (*ret) += 8; // tried 5, 8, 12; 8 is correct value! 
} 

void main() { 
     int x; 
     x = 0; 
     function(1,2,3); 
     x = 1; 
     printf("%d\n", x); 
} 
+0

你意識到這只是解決了你的機器上的問題,並沒有解釋爲什麼位移是8字節在文章中?這是你的問題。 – iabdalkader

+0

如果你認爲它回答你的問題,你應該接受一個答案:)當然這取決於你,我只是指出了這一點。 – iabdalkader

+0

但你自己的回答並不回答這個問題,這就是爲什麼文章中的位移是8字節,這是一個錯誤的位移,無論如何,無論如何,無論幫助你理解得更好。 – iabdalkader

3

這不是我如何解讀文章。按照我的理解,他希望修改返回地址,以便跳過x = 1;分配,即他希望function返回到將執行printf的位置。如您在反彙編中所看到的,賦值爲8個字節(c7 44 24 1c 01 00 00 00),因此向前移動返回地址8個字節將使其移過該指令。至於「我們先使用了一個測試值」的評論。也許他只是意味着他在反彙編器中查看代碼來計算長度,或者他嘗試了不同的偏移量(?)。

1

文章中的位移是錯誤的,應該是10字節。當一個函數被調用(或執行跳躍),返回地址被設定爲等於指令指針+當前指令大小:

ret = IP + Curr_Inst_size 

所以,當調用該函數返回時,指令指針應等於0x80004a80x80004a3 +調用指令大小):

0x80004a3 <main+19>: call 0x8000470 <function> 
--> 0x80004a8 <main+24>: addl $0xc,%esp    
    0x80004ab <main+27>: movl $0x1,0xfffffffc(%ebp) 
    0x80004b2 <main+34>: movl 0xfffffffc(%ebp),%eax 

但是,您希望將指令指針設置爲0x80004b2相反,跳過分配,也不可避免地要跳到另一個指令(addl $0xc,%esp)噸Ø那裏,或者換句話說,你需要添加(0x80004b2-0x80004a8)字節或10個字節的指令指針跳過這兩個指令:

0x80004a3 <main+19>: call 0x8000470 <function> 
    0x80004a8 <main+24>: addl $0xc,%esp    
    0x80004ab <main+27>: movl $0x1,0xfffffffc(%ebp) 
--> 0x80004b2 <main+34>: movl 0xfffffffc(%ebp),%eax 

的實際指令大小取決於操作數,機器類型等。但在本例中,addl長度爲3個字節,而movl長度爲7個字節。你可以檢查x86 Instruction Set Reference的確切指令大小,或者你可以編譯和反彙編的代碼,你會看到,這兩個指令是10個字節長:

int main() 
{ 
    asm("addl $0xc,%esp\n\ 
     movl $0x1,0xfffffffc(%ebp)"); 

} 

GDB:

0x08048397 <+3>: 83 c4 0c    add $0xc,%esp 
0x0804839a <+6>: c7 45 fc 01 00 00 00 movl $0x1,-0x4(%ebp) 

還有的還關於示例3中完全相同的問題的討論herehere