前言
國內大牛們在這方面的著作其實已經比較多了,但他們的重心多是教會大家逆向分析。我寫這系列的目的,則是為了通過反匯編來更好的理解C++中的一些概念。
文中第一次出現的匯編指令我都會簡單解釋一下,方便不熟悉匯編又想了解匯編的人查看。但是匯編的大多數指令都會涉及到直接尋址,間接尋址,寄存器間接尋址等等,詳細介紹這個就脫離本文的主題了,各位若想要更深入的說明,還請各位自己查看相關資料。
那就開始吧。
正文
傳遞常量
先來看看最簡單的參數傳遞是如何實現的。
void testInt(int i) { i = 10; } int main() { testInt(1); //傳遞常量 return 0; }
反匯編如下
主函數做了這些事
00A813CE push 1 00A813D0 call testInt (0A81082h) 00A813D5 add esp,4
匯編指令解釋:
push data data是四個字節的數據,這條指令將data壓入堆棧,然後堆棧指針ESP 減去 4
call addr addr是函數地址。這條指令會將當前指令的下一條指令的地址壓入堆棧。然後跳轉到地址為addr的指令處,繼續執行。
add ptr,data ptr指向的數據加上data後,存儲在ptr指向的空間
下面來看看testInt函數反匯編後是什麼樣的。注意:下面顯示的地址值可能和上面提到過的不同。這是因為每次重新調試程序的時候,函數的地址都會改變,請不要在意。
009B1380 push ebp 009B1381 mov ebp,esp 009B1383 sub esp,0C0h 009B1389 push ebx 009B138A push esi 009B138B push edi 009B138C lea edi,[ebp+FFFFFF40h] 009B1392 mov ecx,30h 009B1397 mov eax,0CCCCCCCCh 009B139C rep stos dword ptr es:[edi] 009B139E mov dword ptr [ebp+8],0Ah 009B13A5 pop edi 009B13A6 pop esi 009B13A7 pop ebx 009B13A8 mov esp,ebp 009B13AA pop ebp 009B13AB ret
匯編指令解釋:
mov ptr1,ptr2 將ptr2指向的數據存儲在ptr1指向的空間
sub ptr1,ptr2 ptr1指向的數據減去ptr2指向的數據,將結果存儲在ptr1指向的空間。
lea ptr,data 將data存儲在ptr指向的空間。注意是data,而不是data指向的數據。
rep stos ptr 這條指令是這樣執行的:重復判斷ecx是否為0。為0則結束該指令。否則將eax寄存器中的內容存儲在ptr指向的空間。每循環一次,ecx減1,如果設置了direction flag,edi減1,否則加1。通常ptr都是一個由edi組成的表達式,不然循環多次,把eax的值放在同一個地方,就沒意義了。
pop ptr 這條指令與push相對。先彈出棧頂的一個四字節數據,存儲在ptr指向的空間,然後esp加4。
ret 與call相對。彈出棧頂的一個四字節數據,存儲在EIP寄存器中。實現函數返回。
第1-10行 幾乎所有的函數體中都有,主要作用是保存ebp,ebx,esi,edi這四個寄存器的值,以便在本函數中可以隨意使用這四個寄存器,而不會影響調用函數。 為了堆棧平衡,esp寄存器的值也要保存。
第12-16行 恢復ebp,ebx,esi,edi,esp寄存器的值。
第11行 十幾行的指令只有這行是我們寫在函數內的i = 10;反匯編出來的結果。
ebp+8是什麼呢?
首先可以看到3-11行並未改變ebp的值。然後我們將第3行之前影響堆棧的指令按照執行順序給列出來。
push 1
call testInt
push ebp
mov ebp,esp
這4條指令執行完後堆棧是這樣的:
由上圖就可以知道esp+8是第一個參數的地址。第11行指令把0Ah存在了這個地址指向的空間,也就是執行了i = 10;
當然這系列操作毫無意義,函數返回後,除了參數所占其余棧空間全部釋放。函數返回後,又立即給esp加上4,好了,參數所占的棧空間也釋放了。
本文出自 “三人乘虎” 博客,請務必保留此出處http://darhx.blog.51cto.com/7920146/1304242