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