安全圈 | 专注于最新网络信息安全讯息新闻

首页

開放安全研究:windbg入門

作者 recor 时间 2020-03-02
all

布拉德·安東尼維奇。

在這一系列的博客文章中,我們已經介紹了如何安裝WinDBG,並通過附加到行程和設定中斷點來開始。我們的下一步是實際的調試部分,我們將逐步通過一個程式並查看記憶體。

實際上,使用調試器的全部原因是在特定操作或函數期間檢查行程的狀態。幾乎每一條被執行的指令都會以某種管道改變程式的狀態,這意味著有能力執行一條指令然後檢查狀態是非常重要的。第一部分是“步進”-執行指令,然後暫停。WinDBG提供了許多不同的步進命令,這取決於您在程式中的位置和您想去的地方。

大多數調試器使用以下術語描述如何瀏覽程式及其功能:

call call call call ret

這裡需要注意的是,Step-Into和Step-Over都將執行一條指令,而pause-behavior只在到達調用指令時才發生變化。

call

去吧

g(Go)命令更像是一個中斷點命令,但它的功能模糊了中斷點和單步執行命令之間的界限。它用於恢復程式的執行,但與大多數步進命令不同,它並不是真正意義上的逐條指令使用。g將繼續程式,直到出現中斷點或异常。實際上,您可以使用g來執行所有的指令直到一個中斷點,而使用stepping命令執行指令時不設定中斷點。但是,要澄清的是,調試器在遇到中斷點時將暫停,而不管您是使用單步執行命令還是g之類的命令。

g g g g

g很容易使用:

g

當程式運行時,WinDBG將在命令輸入框中給您一條消息:

如果您知道要執行的地址,請將其作為參數提供給g:

g

單步走

執行一條指令,然後暫停稱為單步執行。這可以通過使用“Step Into”或“Step Over”命令來實現,因為這兩個命令在非調用指令上的行為相同。與其在這裡同時顯示它們,不如讓我們單獨看一下這些命令。

call

踏入

要使用WinDBG,請使用t(Trace)命令。每個步驟都會顯示寄存器的狀態和將要執行的指令。在這個例子中,我們將暫停在程式的入口點(記事本!WinMainCRTStartup)並查看要執行的前幾個指令(u eip)。第一條指令是調用記事本!__安全初始化cookie函數。讓我們看看調試器在使用t時的行為:

t notepad!WinMainCRTStartup u eip call notepad!__security_init_cookie t

在這裡我們可以看到我們在記事本內運行!WinMainCRTStartup,然後在通話時我們用t跟踪通話進入記事本!__security_init_cookie函數,在第一條指令上暫停。

notepad!WinMainCRTStartup call t notepad!__security_init_cookie

跨過

WinDBG使用p命令跳過函數調用。這意味著調用和被調用函數中的所有子指令將被執行,程式將暫停當前函數中的下一條指令(例如記事本!WinMainCRTStartup)。讓我們看看相同的場景,但這次我們將使用p:

p call notepad!WinMainCRTStartup p

在這裡我們可以看到,指令調用後的記事本!__security_init_cookie是push 58h。當我們使用p時,我們會自動執行記事本中的所有內容!__security_init_cookie函數,然後在其後面的推送處暫停。

call notepad!__security_init_cookie push 58h p notepad!__security_init_cookie push

走出去

使用WinDBG跳出可以通過gu(Go Up)命令實現。此命令掃描當前函數以獲取ret,然後在執行後暫停。這是一個重要的行為,因為無論出於什麼原因,如果函數沒有以ret結尾,或者程式碼路徑沒有導致ret,那麼gu可能會出現意外的結果。讓我們看看它是什麼樣子:

gu ret ret gu

我們在記事本上停下來了!WinMainCRTStartup+0x1d,這是對記事本的調用!_小鬼獲得創業資訊。我們可以看到(u eip L2)調用後的指令是mov dword ptr[ebp-4],0FFFFFFFEh。所以我們將單步(t)進入函數,在第一條指令處暫停。現在我們使用gu執行子函數中的所有指令和函數調用,然後暫停父函數中的下一條指令,即mov dword ptr[ebp-4],0FFFFFFFEh

notepad!WinMainCRTStartup+0x1d call notepad!_imp__GetStartupInfoA u eip L2 call mov dword ptr [ebp-4],0FFFFFFFEh t gu mov dword ptr [ebp-4],0FFFFFFFEh

執行到返回

gu是很好的,但有時您希望在函數返回之前查看堆棧,在這個場景中,您需要使用tt(Trace to Next Return)或pt。兩者都很容易調用:

gu tt pt

這裡要記住的重要一點是,tt將在下一次返回時停止,即使它不在當前函數中。例如,考慮下麵的偽代碼,我們的目標是在func中暫停ret:

tt ret func

在這個例子中,如果pause at call somefunc,然後使用tt,我們將在someotherfunc中的ret處結束pause。

call somefunc tt ret someotherfunc

對於這個場景,一個更好的方法可能是使用pt:使用相同的偽代碼,如果我們暫停調用somefunc,然後使用pt,我們將執行somefunc中的所有程式碼(然後是somefunc),然後暫停在func中的ret。實際上,對於這個例子,我們可以使用p,但這並不能說明問題:)

pt call somefunc pt somefunc someotherfunc ret func p

最終,這取決於作為使用調試器的人,您想要做什麼。

現在我們終於可以進入調試的最重要部分:檢查記憶體。WinDBG為此提供d(顯示記憶體)命令。以最簡單的形式運行它:

d

但這或多或少是沒用的。第一次單獨運行d將輸出eip指向的記憶體。這是無用的,因為eip應該指向一個程式碼段,為了理解這一點,您需要使用u(Unassemble)命令。所以一個更好的啟動命令是:

d eip eip u

這將顯示堆棧上的值。對於d,WinDBG將使用最後執行的d命令指定的格式顯示數據。如果這是您第一次運行d,則它沒有存儲先前的命令,囙此WinDBG將為您提供db(Display Byte)命令的輸出。

d d d db

顯示位元組

db將以位元組為組織輸出數據,並提供相應的ASCII值:

db

顯示文字

單詞或2位元組值可以用dw(顯示單詞)顯示。或者,可以使用dW顯示單詞和ASCII值:

dw dW

顯示單詞

我最喜歡的記憶體查看命令是dd(Display DWORDs)。雙字是一個雙字,所以是4位元組。dd將只顯示DWORDS,而dc將顯示DWORDS和ASCII值:

dd dd dc

顯示四字

要在WinDBG中顯示四字(4字/8位元組),請使用dq:

dq

顯示比特

你甚至可以用dyb顯示二進位:

dyb

顯示字串

字串用da顯示,實際上WinDBG將把所有內容都列印為ASCII,直到它達到一個空位元組。所以在這裡,即使esp不是字串,它也會將所有內容都視為字串,直到它達到空值。為了進一步說明這一點,我在esp和db esp L5中列印出了5個位元組:

da esp esp db esp L5

定址

到目前為止,我們只是通過使用esp作為記憶體檢查命令的參數來查看esp指向的記憶體,但是有許多不同的方法可以在啟動時引用記憶體。

esp esp

寄存器-如我們所見,您可以使用任何寄存器,WinDBG將使用該寄存器中的地址作為記憶體位址:

記憶體位址-您也可以通過提供記憶體位址來使用它本身:

偏移量-也可以使用數學運算式對寄存器或記憶體位址使用偏移量:

這些運算式可以在任何可以使用地址的地方使用。以下是WinDBG中的外觀:

WinDBG將輸出問號(?)用於無效/空閒記憶體。

?

指針

有時堆棧上的值只是指向另一個位置的指針。如果你想看看這個值,你需要做兩個查找。例如,假設我們知道值ebp+4是指向要讀取的某些程式集程式碼的指針。要查看該程式集,需要兩個命令。第一個命令顯示ebp+4的記憶體位址:

ebp+4 ebp+4

然後,第二個命令要求我們手動複製該地址的值,然後將其作為參數粘貼到u命令中,以便我們可以查看該程式集:

u

這很好,但是poi()函數有一個更簡單的方法。使用poi()我們只提供ebp+4作為參數,它將自動獲取該地址的值並使用它,而不是僅使用ebp+4的值:

poi() poi() ebp+4 ebp+4

限制輸出

默認情况下,WinDBG將輸出一組數據,但是我們可以限制如何使用L(大小範圍說明符)内容輸出該數據。我將使用大多數命令,只需在結尾附加一個值:

L L

用L指定的數位是與執行的命令相關的大小。例如,對於db,L表示要列印的位元組數,而對於dd,L表示要列印的dword數。

L db L dd L

這真的是讓你開始檢查記憶體-我知道,三篇博客文章建立了這個功能,它只是這個小部分?是的-還有一些記憶體檢查命令,但是要開始,d是覈心命令。查看下麵的提示瞭解更多資訊。

d

現在您已經脫離實際,讓我們看看一些方便的技巧和技巧,它們可以使您的調試體驗更好。

鍵盤快速鍵

在調試過程中,您可能會啟動和停止應用程序數百次,囙此從長遠來看,任何一個小的捷徑都可以為您解决大量的時間問題。鍵盤快速鍵很大,下麵是我使用最多的四個:

g

轉換格式

如果你還沒有弄清楚,WinDBG默認列印十六進位數位。這意味著12和十進位12不一樣。一個快速提示是.formats命令。使用它很簡單:

12 .formats

其中值是要轉換的內容。囙此,格式將接受您提供的任何內容,並以多種格式輸出:

.formats

現在我們知道12實際上是18:)。但是,也可以使用0n說明符提供十進位值:

12 0n

數學

有時你可能需要計算一個偏移量或只是做一些基本的數學運算。WinDBG將使用計算運算式?命令:

?

這些運算式可以是簡單的,也可以是複雜的,可以包含WinDBG使用的所有標準地址:

…當你有WinDBG時誰需要calc.exe!

calc.exe

為了讓生活更輕鬆,人們為WinDBG創建了許多擴展。這些是很好的小工具,可以在調試器中使用以提供功能。有些甚至是微軟製造的。有用的是!堆!地址!dh,還有!佩布。我將在另一篇博客文章中討論這些和更多內容-請繼續關注!

!heap !address !dh !peb

如果你不喜歡的話,這裡有一些非常好的WinDBG命令參攷、備忘單和教程。hh,這裡有幾個好的:

.hh

還有什麼竅門嗎?在下麵的評論中分享!