0x00:寫在前面
對很多初學者來說,入門逆向很困難,很多坐高地需要去攀登,那么堆棧對於很多人來說就是一座高地,如何去征服堆棧?畫堆棧圖是一種很好的理解堆棧的方式。
0x01:介紹堆棧
堆棧都是一種數據項按序排列的數據結構,只能在一端(稱為棧頂(top))對數據項進行插入和刪除。
要點:堆:順序隨意 棧:后進先出(Last-In/First-Out)
1、棧區(stack)— 由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似於數據結構中的棧。
2、堆區(heap) — 一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收 。注意它與數據結構中的堆是兩回事,分配方式倒是類似於鏈表
0x02:開始着手
准備:OD(吾愛破解專用OD)
畫圖工具 excel
程序:Helloworld.exe(鏈接:https://pan.baidu.com/s/130job_JfoQLEWQxKn0SMYQ 密碼:71xq)
這里我們OD加載程序以后F2加斷點在00401168處。
這里的00401168就是Helloworld.exe的其中一個函數入口點
當程序斷在此處時,注意寄存器窗口EIP:00401168,那么此時程序的下一跳就是此位了。
觀察寄存器窗口的ESP,EBP
那么初始的堆棧圖為下圖
一步一步F8往下執行
首先看00401168位置處的push 0x2 (壓棧 0x2(16進制))
因為兩個push是一個類型的匯編指令。放一起寫~
往堆棧里壓入0x2和0x1(注意均是16進制),那么入了兩次棧 ESP(棧頂-8)=0012FF2C,EBP(棧底無變化),2和1被壓入堆棧待使用。
接下來到call HelloWor.0040100A這個函數了
那么直接F7進入call中
此時堆棧變化 ESP=0012ff28
可以看到是到了jmp指令處,jmp(直接跳轉,無條件),直接回車跟隨jmp后的地址
00401040就是jmp后的地址,也是我們下一步執行的命令 push ebp
把EBP壓入棧中,此時ESP-4
接着F8執行mov ebp,esp
把esp(棧頂的值賦給ebp),此時ESP和EBP相等
再順序執行sub esp,0x40,esp-40(換算為16進制就是64,那么棧頂向下移動16個空格(堆棧由下向上是遞減的)一格4個字節(32位))
F8單步過至push ebx,push esi,push edi
把ebx,esi,edi的值壓入堆棧,相當於把這三個寄存器的值放到堆棧進行保存
lea edi,dword ptr ss:[ebp-0x40] :意思是把edp-0x40的地址值保存到edi中
EDI=0012FEE4
接下來到了循環處,ecx(循環寄存器)賦值0x10,eax賦值0xcccccccc,rep循環10次,且edi的地址每次循環+4,eax賦給其代表值
堆棧圖為下圖(VC會將未初始化的棧內存上的指針全部填成 0xcccccccc,翻譯中文就是燙燙燙燙燙燙燙燙。。。
接下來執行
mov eax,dword ptr ss:[ebp+0x8] 將ebp+0x8的值賦給eax
add eax,dword ptr ss:[ebp+0xc] 將ebp+0xc的值和eax進行add相加
那么此時的eax=00000003堆棧圖如下
接着執行pop edi,pop esi,pop ebx。讓保存的3個寄存器值取出來恢復。
mov esp,ebp將ebp值賦給esp
pop ebp(將棧頂出棧賦給ebp)
最后函數最后retn進行返回,retn轉換為匯編代碼就是 pop esi
最后一步 add esp,0x8
進行對比可知,一次函數執行的前后,堆棧是最終恢復成函數執行初始的樣子,對比第一步和最后一步ESP,EBP即可知。
那么經過堆棧圖的制作,我們通過過程得知了這個函數功能很簡單,就是傳參然后實現相加並返回結果。
0x04:總結
堆棧在逆向,程序開發中都尤為重要,知道代碼運行過程中堆棧的變化會讓逆向清晰起來。
一個函數的調用前和return結束過程后,堆棧不會發生變化,遵循堆棧平衡
cc對應着int 3調試中斷,堆棧中的存放的局部數據一般情況下是只讀的,當發生意外執行堆棧里面的數據就會引發該調試中斷,可以把0xccccccc理解為占位符,防止堆棧空白導致程序出錯