程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 小覽CallStack(調用棧)(三)-用調試器腳本查看調用棧信息

小覽CallStack(調用棧)(三)-用調試器腳本查看調用棧信息

編輯:關於C++

在這一系列之前的兩篇文章中,我介紹了如何在windbg中查看調用棧的相關 信息(詳見小覽call stack(調用棧)(一)),以及調用約定(詳見小覽call stack(調用棧) (二)——調用約定)。今天的這篇博客在二者的基礎 之上,介紹如何使用調式器腳本程序來觀察調用棧。對CallStack感興趣的朋友 可以在此基礎上開發更加詳盡的腳本來觀察CallStack的信息;對調試感興趣的 朋友則可以看一下DScript的用處。

我們先來看一個例子,下面的程序並不是一個優美的程序片段,但是它能夠 幫助我們說明問題。程序使用了一個簡單的遞歸,把1到參數d的和累加到sum之 上。在main中,我們把d設為10,這樣,在斷點處,我們就能獲得一個深度為11 的調用棧。

#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 eip d sum ebp指向存儲前一個ebo的位置,所以d的位置在ebp+8,sum在ebp+c

前往下一個frame,只需要把棧上ebp位置的值取出,作為新的ebp就可以了。 因為基本上每一個程序在進行棧操作之前都會備份老的ebp(push ebp),然後把 當前的esp作為新的ebp(mov ebp, esp)

總結一下,今天這篇博文作為這個系列的結束,通過一個調式器腳本,復習 了之前講述的調用棧的相關概念。同時也展示了調試器腳本的相關語法。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved