我們先來(lái)看一個(gè)例子,下面的程序并不是一個(gè)優(yōu)美的程序片段,但是它能夠幫助我們說(shuō)明問(wèn)題。程序使用了一個(gè)簡(jiǎn)單的遞歸,把1到參數(shù)d的和累加到sum之上。在main中,我們把d設(shè)為10,這樣,在斷點(diǎn)處,我們就能獲得一個(gè)深度為11的調(diào)用棧。
#include
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 framesn", ${$arg1}
r $t1=@ebp;
.for (r $t0=1; $t0<=${$arg1}; r $t0=$t0+1)
{
.printf "frame %d, d=%d sum=%dn", $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)看看算法:
腳本通過(guò)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,壓棧順序從右往左,如下表所示
前一個(gè)ebp
eip
d
sum
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é)束,通過(guò)一個(gè)調(diào)式器腳本,復(fù)習(xí)了之前講述的調(diào)用棧的相關(guān)概念。同時(shí)也展示了調(diào)試器腳本的相關(guān)語(yǔ)法。
#include
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 framesn", ${$arg1}
r $t1=@ebp;
.for (r $t0=1; $t0<=${$arg1}; r $t0=$t0+1)
{
.printf "frame %d, d=%d sum=%dn", $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)看看算法:
腳本通過(guò)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,壓棧順序從右往左,如下表所示
前一個(gè)ebp
eip
d
sum
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é)束,通過(guò)一個(gè)調(diào)式器腳本,復(fù)習(xí)了之前講述的調(diào)用棧的相關(guān)概念。同時(shí)也展示了調(diào)試器腳本的相關(guān)語(yǔ)法。

