https://www.52pojie.cn/thread-664189-1-1.html
無論是分析程式崩潰原因,還是解決程式hang問題,我們最常檢視的就是程式調用堆棧。學會windbg調用堆棧指令,以及了解堆棧中的各個參數的意義就顯得至關重要。
上圖就是一個典型的Windbg堆棧,如果不了解ChildEBP、RetAddr、Args to Child等參數意義,以及它們之間的來龍去脈,調試工作将很難進行下去。![]()
Windbg檢視調用堆棧(k*) 1. 函數參數 2. Windbg堆棧指令 3. 執行個體分析
1. 函數參數
函數的參數傳遞有二種方式:堆棧方式、寄存器方式。如果是堆棧方式傳遞的,就需要定義參數在堆棧中的傳遞順序,并約定函數被調用之後,由誰來平衡堆棧;如果是寄存器方式傳遞的,就需要确定參數存放在哪個寄存器中。每一種方式都有其優缺點,而且與使用的程式設計語言有關系,不存在哪種方式好與壞。
如Visual Studio中的C++工程,可以C++ --> 進階 --> 調用約定中進行設定:
常用的調用約定類型有__cdecl、stdcall、PASCAL、fastcall。除了fastcall可以支援以寄存器的方式來傳遞函數參數外,其他的都是通過堆棧的方式來傳遞函數參數的。
利用堆棧傳遞參數
堆棧是一種“後進先出”的資料結構,ESP寄存器始終指向棧頂。棧中資料位址從底部到頂部依次減小,也就是說,棧底對應高位址,棧頂對應低位址。
調用函數時,調用者依次把參數壓棧,然後調用函數,函數被調用之後,在堆棧中取得參數資料。函數調用結束以後,堆棧需要恢複到函數調用之前的樣子,具體由調用者來恢複還是由函數自身來恢複,根據不同的調用約定類型采用不同的方式。
約定類型 | __cdecl | stdcall | PASCAL | fastcall |
---|---|---|---|---|
參數傳遞順序 | 從右到左 | 從左到右 | 使用寄存器 | |
平衡堆棧者 | 調用者 | 函數自身 |
cdcel是C/C++/MFC程式預設的調用約定。
stdcall是Win32中絕大多數 API函數的約定方式,也有少部分使用cdcel約定方式,如wsprintf等。
在windows C/C++開發中常用的就是__cdecl和stdcall這2種調用約定。
假設調用函數
int add(int a, int b)
, 按照不同的調用約定來調用它。從調用者的視角來看,其彙編代碼分别表示如下:
push b ;參數按從右到左傳遞
push a
call add
add esp, 8 ;調用者在函數外部平衡堆棧
push b ;參數按從右到左傳遞
push a
call add ;函數自己内部平衡堆棧
在函數調用過程中,參數入棧的過程:
上圖中,EBP和函數傳回位址都是位址,在32位程式中位址占4個位元組。在函數的一次調用過程中EBP是不會變化的,函數調用完之後會将EBP恢複為暫存在堆棧中的原EBP值。是以,通過EBP可以擷取函數各個參數的值:
參數a = EBP + 0x8
參數b = EBP + 0xC
2. Windbg堆棧指令
2.1 顯示堆棧資訊k*
[~Thread] k[b|p|P|v] [c] [n] [f] [L] [M] [FrameCount]
[~Thread] k[b|p|P|v] [c] [n] [f] [L] [M] = BasePtr [FrameCount]
[~Thread] k[b|p|P|v] [c] [n] [f] [L] [M] = BasePtr StackPtr InstructionPtr
[~Thread] kd [WordCount]
參數:
Thread 指定顯示哪個線程的調用堆棧。如果省略該參數,則顯示目前線程的調用堆棧。*顯示所有線程的調用堆棧。
b 顯示每個函數的前3個參數。
p 顯示每個函數的所有參數。參數清單包括每個參數的類型、名稱、值。
如上圖,可以看到函數的每個參數的類型,名稱,值。但是這個需要有對應的符号檔案(pdb),沒有應用程式的符号檔案隻能顯示系統API的參數資訊。
P 類似p。不同之處在于,每個參數顯示在單獨的行上面。
n 顯示調用堆棧中每幀的序号(一般稱棧幀,如棧幀3)。
FrameCount 指定顯示調用堆棧的幀數,即調用堆棧的深度。預設為16進制格式。預設幀數為0x14=20
2.2 切換到指定幀資訊.frame
調用堆棧顯示出來之後,如果想知道調用某幀時的相關資訊,可以使用
.frame
來切換指定幀,然後就可以使用如dv指令顯示局部變量等。
.frame [/c] [/r] [FrameNumber]
/c
/r 顯示執行該幀時寄存器的值。
FrameNumber 指定要切換到的幀号。
3. 執行個體分析
kbn
顯示堆棧資訊:
棧幀12:
調用add函數,參數1=00000001,參數2=000000002,EBP=0015fc0c
根據圖1得知,函數傳回位址=EBP+4,我們使用dw指令來驗證。
參考:
《軟體調試》張銀奎 著
《格蠹彙編》張銀奎 著
《加密與解密》第三版 段剛編著
怎麼删除?