Posted on 2009-11-02 17:31
S.l.e!ep.¢% 閱讀(643)
評論(0) 編輯 收藏 引用 所屬分類:
WinDbg
在這一系列之前的兩篇文章中,我介紹了如何在windbg中查看調用棧的相關信息(詳見小覽call stack(調用棧)(一)),以及調用約定(詳見小覽call stack(調用棧) (二)——調用約定)。今天的這篇博客在二者的基礎之上,介紹如何使用調式器腳本程序來觀察調用棧。對CallStack感興趣的朋友可以在此基礎上開發更加詳盡的腳本來觀察CallStack的信息;對調試感興趣的朋友則可以看一下DScript的用處。
我們先來看一個例子,下面的程序并不是一個優美的程序片段,但是它能夠幫助我們說明問題。程序使用了一個簡單的遞歸,把1到參數d的和累加到sum之上。在main中,我們把d設為10,這樣,在斷點處,我們就能獲得一個深度為11的調用棧。
Code
#include
<
stdio.h
>
int
SumToOne(
int
d,
int
sum)
{
??? sum
+=
d;
???
if
(d
!=
?
1
)
??????? sum
=
SumToOne(d
-
1
, sum);
???
else
??????? sum
=
sum;
//
這條語句方便設置斷點
???
return
sum;
}
void
main()
{
???
int
sum
=
SumToOne(
10
,
0
);
??? printf(
"
sum=%d
"
, sum);
}
然后,在當前文件夾下,編輯調試器腳本文件DumpStack.txt,內容如下
.
printf
"
Dump %d frames\n
"
,
?
$
{
$
arg1}
r
$
t1
=
@ebp
;
.
for
?
(
r
$
t0
=
1
;
?
$
t0
<=$
{
$
arg1}
;
r
$
t0
=$
t0
+
1
)
{
???
.
printf
"
frame %d, d=%d sum=%d\n
"
,
?
$
t0
,
poi
($
t1
+
8
),
poi
($
t1
+
c
)
??? r
$
t1
=
poi
($
t1
)
}
在windbg中,運行程序,當程序停止在斷點處時,執行腳本
$$>a< “dumpStack.txt”a
如下圖所示
我們看到了10個frame以及它的參數信息。
現在,對這個調試腳本稍加解釋,稍顯來看看腳本的語法:
- 調試腳本的調用方法,windbg的語法是$$>a< “腳本文件名”參數。其中$$>a<中的a示意運行腳本的時候傳入參數(argument)
- 調試腳本的參數:在調試腳本中,用${$argi}來引用第i個參數。由于windbg默認16進制數,所以我們在調用這個參數的時候,用了a($$>a< "dumpStack.txt" a)
- 腳本變量的賦值和引用:這里使用了windbg別名(alias)的語法,大家可以把別名類比成c中的宏。在賦值的時候,用r $別名= 的格式,引用的時候,使用$別名
- 取值操作:c中的*p操作在windbg中,要用poi(p),原因是因為windbg默認支持MSAM語法。
- 控制語句:.for語句的使用和任何一種語言的for語句思想一樣,不再多述
- 輸出語句:.printf和c中的printf也基本相似,這里也不多述
了解了語法之后,來看看算法:
- 腳本通過poi($t1+8), poi($t1+c)來顯示每個frame中d和sum的值,這里$t1代表了每個frame中ebp的值,所以簡單的說,就是把每個frame中ebp+8,ebp+c的值輸出。在介紹調用約定的博客中,我講述了這個偏移量的由來,在這里重溫一下。由于函數SumToOne是stdcall,壓棧順序從右往左,如下表所示ebp指向存儲前一個ebo的位置,所以d的位置在ebp+8,sum在ebp+c
-
前往下一個frame,只需要把棧上ebp位置的值取出,作為新的ebp就可以了。因為基本上每一個程序在進行棧操作之前都會備份老的ebp(push ebp),然后把當前的esp作為新的ebp(mov ebp, esp)
總結一下,今天這篇博文作為這個系列的結束,通過一個調式器腳本,復習了之前講述的調用棧的相關概念。同時也展示了調試器腳本的相關語法。