Ollydbg入门
OllyDbg完全教程
目錄
第一章概述.................................................................. 1
第二章組件.................................................................. 5
一、一般原理[General prnciples]........................................ 5
二、反匯編器[Disassembler]............................................. 8
三、分析器[Analysis]................................................... 9
四、Object掃描器[Object scanner]...................................... 12
五、Implib掃描器[Implib scanner]..................................... 12
第三章 OllyDbg的使用........................................................ 13
一、如何開始調試[How to start debugging session]...................... 13
二、CPU 窗口[CPU window].............................................. 14
三、斷點[Breakpoints]................................................. 14
四、數據窗口[Dump].................................................... 15
五、可執行模塊窗口[Executable modules window]......................... 16
六、內存映射窗口[Memory map window]................................... 17
七、監視與監察器[Watches and inspectors].............................. 19
八、線程[Threads]..................................................... 19
九、調用棧[Call stack]................................................ 20
十、調用樹[Call tree]................................................. 21
十一、選項[Options]................................................... 21
十二、搜索[Search].................................................... 22
十三、自解壓文件[Self—extracting (SFX)files]........................ 22
十四、單步執行與自動執行[Step—by—step execution and animation]...... 23
十五、Hit跟蹤[Hit trace].............................................. 23
十六、Run 跟蹤[Run trace]............................................. 24
十七、快捷鍵........................................................... 26
十八、插件[Plugins]................................................... 29
十九、技巧提示[Tips and tricks]....................................... 29
第四章其他功能............................................................. 30
一、調試獨立的DLL[Debugging of stand—alone DLLs]..................... 30
二、解碼提示[Decoding hints].......................................... 32
三、表達式賦值[Evaluation of expressions]............................. 32
四、自定義函數描述[Custom function descriptions]...................... 34
?
第一章 概述
?
OllyDbg 是一種具有可視化界面的 32 位匯編—分析調試器。它的特別之處在于可以在沒有源代碼時解決問題,并且可以處理其它編譯器無法解決的難題。
Version 1.10 是最終的發布版本。 這個工程已經停止,我不再繼續支持這個軟件了。但不用擔心:全新打造的OllyDbg2.00 不久就會面世!
運行環境:OllyDbg可以以在任何采用奔騰處理器的 Windows 95、98、ME、NT 或是 XP(未經完全測試)操作系統中工作,但我們強烈建議你采用300—MHz以上的奔騰處理器以達到最佳效果。還有,OllyDbg 是極占內存的,因此如果你需要使用諸如追蹤調試[Trace]之類的擴展功能話,建議你最好使用128MB以上的內存。
支持的處理器:OllyDbg支持所有 80x86、奔騰、MMX、3DNOW!、Athlon 擴展指令集、SSE指令集以及相關的數據格式,但是不支持SSE2指令集。
配置: 有多達百余個選項用來設置OllyDbg的外觀和運行。
數據格式:OllyDbg的數據窗口能夠顯示的所有數據格式:HEX、ASCII、UNICODE、 16/32位有/無符號/HEX整數、32/64/80位浮點數、地址、反匯編(MASM、IDEAL或是HLA)、PE文件頭或線程數據塊。
幫助: 此文件中包含了關于理解和使用OllyDbg的必要的信息。如果你還有 Windows API 幫助文件的話(由于版權的問題win32.hlp沒有包括在內),你可以將它掛在OllyDbg 中,這樣就可以快速獲得系統函數的相關幫助。
啟動: 你可以采用命令行的形式指定可執行文件、也可以從菜單中選擇,或直接拖放到OllyDbg中,或者重新啟動上一個被調試程序,或是掛接[Attach]一個正在運行的程序。OllyDbg支持即時調試。OllyDbg根本不需要安裝,可直接在軟盤中運行!
調試DLLs: 你可以利用OllyDbg調試標準動態鏈接庫 (DLLs)。OllyDbg 會自動運行一個可執行程序。這個程序會加載鏈接庫,并允許你調用鏈接庫的輸出函數。
源碼級調試:OllyDbg可以識別所有 Borland 和 Microsoft 格式的調試信息。這些信息包括源代碼、函數名、標簽、全局變量、靜態變量。有限度的支持動態(棧)變量和結構。
代碼高亮:OllyDbg的反匯編器可以高亮不同類型的指令(如:跳轉、條件跳轉、入棧、出棧、調用、返回、特殊的或是無效的指令)和不同的操作數(常規[general]、FPU/SSE、段/系統寄存器、在棧或內存中的操作數,常量)。你可以定制個性化高亮方案。
線程:OllyDbg可以調試多線程程序。因此你可以在多個線程之間轉換,掛起、恢復、終止線程或是改變線程優先級。并且線程窗口將會顯示每個線程的錯誤(就像調用 GETLASTERROR 返回一樣)。
分析:OllyDbg 的最大特點之一就是分析。它會分析函數過程、循環語句、選擇語句、表[tables]、常量、代碼中的字符串、欺騙性指令[tricky constructs]、API調用、函數中參數的數目,import表等等。這些分析增加了二進制代碼的可讀性,減少了出錯的可能性,使得我們的調試工作更加容易。
Object掃描:OllyDbg 可以掃描Object文件/庫(包括 OMF 和 COFF 格式),解壓代碼段[code segments]并且對其位置進行定向。
Implib掃描: 由于一些DLL文件的輸出函數使用的索引號,對于人來說,這些索引號沒有實際含義。如果你有與DLL相應的輸入庫[importlibrary],OllyDbg 就可以將序號轉換成符號名稱。
完全支持Unicode: 幾乎所有支持 ASCII 的操作同時也支持 UNICODE,反之亦然。
名稱:OllyDbg可以根據 Borland 和 Microsoft 格式的調試信息,顯示輸入/輸出符號及名稱。Object掃描器可以識別庫函數。其中的名稱和注釋你可任意添加。如果DLL中的某些函數是通過索引號輸出的,則你可通過掛接輸入庫[import library]來恢復原來的函數名稱。不僅如此,OllyDbg還能識別大量的常量符號名(如:窗口消息、錯誤代碼、位域[bit fields]…)并能夠解碼為已知的函數調用。
已知函數:OllyDbg 可以識別 2300 多個 C 和 Windows API 中的常用函數及其使用的參數。你可以添加描述信息、預定義解碼。你還可以在已知函數設定 Log 斷點并可以對參數進行記錄。
函數調用:OllyDbg可以在沒有調試信息或函數過程使用非標準的開始部分[prolog]和結尾部分[epilog]的情況下,對遞歸調用進行回溯。
譯者注:
004010D0?? push?ebp????????????? \
004010D1?? mov??ebp,esp???????? ???????? ?|
004010D3?? sub??esp,10h ???????? ???????? ? |>prolog
004010D6?? push?ebx??????? ???????? ???????? ? |
004010D7?? push?esi???????? ???????? ???????? ? |
004010D8?? push?edi???????? ???????? ???????? /
……
004010C5?? pop ??edi???????? ???????? ???????? \?
004010C6?? pop??esi???????? ???????? ???????? |
004010C7?? pop??ebx??????? ???????? ???????? |>epilog
004010C8?? mov??esp,ebp ???????? ???????? |
004010CA?? pop??ebp?????? ???????? ???????? |
004010CB?? ret? ???????? ???????? ???????? ???????? /
棧:在棧窗口中,OllyDbg 能智能識別返回地址和棧框架[Stack Frames]。并會留下一些先前的調用。如果程序停在已知函數上,堆棧窗口將會對其參數進行分析解碼。
譯者注:棧框架[Stack Frames]是指一個內存區域,用于存放函數參數和局部變量。
SEH 鏈: 跟蹤棧并顯示結構化異常句柄鏈。全部鏈會顯示在一個單獨的窗口中。
搜索:方法真是太多了!可精確、模糊搜索命令或命令序列,搜索常數,搜索二進制、文本字符串,搜索全部命令地址,搜索全部常量或地址域[address range],搜索所有能跳到選定地址的跳轉,搜索所有調用和被調用的函數,搜索所有參考字符串,在不同模塊中搜索所有調用、搜索函數名稱,在全部已分配的內存中搜索二進制序列。如果搜索到多個結果,你可以對其進行快速操作。
窗口:OllyDbg 能夠列出關于調試程序中的各種窗口,并且可以在窗口、類甚至選定的消息上設置斷點。
資源:如果 Windows API 函數使用了參考資源串,OllyDbg 可以顯示它。其支持顯示的類型僅限于附帶資源[attached resources]的列表、數據顯示及二進制編輯。
斷點:OllyDbg支持各種斷點:一般斷點、條件斷點、記錄斷點(比如記錄函數參數到記錄窗口)、內存讀寫斷點、硬件斷點(只適用于ME/NT/2000)等。在Hit跟蹤情況下,可以在模塊的每條命令上都設置INT3斷點。在使用500—MHZ處理器的 Windows NT 中,OllyDbg 每秒可以處理高達 5000 個中斷。
監視與監察器:每個監視都是一個表達式并能實時顯示表達式的值。你可以使用寄存器、常數、地址表達式、布爾值以及任何復雜代數運算,你還可以比較ASCII和UNICODE字符串。監察器[inspectors]是一種包含了兩個的索引序列的監視[Watches],它以二維表的形式呈現,可以對數組和結構進行解碼分析。?
Heap walk.:在基于Win95的系統中,OllyDbg 可以列出所有的已分配的堆。
句柄:在基于NT的系統中,OllyDbg 可列出被調試程序的所有系統句柄。
執行:.你可以單步執行、步入子程序或者步過子程序。你也可以執行程序直到函數返回時、執行到指定地址處,還可以自動執行。當程序運行時,你仍然可以操縱程序并能夠查看內存、設置斷點甚至修改代碼。你也可以任意的暫停或重啟被調試的程序。
Hit跟蹤:.Hit跟蹤可以顯示出目前已執行的指令或函數過程,幫助你檢驗代碼的各個分支。Hit跟蹤會在指定指令到達之前設置斷點,而在這個指令執行后,會把這個斷點清除掉。譯者注:Hit在英文中是“擊中”的意思,指令如果運行了就表示這個指令被“擊中”了,沒有執行的指令就是“未擊中”,這樣我們就很容易看出被調試程序哪些部分運行了,而哪些沒有運行。
Run跟蹤: Run跟蹤可以單步執行程序,它會在一個很大的循環緩沖區中模擬運行程序。這個模擬器包含了除了SSE指令集以外的所以寄存器、標志、線程錯誤、消息、已經函數的參數。你可以保存命令,這樣可以非常方便地調試自修改代碼(譯者注:比如加殼程序)。你可以設置條件中斷,條件包括地址范圍、表達式、命令。你可以將Run跟蹤信息保存到一個文件中,這樣就可以對比兩次運行的差別。Run跟蹤可以回溯分析已執行過的上百萬條命令的各種細節。
統計: 統計[Profiler]可以在跟蹤時計算某些指令出現的次數。因此你就能了解代碼的哪一部分被頻繁執行。
補丁:內置匯編器能夠自動找到修改過的代碼段。二進制編輯器則會以ASCII、UNICODE或者十六進制的形式同步顯示修改后的數據。修改后的數據同其它數據一樣,能夠進行復制—粘貼操作。原來的數據會自動備份,以便數據恢復時使用。你可以把修改的部分直接復制到執行文件中,OllyDbg會自動修正。 OllyDbg還會記錄以前調試過程中使用的所有補丁。你可以通過空格鍵實現補丁的激活或者禁止。
自解壓文件: 當調試自解壓文件時,你往往希望跳過解壓部分,直接停在程序的原始入口點。OllyDbg的自解壓跟蹤將會使你實現這一目的。如果是加保護的自解壓段,自解壓跟蹤往往會失敗。而一旦OllyDbg找到了入口點,它將會跳過解壓部分,并準確的到達入口點。
插件:你可以把自己的插件添加到OllyDbg中,以增加新的功能。OllyDbg 的插件能夠訪問幾乎所有重要的數據的結構、能夠在OllyDbg的窗口中添加菜單和快捷鍵,能夠使用100個以上的插件API函數。插件API函數有詳細的說明文檔。默認安裝已經包含了兩個插件:命令行插件和書簽插件。
UDD:OllyDbg 把所有程序或模塊相關的信息保存至單獨的文件中,并在模塊重新加載時繼續使用。這些信息包括了標簽、注釋、斷點、監視、分析數據、條件等等
更多:這里介紹的功能,僅僅是OllyDbg的部分功能。因為其具有如此豐富的功能,以至于OllyDbg能成為非常方便的調試器!
第二章 組件
一、一般原理[General prnciples]
我希望你能對80x86系列處理器的內部結構有所了解,同時具有一定的編寫匯編程序的能力。對于Microsoft Windows方面的知識,你也要熟悉。
OllyDbg是運行在Windows 95、Windows 98、Windows ME、Windows NT 和 Windows 2000系統下的一個單進程、多線程的分析代碼級調試工具。它可以調試PE格式的執行文件及動態鏈接庫,并可以對其打補丁。“代碼級”意味著你可以直接與比特、字節或處理器指令打交道。OllyDbg 僅使用已公開的 Win32 API 函數,因此它可以在所有 Windows 操作系統及后繼版本中使用。但是由于我沒有對 XP 系統進行徹底測試,因此不能保證OllyDbg功能的充分發揮。注意:OllyDbg 不支持對 .NET 程序的調試。
OllyDbg不是面向編譯器的。它沒有特別的規則規定必須是哪一個編譯器產生的代碼。因此,OllyDbg可以非常好的處理通過編譯器生成的代碼,或是直接用匯編寫入的代碼。
OllyDbg可以并行調試程序。你無須暫停執行程序,就可以瀏覽代碼和數據,設置斷點、停止或恢復線程,甚至直接修改內存。(這可以視為一種軟件調試的模式,與之相對的硬件模式則是當進程在運行時調試器被阻滯,反之亦然)。假使所需的操作比較復雜,OllyDbg會讓進程終止一小段時間,但是這種暫停對于用戶來說是透明的。有時進程會發生非法操作。你可以把OllyDbg設置成即時[just—in—time]調試器,它會掛接出錯程序,并停在程序產生異常的地方。
通過OllyDbg,你可以調試單獨的DLL[standaloneDLLs]文件。操作系統不能直接運行DLL 文件,因此OllyDbg將一個可以加載 DLL 的小程序壓縮到資源里,這個程序允許你調用最多10個參數的輸出函數。
OllyDbg是完全面向模塊[module—oriented]的。模塊[Module]包括可執行文件(擴展名通常為.EXE)和在啟動時加載或需要時動態加載的動態鏈接庫(擴展名通常為.DLL)。在調試期間,你可以設置斷點[breakpoints]、定義新的標簽[labels]、注釋[comment]匯編指令,當某個模塊從內存中卸載[unload]時,調試器會把這些信息保存在文件中,文件名就是模塊的名稱,擴展名為.UDD(表示 用戶自定義文件[User—Defined Data])當OllyDbg下一次加載該模塊時,它會自動恢復所有的調試信息,而不管是哪一個程序使用這個模塊。假設你正在調試程序Myprog1,這個程序使用了Mydll。你在 Mydll 中設置了一些斷點,然后你開始調試Myprog2,這個程序同樣使用了Mydll。這時你會發現,所有 Mydll 中的斷點依然存在,即使 Mydll 加載到不同的位置!
一些調試器把被調試進程的內存當作一個單一的(并且大部分是空的)大小為2 ^32字節的區域。OllyDbg采用了與之不同的技術:在這里,內存由許多獨立的塊組成,任何對內存內容的操作都被限制在各自的塊內。在大多數情況下,這種方式工作得很好并且方便了調試。但是,如果模塊包含好幾個可執行段[executable ?sections],你將不能一次看到全部代碼,然而這種情況是非常少見的。
OllyDbg 是一個很占用內存的程序[memory—hungry application]。它在啟動時就需要 3 MB,并且當你第一次裝載被調試的程序時還需要一到兩兆的內存。每一次的分析、備份、跟蹤或者文件數據顯示都需要占用一定的內存。因此當你調試一個很大的項目,發現程序管理器顯示有 40 或 60 兆內存被占用時,請不要驚慌。
為了有效地調試一些不帶源碼的程序,你必須首先理解它是如何工作的。OllyDbg 包含的大量特性可以使這種理解變得非常容易。
首先,OllyDbg包含一個內置的代碼分析器。分析器遍歷整個代碼,分出指令和數據,識別出不同的數據類型和過程,分析出標準API函數(最常用的大約有1900個)的參數并且試著猜出未知函數的參數數目。你也可以加入自己的函數說明[yourown function descriptions]。它標記出程序入口點和跳轉目的地,識別出跳轉表[table—drivenswitches]和指向字符串的指針,加入一些注釋,甚至標示出跳轉的方向等等。在分析結果的基礎上,調用樹[call tree]顯示哪些函數被指定過程調用(直接或間接)并且識別出遞歸調用、系統調用和葉子過程[leaf procedures]。如果需要的話,你可以設置解碼提示[decoding hints]來幫助分析器解析那些不明確的代碼或數據。
OllyDbg還包含Object掃描器[Object Scanner]。如果你有庫文件[libraries]或目標文件[object files],掃描器會在被調試的程序中定位這些庫函數。在全部函數調用中,對標準函數的調用占很重要的一部分(據我估計可達70%)。如果你知道正要被調用的函數的功能,你就不必把注意力集中在這個函數上,可以簡單地單步步過[step over]這個call。分析器知道400多個標準C函數,比如fopen和memcpy。然而我必須承認當前版本的OllyDbg不能定位很短的函數(比一個return命令多不了多少的)或相似的函數(只在重定位上有不同)。Object掃描器[Object scanner]也能夠識別輸入庫[import libraries]。如果某個DLL是按序號輸出的,你不會看到函數名,只會發現一堆無意義的神秘數字。這種DLL的開發者通常會提供一個輸入庫來實現函數符號名與序號間的對應。讓OllyDbg使用這個輸入庫,它就會恢復原始的函數符號名。
面向對象的語言(如C++),使用了一種叫做名稱修飾[namemangling]的技術,把函數類型和參數都加入函數名中。OllyDbg 可以解碼[demangle]這種函數名,使程序更易讀。譯者注:C++的名稱修飾是編譯器將函數的名稱轉變成為一個唯一的字符串的過程,這個字符串會對函數的類、其命名空間、其參數表,以及其他等等進行編碼。 C++的名稱修飾適用于靜態成員函數,也適用于非靜態成員函數。靜態函數的名稱修飾的一個好處之一,是能夠在不同的類里使用同一個名稱來聲明兩個或者更多的靜態成員函數————而不會發生名稱上的沖突。
OllyDbg完全支持 UNICODE,幾乎所有對 ASCII 字符串的操作都可以同樣應用于 UNICODE。
匯編指令都是很相似的。你經常會搞不清自己是不是已經跟蹤過某一段代碼。在OllyDbg中你可以加入自己的標簽[labels]和注釋[comments]。這些極大地方便了調試。注意一旦你注釋了某個DLL,以后每次加載這個DLL時,注釋和標簽都有效————盡管你在調試不同的程序。
OllyDbg可以跟蹤標準的棧幀[stack frames](由PUSH EBP; MOV EBP,ESP所創建的)。現代編譯器有禁止產生標準棧框架的選項,在這種情況下分配棧[stackwalk]是不可能的。當程序運行到已知的函數時,棧窗口[stack window]解析它的參數,調用棧[Call stack]窗口顯示到達當前位置所調用函數的序列。
?
現代的面向對象應用程序廣泛地使用了一種叫做結構化異常處理[Structured Exception Handling,SHE]的技術。SHE窗口[SEH window] 可以顯示異常處理鏈。
多種不同的搜索[search]選項可以讓你找到二進制代碼或數據、命令或命令序列、常量或字符串、符號名或在 Run跟蹤中的一條記錄。
對于任何地址或常量,OllyDbg 可以找出參考[referencing]到該地址或常量的全部命令的列表。然后你可以在這個列表里找出對你來說是重要的參考。舉例來說,某個函數可能被直接調用,或者經過編譯器優化后把地址放入寄存器間接調用,或者把地址壓入堆棧作為一個參數————沒問題,OllyDbg 會找出所有這樣的地方。它甚至能找到并列出所有和某個指定的位置有關的跳轉。
OllyDbg 支持所有標準類型的斷點[breakpoints]————非條件和條件斷點、內存斷點(寫入或訪問)、硬件斷點或在整個內存塊上下斷點(后兩項功能只在 Window ME,NT,2000,XP中有效)。條件表達式可以非常復雜(“當 [ESP+8] 的第 2 位被設置,并且 123456 位置處的字[word]小于10,或者 EAX 指向一個以“ABC”開頭的 UNICODE 字串,但跳過前10次斷點而在第11次中斷”)。你可以設定一條或多條指令,當程序暫停時由OllyDbg傳遞給插件插件[plugins]。除了暫停,你還可以記錄某個表達式的值(可以帶有簡短的說明),或者記錄OllyDbg已知的函數的參數。在Athlon 2600+、Windows2000 環境下,OllyDbg 可以每秒處理多達 25000 個條件斷點。
另一個有用的特性是跟蹤。OllyDbg 支持兩種方式的跟蹤:hit和run。
在第一種情況下,它對指定范圍內的每條指令上設置斷點(比如在全部可執行代碼中)。當到達設斷的指令后,OllyDbg清除斷點并且把該指令標記為hit。這種方法可以用來檢測某段代碼是否被執行。Hit跟蹤速度驚人的快,在一個很短時間的啟動后程序幾乎達到了全速(譯者注:這應該是與不進行調試時速度相比而言)。因為INT3斷點可能對數據有災難性的影響,所以我建議不要使用模糊識別過程。當代碼沒有被分析時Hit跟蹤是不可以使用的。
Run跟蹤[Run trace]是一步一步地執行程序,同時記錄精確的運行歷史和所有寄存器的內容、已知的參數和可選的指令(當代碼是自修改時會有幫助)。當然,這需要大量的內存(每個指令需要15至50個字節,取決于調試的模式)但是可以精確地回溯和分析。你可以只在選定的一段代碼甚至是一條指令中進行Run跟蹤,或者你可以跳過無關緊要的代碼。對于每個地址,OllyDbg能夠計算這個地址在Run跟蹤日志中出現的次數,雖然會導致執行緩慢但是可以得到代碼執行的統計。比如說,某命令讓你在每個已識別的過程入口處進行Run跟蹤,那么統計[profile]就會給你每個過程被調用的次數。在到達某條指令、某個地址范圍或指令計數器達到某一數值時Run跟蹤可以自動地暫停[pause]。
在多線程程序里OllyDbg可以自動管理線程[threads],如果你單步調試或跟蹤程序,它會自動恢復當前線程而掛起其它線程。如果你運行程序,OllyDbg 會恢復先前的線程狀態。
你可以為內存塊建立快照(叫做備份)。OllyDbg會高亮顯示所有的改動。你可以把備份保存到文件或從文件中讀取出來,從而發現兩次運行的不同之處。你可以查看備份,搜索下一處改動,恢復全部或選定的改動。補丁管理器[Patch manager]記錄了上次應用到程序中的所有補丁,在下次調試時可以再次應用它們。你可以很容易地把你的補丁加在可執行文件上。OllyDbg 會自動進行修正。
你不能在帶有 Win32 的16位 Windows 下使用 OllyDbg。這種32位擴展操作系統無法實現某些必需的調試功能。你既不能調試 DOS 程序也不能調試16位 NE(New Executable)格式文件,我也沒有打算在未來的版本中支持這些。
?
二、反匯編器[Disassembler]
反匯編器識別所有的標準80x86、保護、FPU、MMX和3DNow!指令集(包括Athlon擴展的MMX指令集)。但它不識別ISSI命令,盡管計劃要在下個版本中支持這種命令。某些過時或者未公開的命令,像LOADALL,也不支持。
反匯編器可以正確解碼16位地址。但它假設所有的段都是32位的(段屬性使用32位)。這對于PE[Portable Executable]格式文件總是正確的。OllyDbg不支持16位的NE[NewExecutables]格式。
如果你熟悉MASM或者TASM,那么反匯編的代碼對于你沒有任何問題。但是,一些特例也是存在的。以下命令的解碼與Intel的標準不同:
AAD (ASCII Adjust AX BeforeDivision)—該命令的解碼后的一般形式為:AAD imm8
AAM (ASCII Adjust AX AfterMultiply)—該命令(非十進制數)的一般解碼形式為:AAM imm8
SLDT (Store Local DescriptorTable register)—操作數總被解碼為16位。這個命令的32位形式會在目的操作數的低16位中存儲段選擇器,并保留高16位不變。
SALC (Sign—extend Carry bit to AL,undocumented)—OllyDbg 支持這個未公開指令。
PINSRW (Insert Word FromInteger Register, Athlon extension to MMX)—在AMD的官方文檔中,這個命令的內存形式使用了16位內存操作數;然而寄存器形式需要32位寄存器,但只使用了低16位。為了方便處理,反匯編器解碼寄存器為16位形式。而匯編器兩種形式都支持。
CVTPS2PI and CVTTPS2PI(Convert Packed Single—Precision Floating to Packed Doubleword, Convert with TruncationPacked Single—PrecisionFloating to Packed Doubleword)—在這些命令中,第一個操作數是MMX寄存器,第二個或者是128位XMM寄存器或者是64位內存區域。為了方便處理,內存操作數也被解碼為128位。
有些指令的助記符要依賴操作數的大小:
不分大小的形式 明確的16位形式?????? 明確的32位形式
PUSHA????? ???????? PUSHAW? ???????? ???????? PUSHAD
POPA ???????? ???????? POPAW???? ???????? ???????? POPAD
LOOP???????? ???????? ???????? LOOPW??? ???????? ???????? LOOPD
LOOPE?????? ???????? LOOPWE? ???????? ???????? LOOPDE
LOOPNE??? ???????? LOOPWNE??????? ???????? LOOPDNE
PUSHF ???? ???????? PUSHFW? ???????? ???????? PUSHFD
POPF ???????? ???????? POPFW???? ???????? ???????? POPFD
IRET? ???????? ???????? IRETW????? ???????? ???????? IRETD
你可以改變解碼大小敏感助記符[decoding of size—sensitive mnemonics]。根據選項,反匯編器從三種可能中選擇之一進行解碼。這個選項也會影響匯編器的默認處理方式。解碼MMX和3DNow!指令總是開啟的,盡管你的處理器并不支持這些指令。
?
三、分析器[Analysis]
OllyDbg 整合了一個快速而強大的代碼分析器。你可以從快捷菜單,或者在CPU窗口的反匯編面板中按 Ctrl+A ,或者在可執行模塊中選擇“分析全部模塊[Analyze all modules]”,來使用它。??????
分析器有很高的啟發性。它能區分代碼和數據,標記入口和跳轉目的地址,識別轉換表[switch tables],ASCII 和 UNICODE 串,定位函數過程,循環,高階轉換[high—level switches]并且能解碼標準API函數的參數(示例[example])。OllyDbg 的其他部分也廣泛的使用了分析后的數據。
這是如何實現的?我將為你揭開這一神秘面紗。第一遍,OllyDbg反匯編代碼段中所有可能的地址,并計算調用的每個目的地址的個數。當然,很多調用是假的,但不可能兩個錯誤的調用都指向了相同的命令,當然如果有三個的話,就更不可能了。因此如果有三個或者更多的調用指向了相同的地址,我可以肯定的說這個地址是某個頻繁使用的子程序的入口。從定位的入口出發,我繼續跟蹤所有的跳轉和函數調用,等等。按這種方法,我可能準確定位99.9% 的命令。但是,某些字節并不在這個鏈條上。我再用20多種高效的啟發方法(最簡單的方法,比如“直接訪問前64K內存是不允許的,像在MOV [0],EAX中”)來探測他們。有時,分析器在你感興趣的地方分析錯誤。有兩種解決方法:或者從選中的部分移除分析(快捷鍵退格鍵),這樣OllyDbg將使用默認的解碼(反匯編)方式;或者設置解碼提示[decoding hints]并重新分析。注意:在某些情況下,當分析器認為你的提示是不合適的,或者有沖突,則可能忽略你的設置。
探測程序的函數過程也很簡單。在分析器眼中看來,程序只是一個連綿不斷的代碼,從一個入口開始,可能達到(至少從理論上)所有的命令(除了NOP以及類似的用于填充間隙的命令)。你可能指定三個識別級別。嚴格的函數過程要求有準確的一個入口,并且至少有一個返回。在啟發級別下,分析器只要求過程有一個入口。而如果你選擇模糊模式,差不多連貫的代碼都會被識別為單獨的過程。現代編譯器進行全局代碼優化,有可能把一個過程分成幾個部份。在這種情況下,模糊模式非常有用。但是也會誤識別的機率也就更高。
同樣地,循環是一個封閉的連續的命令序列,并有一個到開始處的跳轉作為一個入口,還有若干個出口。循環與高級操作命令 do, while 和 for 相對應。OllyDbg能夠識別任何復雜的嵌套循環。他們會在反匯編欄[Disassembly]中用長而粗括號標記。如果入口不是循環的第一個命令,OllyDbg會用一個小三角進行標記。
為了實現一個轉換[switch], 許多編譯器,讀取轉換變量[switch variable]到寄存器中,然后減它,像如下的代碼序列:
???? MOV EDX,<switchvariable>
???? SUB EDX,100
???? JB DEFAULTCASE
???? JE CASE100??????? ; Case 100
???? DEC EDX
???? JNE DEFAULTCASE
???? ...?????????????? ; Case 101
?
這個序列可能還包含一到兩階的轉換表、直接比較、優化和其他元素。如果在比較或跳轉的很深處,這就很難知道哪是一個分支[Case]。OllyDbg 會幫助你,它會標記所有的分支,包括默認的,甚至嘗試分析每個分支的含義,如'A'、WM_PAINT 或者 EXCEPTION_ACCESS_VIOLATION。如果命令序列沒有修改寄存器(也就是僅僅由比較組成),那么這可能不是轉換,而很有可能是選擇嵌套:
???? if (i==0) {...}
???? else if (i==5) {...}
???? else if (i==10) {...}
如果需要OllyDbg將選擇嵌套解碼成選擇語句,請在分析1[Analysis1]中設置相關選項。
OllyDbg包含多達1900條常用API函數,這些都作為內部預處理資源。這個列表包含了KERNEL32, GDI32, USER32, ADVAPI32, COMDLG32, SHELL32, VERSION,SHLWAPI, COMCTL32, WINSOCK, WS2_32 和 MSVCRT。你可以添加自己的函數描述[add your own descriptions]。如果分析器遇到的調用,使用了已知的函數名(或者跳轉到這樣的函數),它將在調用之前立即解碼PUSH命令。因此,你只需略微一看就能明白函數調用的含義。OllyDbg還包含了大約400多種的標準C函數。如果你有原始的庫文件,我推薦你在分析前掃描目標文件。這樣OllyDbg將能解碼這些C函數的參數。
如果選項“猜測未知函數的參數個數”開啟,分析器將會決定這個調用函數過程使用的長度為雙字的參數個數。并且標記他們為參數1[Arg1],參數2[ Arg2],等等。注意:無論如何,寄存器參數是無法識別的,所以不會增加參數的數目。分析器使用了一種比較安全的方法。例如,它不能識別的沒有參數的函數過程,或者該過程POP命令直接做返回前的寄存器恢復,而不銷毀參數。然而,識別出來的函數參數數目通常非常高,這大大加大了代碼的可讀性。
分析器能夠跟蹤整型寄存器的內容。現代優化編譯器,特別是奔騰系列,頻繁地使用寄存器讀取常量和地址,或使用盡量少的使用內存。如果某個常量讀取到寄存器中,分析器會注意它,并嘗試解碼函數和其參數。分析器還能完成簡單的算術計算,甚至可以跟蹤壓棧和出棧。
分析器不能區分不同類的名稱[different kinds of names]。如果你將某些函數指定為已知的名稱,OllyDbg將會解碼所有到該地址的調用。這是幾個預定義的特殊名稱WinMain, DllEntryPoint and WinProc。你可能使用這些標簽標記主程序、DLL的的入口以及窗口過程(注意:OllyDbg不檢查用戶自定義的標簽是否唯一)。另外,假定預定義參數assumepredefined arguments是一種更好的方法,不幸的是,沒有一般規則能夠做到100%的準確分析。在某些情況下,例如當模塊包含了P—Code或代碼段中包換了大量的數據,分析器可能將一些數據解釋成代碼。如果統計分析顯示代碼部分很可能是壓縮包或者經過加密了,分析器會發出警告。如果你想使用Hit跟蹤[Hit trace],我建議你不要使用模糊分析[fuzzy analysis],因為設置斷點的地方可能正是數據部分。
自解壓文件[Self—extractablefiles] 通常有一個自提取器,在“正式”代碼段之外。如果你選擇自解壓選項[SFX option]中的“擴展代碼段,包含提取器[Extend code section to include self—extractor]”,OllyDbg將會擴展代碼段,形式上允許分析它,并可以使用Hit跟蹤[Hit] trace和Run跟蹤[Run trace]。
?
四、Object掃描器[Objectscanner]
?
掃描器將特定的目標文件或者目標庫(包括OMF和COFF兩種格式),提取出代碼段,然后將這些段定位在當前模塊的代碼節[Codesection]中.如果段定位好了,掃描器將從目標文件中的調試信息提取名稱(也就是所謂的庫標簽[librarylabels])。這極大的增加了代碼與數據的可讀性。掃描器并不會對已識別的目標文件進行標簽匹配,所以它不能識別非常小或相似的函數(比如:兩個函數只是在重定位有區別)。因此要經常檢查掃描器發送到登陸窗口的警告列表!
?
五、Implib掃描器 [Implibscanner]
?
某些DLL的輸出符號僅僅是一個序號。許多符號都是井號加數字(比如:MFC42.#1003),這非常不便于理解。幸運的是,軟件零售商提供了輸入連接庫(implibs),它與序號符號名相關。
使用implib掃描器的方法:從主菜單中選擇調試[Debug]—>選擇輸入鏈接庫[Select importlibraries]。當你加載應用程序時,OllyDbg會讀取鏈接庫并從內置表格[internal tables]中提取符號名。每次遇到序號符號,而對應的鏈接庫已經注冊到OllyDbg中時,這個序號符號會被替換。
?
第三章 OllyDbg的使用
一、如何開始調試[How to start debugging session]
最簡單的方法是:運行 OllyDbg,點擊菜單上的文件[File]—>打開[Open],選擇你想調試的程序。如果程序需要命令行參數,你可以在對話框底部的輸入欄中,輸入參數或者選擇以前調試時輸入過的一條參數。
OllyDbg 能夠調試獨立的DLL[stand—alone DLLs]。在這種情況下,OllyDbg 會創建并運行一個小的應用程序來加載鏈接庫并根據你的需要調用輸出函數。
如果你想重新啟動上一次調試的程序,只要按一下 Ctrl+F2(這是重啟程序的快捷鍵)(???),這樣OllyDbg會以同樣的參數運行這個程序。另一種做法是在菜單中選擇文件[File],從歷史列表中選擇程序。你也可以在 Windows 資源管理器中將可執行文件或 DLL 文件拖拽到OllyDbg中。
當然,你可以在OllyDbg啟動時,運行指定帶有運行參數的被調試程序。例如:你可以在桌面創建一個OllyDbg的快捷方式,右擊并選擇“屬性”,在“快捷方式”中的“目標”中添加調試的程序的全路徑。這樣,你每次雙擊快捷方式時,OllyDbg 將自動運行被調試程序。注意:DLL文件不支持這種方式。
你可以把正在運行的進程掛接到OllyDbg中。在菜單中打開文件[File]—>掛接[Attach],從進程列表中選擇要掛接的進程。注意:在你關閉OllyDbg的同時,這個進程也會被關閉。不要掛接系統進程,否則可能會導致整個操作系統的崩潰。(事實上在大多數情況下,操作系統禁止你掛接敏感進程)。
OllyDbg 可以作為即時[just—in—time]調試器。這需要在系統注冊表中注冊。在菜單中選擇選項[Options]—>即時調試[Just—in—time debugging] 并在彈出的對話框中單擊按鈕“設置OllyDbg為即時調試器”[MakeOllyDbgjust—in—time debugger]。今后,如果某個應用程序發生了非法操作,系統將提示你是否用OllyDbg 調試這個程序。操作系統會啟動OllyDbg并直接停在發生異常的地方。如果你選擇了“掛接時不詢問”[attaching without confirmation],則在即時調試時OllyDbg不會彈出詢問對話框。如果想恢復成以前的即時調試器[Restore oldjust—in—time debuger],按相應的按鈕即可。
另一種方法是把OllyDbg添加到與可執行文件關聯的快捷菜單中(這個想法是 Jochen Gerster 提出的)。在主菜單中,選擇選項[Options]—>添加到資源管理器中[Addto Explorer]。以后你可以在所有的文件列表中,右擊可執行文件或DLL,在快捷菜單中選擇OllyDbg。這個功能會創建四個注冊表鍵值:
HKEY_CLASSES_ROOT\exefile\shell\Open with OllyDbg
HKEY_CLASSES_ROOT\exefile\shell\Open with OllyDbg\command
HKEY_CLASSES_ROOT\dllfile\shell\Open with OllyDbg
HKEY_CLASSES_ROOT\dllfile\shell\Open with OllyDbg\command
OllyDbg能夠調試控制臺程序(基于文字的)。
OllyDbg不能調試.NET應用程序。.NET程序是由微軟的中間語言這種偽指令組成的,或是on—the—fly to native ?6 commands編譯的。
注意:如果你運行的是Windows NT、2000 或XP操作系統,你應該擁有管理員權限以便能夠調試程序。。
?
二、CPU 窗口[CPUwindow]
對于用戶來說,CPU窗口在OllyDbg中是最重要的窗口。你調試自己程序的絕大部分操作都要在這個窗口中進行。它包括以下五個面板(這五個面板的大小都是可以調節的):
l? 反匯編[Disassembler]
l? 信息[Information]
l? 數據[Dump]
l? 寄存器[Registers]
l? 棧[Stack]
按TAB鍵,可以切換到下一個CPU面板中(順時針方向)。
按Shift+TAB,可以切換到前一個CPU面板(逆時針方向)。
?
三、斷點[Breakpoints]
OllyDbg支持數種不同類型的斷點:
一般斷點[Ordinary breakpoint], 將你想中斷的命令的第一個字節,用一個特殊命令INT3(調試器陷阱)來替代。你可以在反匯編窗口中選中要設斷點的指令行并按下 F2 鍵就可以設定一個此類型的斷點。也可以在快捷菜單中設置。再次按下F2 鍵時,斷點將被刪除。注意,程序將在設斷指令被執行之前中斷下來。INT3斷點的設置數量是沒有限制的。當你關閉被調試程序或者調試器的時候,OllyDbg將自動把這些斷點保存到硬盤中,永遠不要試圖在數據段或者指令的中間設置這種斷點,如果你試圖在代碼段以外設置斷點,OllyDbg將會警告。你可以在安全選項[Security options]中永遠關閉這個提示,在某些情況下調試器會插入自帶的臨時INT3斷點。
條件斷點[Conditional breakpoint](快捷鍵 Shift+F2)是一個帶有條件表達式的普通INT3斷點。當調試器遇到這類斷點時,它將計算表達式的值,如果結果非零或者表達式無效,將暫停被調試程序,當然,由條件為假的斷點引起的開銷是非常高的(主要歸因于操作系統的反應時間)。在Windows NT、奔騰Ⅱ/450處理器環境下OllyDbg每秒最多處理2500個條件為假的斷點。條件斷點的一個典型使用情況就是在Windows消息上設置斷點(比如 WM_PAINT)。為此,你可以將偽變量 MSG 同適當的參數說明聯合使用。如果窗口被激活,參考一下后面的消息斷點描述。
條件記錄斷點Conditional logging breakpoint](Shift+F4)是一種條件斷點,每當遇到此類斷點或者滿足條件時,它將記錄已知函數表達式或參數的值。例如,你可以在一些窗口過程函數上設置記錄斷點并列出對該函數的所有調用。或者只對接收到的WM_COMMAND消息標識符設斷,或者對創建文件的函數(CreateFile)設斷,并且記錄以只讀方式打開的文件名等,記錄斷點和條件斷點速度相當,并且從記錄窗口中瀏覽上百條消息要比按上百次F9輕松的多,你可以為表達式選擇一個預先定義好的解釋說明。你可以設置通過的次數—每次符合暫停條件時,計數器就會減一。如果通過計數在減一前,不等于零,OllyDbg就會繼續執行。如果一個循環執行100次(十進制),在循環體內設置一個斷點,并設置通過次數為99(十進制)。OllyDbg將會在最后一次執行循環體時暫停。
另外,條件記錄斷點允許你傳遞一個或多個命令給插件[plugins]。例如,你需要使用命令行插件改變一個寄存器的內容,然后繼續執行程序。
消息斷點[Message breakpoint]和條件記錄斷點基本相同,除了OllyDbg會自動產生一個條件,這個條件允許在窗口過程的入口處設置某些消息(比如WM_PSINT)斷點,你可以在窗口[Windows]中設置它。
跟蹤斷點[Trace breakpoint] 是在每個選中命令上設置的一種特殊的INT3斷點。如果你設置了Hit跟蹤[hit trace],斷點會在命令執行后移除,并在該地址處做一個標記。如果你使用的是Run跟蹤[run trace],OllyDbg會添加跟蹤數據記錄并且斷點仍然是保持激活狀態。
內存斷點[Memory breakpoint]OllyDbg每一時刻只允許有一個內存斷點。你可以在反匯編窗口、CPU窗口、數據窗口中選擇一部分內存,然后使用快捷菜單設置內存斷點。如果有以前的內存斷點,將被自動刪除。你有兩個選擇:在內存訪問(讀,寫,執行)時中斷,或內存寫入時中斷。設置此類斷點時,OllyDbg將會改變所選部分的內存塊的屬性。在與80x86兼容的處理器上將會有4096字節的內存被分配并保護起來。即使你僅僅選擇了一個字節,OllyDbg 也會將整個內存塊都保護起來。這將會引起大量的錯誤警告,請小心使用此類斷點。某些系統函數(特別是在Windows95/98下)在訪問受保護的內存時不但不會產生調試事件反而會造成被調試程序的崩潰。
硬斷點[Hardware breakpoint](僅在Windows ME,NT或2000下可用)在80x86兼容的處理器上,允許你設置4個硬件斷點。和內存斷點不同,硬件斷點并不會降低執行速度,但是最多只能覆蓋四個字節。在單步執行或者跟蹤代碼時,OllyDbg能夠使用硬斷點代替INT3斷點。
內存訪問一次性斷點[Single—shot break on memory access](僅在Windows NT或2000下可用)。你可以通過內存窗口的快捷菜單(或按F2),對整個內存塊設置該類斷點。當你想捕捉調用或返回到某個模塊時,該類斷點就顯得特別有用。中斷發生以后,斷點將被刪除。
暫停Run跟蹤[Run trace pause] (快捷鍵:Ctrl+T)是在每一步Run跟蹤[run trace]時都要檢查的一個條件集。你可以在EIP進入某個范圍或超出某個范圍時暫停,某個條件為真時暫停,或者命令與指定的模式匹配時暫停,或者當命令可疑的時候暫停。注意,這一選擇會極大的(高達20%)降低Run跟蹤的速度。OllyDbg也可以在一些調試事件[debugging events]上暫停程序執行。比如加載或卸載DLL,啟動或終止線程,或者程序發出調試字符串的時候。
?
四、數據窗口[Dump]
數據窗口用于顯示內存或文件的內容。你可以從以下預處理格式[predefined formats]中選擇一種顯示方式:字節[byte]、文本[text]、整數[integer]、浮點數[float]、地址[address],反匯編[disassembly]、 PE頭[PE Header]。
所有的dump窗口支持備份[backup]、搜索和編輯操作。CPU 窗口[CPU window]的Dump面板允許你對可執行代碼的數據和可執行文件(.exe,或.dll)的內存映射做如下操作:定義標簽[labels]、設置內存斷點[memory breakpoints], 查找參考[references]。數據菜單[Dump menu]只顯示與選中部分相關的命令。
如果 備份[backup]可用,則單擊第一個列標題欄,會在地址[Address]/備份[Backup] 兩種顯示模式之間切換。點擊其他列標題欄,會改變Dump模式。
像反匯編窗口一樣,數據窗口也保存了大量查看內存地址的歷史記錄。你可以通過“+”和“—”鍵來訪問過去查看過的數據地址空間。要翻動一字節的數據,可以按住Ctrl+↓或Ctrl+↑。
?
五、可執行模塊窗口[Executable modules window]
可執行模塊窗口(快捷鍵:Alt+E)列出了當前被調試進程加載的所有可執行模塊。它也顯示了很多有用的信息,比如模塊大小、入口地址、模塊版本、以及可執行文件路徑等。一些信息,如以十進制顯示的模塊大小、入口地址的符號名、是否為系統模塊等,通常是被隱藏的。如果想看,可以增加相應欄的寬度。快捷菜單支持以下操作:
刷新[Actualize]—重新掃描模塊并去除對新加載模塊的高亮顯示。在大多數情況下,OllyDbg會自動完成該操作。
查看內存[View memory]—打開內存窗口,并定位到屬于該模塊鏡像的第一個內存塊處。
在CPU窗口中查看代碼[Viewcode in CPU](快捷鍵:回車鍵)—在反匯編窗口中顯示模塊的可執行代碼。
跟進到入口[Follow entry]—在反匯編窗口中跟進到模塊的入口處。
在CPU窗口中查看數據[Dumpdata in CPU]—在CPU窗口的數據面板中顯示模塊的數據段。塊代碼段。
顯示名稱[View names](快捷鍵:Ctrl+N)—顯示當前模塊定義或使用的全部名稱[names](包括輸出表、引入表、鏈接庫、用戶自定義)。
標記為系統DLL[Mark assystem DLL],標記為非系統DLL[Mark as non—system DLL]—將選中模塊標記為系統或非系統屬性。如果設置為系統屬性,則在Run跟蹤[Run trace]時會直接執行(不進行跟蹤)這個模塊,從而大大加快跟蹤速度。默認情況下,所有駐留在系統目錄(通常在Windows 95/98下為c:\windows\system,在WinNT/2000/XP下為c:\winnt\system32)的模塊都認為是系統模塊。
立即更新.udd文件[Update .uddfile now]—向文件“<模塊名>.udd”寫入模塊相關的全部數據,udd文件保存了在調試期間設置的斷點、標簽、注釋、監視、分析等信息。當模塊卸載時OllyDbg會自動創建.udd文件。
查看可執行文件[View executable file]—顯示可執行文件的全部內容。
查看全部資源[View all resources]—以列表形式顯示模塊定義的全部資源,并帶有一個簡短信息。OllyDbg并不把資源當作單獨實體來支持。你可以提取[Dump]并以二進制的形式進行編輯。
查看資源字符串[View resource strings]—以列表形式顯示資源字符串及其標識符。
查看Run跟蹤的統計[View runtrace profile]—在此模塊中計算統計[profile]。
分析全部模塊[Analyze all modules]—允許同時分析全部模塊。分析將從代碼中提取大量的有用信息;代碼經過分析后再進行調試,通常會非常快并且可靠。
鼠標雙擊某一行,將會在反匯編窗口中顯示模塊的執行代碼。
?
六、內存映射窗口[Memory map window]
內存映射窗口顯示了被調試程序分配的所有內存塊。因為沒有標準的方法來完成這項任務,所以 OllyDbg可能會把一個大的內存塊分成幾個部分。然而,在大多數情況下,并非一定要精確處理。如果想查看由應用程序通過調用GlobalAlloc ()和LocalAlloc()等申請的內存塊列表,請使用堆列表[Heap list]。
如果內存塊是可執行模塊的一個節,OllyDbg則會報告這個內存塊所包含的數據類型:代碼、數據、資源等。
Windows95/98是和WindowsNT/2000是有一些區別的。在Windows95/98下,OllyDbg是不能顯示被映射文件的名稱的。另外,Windows95/98不允許的訪存類型為讀和寫,然而,在WindowsNT/2000下,OllyDbg卻有擁有更多功能,包括執行訪問,寫復制[copy—on—write]以及監視標志位。OllyDbg忽略寫復制[copy—on—write]屬性。
如果OllyDbg發現程序分配了新內存或者重新分配了已經存在的內存塊,它將在內存映射窗口中高亮顯示相應的記錄,去掉高亮度顯示,可以選擇快捷菜單中的刷新[Actualize]項。你可以按Alt+M來調用內存窗口。
以下是快捷菜單中可以選擇的菜單項:
刷新[Actualize]—更新已分配內存的列表并去除對新內存塊的高亮顯示。
在反匯編窗口中查看[View in Disassembler]—在反匯編窗口中查看:在反匯編窗口中打開內存塊,這一選項僅在某些模塊的內存塊中包含可執行代碼或者自解壓器時可用。
在CPU數據窗口中查看[Dumpin CPU]—在CPU的數據窗口中顯示內存塊的內容。
?
數據窗口[Dump]—在單獨窗口中顯示內存塊內容。如果內存塊的類型已知,則OllyDbg會自動選擇顯示格式。
查看全部資源[View all resources]—如果內存塊包含資源數據,則列出所有資源及相關數據。OllyDbg并不把資源當作單獨實體來支持。你可以顯示其數據并以二進制的形式進行編輯。
查看資源字符串[View resource strings]—如果內存塊包含資源數據,則列出全部資源字符串及其標識符。
搜索[Search]—允許搜索所有的內存塊,從選擇處開始,搜索匹配的二進制串。如果找到,則OllyDbg將顯示該內存塊。內存映像窗口和數據窗口共享同一種搜索模式,所以你可以在彈出的數據窗口中立即繼續搜索該二進制串出現的下一位置。按Esc鍵可以關閉數據窗口。
搜索下一個[Search next](快捷鍵:Ctrl+L)—繼續上次搜索。
設置訪問中斷[Set break—on—access](快捷鍵:F2,僅在WindowsNT/2000下可用)—保護整個內存塊。當中斷發生后OllyDbg暫停被調試程序并清除斷點。這類斷點在你想捕捉調用或返回到某個模塊的時候特別有用。
清除訪問中斷[Remove break—on—access](快捷鍵:F2)—從內存塊中清除訪問中斷保護。
設置內存訪問斷點[Set memory breakpoint on access]—在整個內存塊上設置斷點,每當該內存塊被訪問時程序都將中斷。OllyDbg只支持一個內存訪問斷點。在Windows95/98下,當系統程序訪問含有內存斷點的內存塊時,可能會導致所被調試程序崩潰,因此,不到萬不得已,請不要設置這種斷點。
設置內存寫入斷點[Set memory breakpoint on write]—在整個內存塊上設置斷點,每當該內存塊被寫入數據時程序都將中斷。在Windows95/98下,當系統程序訪問含有內存斷點的內存塊時,可能會導致所被調試程序崩潰,因此,不到萬不得已,請不要設置這種斷點。
清除內存斷點[Remove memory breakpoint]—清除內存斷點。
清除自解壓內存斷點[Remove SFX memory breakpoint]—停止搜索自解壓程序[self—extractable(SFX) program]的真實入口。這個搜索使用了特殊類型的內存斷點。
訪問設置[Set access]—設置整個內存塊的保護屬性,可選擇的有:
禁止訪問[No access]
只讀[Read only]
讀/寫[Read/write]
執行[Execute]
執行/讀[Execute/read]
完全訪問[Full access]
復制到剪切板[Copy to clipboard]
?
?
整行[Whole line]—以多行文本(包括解釋)的方式把所選記錄復制到剪切板,如果復制時想排除某些列,可將該列的寬度置為最小(該欄剩余的邊框將變灰)。
整個表格[Whole table]—以多行文本的方式將整個內存映像信息復制到剪切板,該文本的第一行為窗口標題("內存映射[Memorymap]"),第二行為列標題欄,后面幾行的內容為內存數據記錄。復制將保持列的寬度。如果復制時想排除某些列,可將該列的寬度置為最小(該欄剩余的邊框將變灰)。
?
七、監視與監察器[Watches and inspectors]
監視[Watch]窗口包含若干個表達式[expressions]。它在第二列里顯示這些表達式的值。OllyDbg 會把這些表達式保存到主模塊的.UDD文件中,因此它們在下一次調試時同樣有效。
監察器[inspector]是顯示若干變量、1/2維數組或是選定項目結構數組[selecteditems of array of structures]的獨立窗口。它的表達式與監視窗口中的基本相同,只是多包含了兩個參數:%A和%B。你可以指定這兩個參數的界限,OllyDbg 將會用所有可能的組合代替表達式中的%A和%B。從0開始一直到界限(不包含界限),并在表格中顯示結果。參數%B(列數)的界限不能超過16。
例如,如果你指定了表達式%A+%B,并且限定%A和%B的上限為3,你將獲得如下的表格:
?
八、線程[Threads]
OllyDbg 以簡單而有效的線程管理為特色。如果你單步調試、跟蹤、執行到返回或者執行到所選,則線程管理器將停止除當前線程以外的所有線程。即使當前線程被掛起,它也會將其恢復。在這種情況下,如果你手動掛起或者恢復線程,動作將被延期。如果你運行被調試的應用程序,OllyDbg將恢復最初的線程狀態。(從調試器的角度來看,Hit跟蹤[hit trace]和自由運行是等效的)。
依據這種方案,線程窗口可能會有如下五種線程狀態:
激活[Active]???? ?????? 線程運行中,或被調試信息暫停t
掛起[Suspended]????? 線程被掛起
跟蹤[Traced] ?? ?????? 線程被掛起,但OllyDbg正在單步跟蹤此線程
暫停[Paused]? ?????? 線程是活動的,但OllyDbg臨時將其掛起,并在跟蹤其它的線程
結束[Finished]? ?????? 線程結束
線程窗口同時也顯示了最后的線程錯誤(GetlastError函數的返回值)并計算該線程以用戶模式和系統模式(僅NT/2000/XP)運行的時間。線程窗口還會高亮主線程的標識符。
以下在快捷菜單中可用:
刷新[Actualize]—標記所有線程為舊的。
掛起[Suspend]—掛起線程。
恢復[Resume]—恢復先前掛起的線程。
設置優先級[Set priority]—調整進程中線程的優先級。以下選項可用:
空閑[Idle]?????? ???????? ???????? —進程中線程的最低優先級
最低[Lowest]??????
低[Low]??????
標準[Normal]??????
高[High]??????
最高[Highest]??????
時間臨界[Time critical]???? —最高優先級
在CPU窗口打開[Open inCPU](雙擊)—在CPU窗口中顯示所選線程的當前狀態。
復制到剪切板[Copy to clipboard]:
整行[Whole line]—全部行——以多行文本的形式并帶注釋將所選記錄復制到剪切板。如果在復制時想排除某個欄目,可以將該欄的寬度置為最小(欄目的殘留部分將變灰)。
整個表格[Whole table]—整個表格——以多行文本的形式將整個內存映象復制到剪切板,該文本的第一行包含窗口標題(“內存映射[Memorymap]”),第二行是欄目標題,所有后繼行是內存數據記錄。復制將保持欄目的寬度。如果在復制時想排除某些欄目,可以將該欄的寬度置為最小(欄目的殘留部分將變灰)。
?
九、調用棧[Call stack]
調用棧窗口(快捷鍵:Alt+K)根據選定線程的棧,嘗試反向跟蹤函數調用順序并將其顯示出來,同時包含被調用函數的已知的或隱含的參數。如果調用函數創建了標準的堆棧框架(PUSH EBP; MOV EBP,ESP),則這個任務非常容易完成。現代的優化編譯器并不會為棧框架而操心,所以OllyDbg另辟蹊徑,采用了一個變通的辦法。例如,跟蹤代碼到下一個返回處,并計算其中全部的入棧、出棧,及 ESP 的修改。如果不成功,則嘗試另外一種辦法,這個辦法風險更大,速度也更慢:移動棧,搜索所有可能的返回地址,并檢查這個地址是否被先前的已分析的命令調用。如果還不行,則會采用啟發式搜索。棧移動[Stack Walk]可能會非常慢。OllyDbg 僅在調用棧窗口打開時才會使用。
調用棧窗口包含5個欄目:地址[Address]、棧[Stack]、過程[Procedure],調用來自[Called from],框架[Frame]。
地址[Adress]欄包含棧地址,棧[Stack]欄顯示了相應的返回地址或參數值。
函數[Procedure](或 函數/參數[Procedure / arguments])顯示了被調用函數的地址,在某些情況下,OllyDbg并不能保證該地址是正確的并會添加如下標記之一:
??????? ?????? ?????? ?????? 找到的入口點不可靠
可能[Maybe]??????? OllyDbg無法找到精確的入口點,報告的地址是用啟發式算法猜測的。
包含[Includes]?????? OllyDbg無法找到入口點,僅知道該函數包含顯示的地址
通過按例標題欄上的按鈕或從菜單中選擇“隱藏/顯示參數[Hide/Show arguments]”,可以在顯示或隱藏函數的參數之間切換。
調用來自[Called from]用于顯示調用該函數的命令地址。
最后一欄是框架[Frame]這一欄默認是隱藏的,如果框架指針的值(寄存器EBP)已知的話,則該欄用于顯示這個值。
當調用函數經過分析[analyzed].后,棧移動會更可靠并且迅速。
?
十、調用樹[Call tree]
調用樹(快捷鍵:在反匯編窗口中Ctrl+K)利用分析[Analysis]的結果來找出指定函數過程直接或間接調用的函數列表,同時列出指定函數過程被調用的地址。為了避免由此可能造成的副作用。調用樹會判斷選定函數是否明確地是遞歸的。 “明確地”意味著它不會跟蹤目標未知的調用,比如CALL EAX。如果函數過程中有未知調用,調用樹將會添加標記“未知目標”。
某些函數調用將會添加如下注釋之一:
葉子[Leaf]???? ????? 不調用其他函數
純函數[Pure]??? ??? 不調用函數,不會產生副作用
單返回[RETN]? ???? 只有一個RETN 命令
系統[Sys]???? ?????? 系統動態鏈接庫中的函數。系統動態鏈接庫定義為保存在系統目錄下的動態鏈接庫。
如果想在調用樹上移動,可以雙擊“被調用[Calledfrom]”或“調用/直接調用[Calls/Callsdirectly]”兩欄中的地址。調用樹窗口保存了移動記錄(快捷鍵“—”和“+”)。
如果被調試的程序包含幾個模塊,推薦你分析所有模塊。Call tree 不會試圖處理系統函數。
?
十一、選項[Options]
外觀選項[Appearance options]
常規[General]
默認[Defaults]
對話框[Dialogs]
目錄[Directories]
字體[Fonts]
顏色[Colours]
代碼高亮[Code highlighting]
調試選項[Debugging options] (Alt+O)
安全[Security]
調試[Debug]
事件[Events]
異常[Exceptions]
跟蹤[Trace]
自解壓[SFX]
字符串[Strings]
地址[Addresses]
命令[Commands]
反匯編[Disasm]
CPU
寄存器[Registers]
棧[Stack]
分析1[Analysis 1]
分析2[Analysis 2]
分析3[Analysis 3]
即時調試[Just—in—time debugging]
添加到資源管理器[Add to Explorer]
?
十二、搜索[Search]
OllyDbg 允許你使用以下的搜索方式:
符號名(標簽)[Symbolic name (label)]
二進制串[binary string]
常量[constant]
命令[command]
命令序列[sequence of commands]
模塊間調用[intermodular calls]
修改過的命令或數據[modified command or data]
自定義標簽[user—defined label]
自定義注釋[user—defined comment
文本字符串[text string]
Run跟蹤的記錄[recordin run trace]
參考命令[referencing commands]
?
十三、自解壓文件[Self—extracting(SFX) files]
自解壓文件由提取程序和壓縮的原程序兩部分組成。當遇到自解壓文件(SFX)文件時,我們通常希望跳過解壓部分,而直接跳到原始程序的入口(真正的入口)。
OllyDbg 包含了幾個便于完成這一任務的功能。
通常提取程序的加載地址都在執行代碼之外。在這種情況下,OllyDbg 將這類文件均視作為自解壓文件(SFX)。
當自解壓選項[SFX options]要求跟蹤真正入口時,OllyDbg 在整個代碼節[Code section]設置內存斷點,最初這里是空的,或者只包含壓縮數據。當程序試圖執行某個在這個保護區域的命令,而這些命令不是RET和JMP時,OllyDbg 會報告真正的入口。這就是提取工作的原理。
上面的方法非常慢。有另外一種比較快的方法。每次讀取數據發生異常時,OllyDbg 使這個4K內存區域變為可讀,而使原先可讀的區域變為無效。而每次發生寫數據異常時,OllyDbg 使這個區域變為可寫,而使原先可寫的區域變為無效。當程序執行在保留的保護區域中的指令時,OllyDbg 報告真正的入口。但是,當真正的入口點在可讀或可寫區域內部時,報告的地址就可能有誤。
你可以糾正入口位置,選擇新的入口,從反匯編窗口的快捷菜單中選擇“斷點[Breakpoint]—>設置真正的自解壓入口[Set real SFX entry here]”。如果相應的SFX選項是開啟的,OllyDbg下次可以迅速而可靠的跳過自提取程序。
注意:OllyDbg 在跟蹤采取了保護或者反調試技術的解壓程序時通常會失敗。
?
十四、單步執行與自動執行[Step—by—step execution and animation]
你可以通過按 F7(單步步入)或 F8(單步步過),對程序進行單步調試。這兩個單步執行操作的主要區別在于:如果當前的命令是一個子函數,按F7,將會進入子函數,并停在子函數的第一條命令上;而按 F8,將會一次運行完這個子函數。如果你單步步過的子函數中含有斷點或其他調試事件,執行將會被暫停,并且OllyDbg會在子函數的后一條命令上,自動下一個斷點,而這個斷點你遲早會碰到。
如果被調試程序停在異常上,你可以跳過它,并轉到被調試程序建立的句柄處。只需簡單的 Shift 鍵和任何一個單步命令。
如果需要連續按F7、F8鍵上百次,你可以使用自動執行(Ctrl+F7或者Ctrl+F8)功能。在這種情況下,OllyDbg 將自動重復F7或者F8操作,并且實時更新所有的窗口。這個過程會在下面情況停止:
—按 Esc 鍵或發出任何單步命令
—OllyDbg 遇到斷點
—被調試程序發生異常
使用“+”和“—”按鍵,可以回朔以前的執行歷史[execution history]。
注意:當執行停止時OllyDbg將會刷新大部分窗口。如果動態執行過程非常慢,可以嘗試關掉或最小化沒有用的窗口。
另外,更快捷的找到以前執行指令的辦法是Run跟蹤[run trace]。它將創建一個執行協議并告知你指定指令的執行時間和次數
?
十五、Hit跟蹤[Hittrace]
Hit跟蹤能夠讓你辨別哪一部分代碼執行了,哪一部分沒有。OllyDbg的實現方法相當簡單。它將選中區域的每一條命令處均設置一個INT3斷點。當中斷發生的時候,OllyDbg便把它去除掉,并把該命令標志為命中[hit]。因為每個跟蹤斷點只執行一次,所以這種方法速度非常快。
在使用Hit跟蹤的時候,一定要注意不能在數據中設置斷點,否則應用程序極有可能崩潰。因此,你必須打開相關的菜單選項,以進行代碼分析[analyze]。我推薦你選擇嚴格或啟發式函數識別[strict or heuristical procedure recognition]。如果選擇模糊[Fuzzy]的話,可能會產生很多難以容忍的錯誤,而且經常把本不是函數的代碼段識別成函數。
只要你在模塊中設置了跟蹤斷點,哪怕只設了一個,OllyDbg都會分配兩倍于代碼段大小的緩沖區。
注意:當你退出Hit跟蹤的時候,Run跟蹤也會同時退出。
?
十六、Run 跟蹤[Run trace]
Run跟蹤是一種反方向跟蹤程序執行的方式,可以了解以前發生的事件。你還可以使用Run跟蹤來了解運行的簡單統計[profile]。基本上,OllyDbg是一步一步地執行被調試程序的,就像動畫[animation]演示一樣,但不會實時刷新窗口,最重要的是它能將地址、寄存器的內容、消息以及已知的操作數記錄到Run跟蹤緩沖區中。如果被調試的代碼是自修改的,你就能夠保存原始的命令。可以通過按Ctrl+F11(Run跟蹤步入,進入子函數)或者 Ctrl+F12(Run跟蹤步過,一次執行完子函數)開始Run跟蹤,并用F12或者Esc鍵停止跟蹤。
你可以指定在Run跟蹤時執行每一步的條件集(快捷鍵:Ctrl+T)。如果條件符合,Run跟蹤將暫停。條件包括:
(1)當EIP在某個地址范圍內時暫停[Pause when EIP is in the address range];
(2)當EIP在某個地址范圍之外時暫停[Pause when EIP is outside the address range];
(3)當某個條件為真時暫停[Pause when some conditionis true];
(4)當下一條指令可疑時暫停[Pause when next commandis suspicious],比如:可能為非法指令(根據在分析3[Analysis 3]中設定的規則而定),訪問不存在的內存,設置了單步陷阱標志[single—step trap flag]或者越ESP界訪問棧。注意:這個選項會明顯地(大約20%)減慢Run跟蹤的速度;
(5)當命令執行達到指定的次數(更確切的說,是添加到Run跟蹤的緩沖區里面的命令數量)時暫停[Pause afterspecified number of commands is traced]。注意計數器不能自動歸零。也就是說,如果你設置指令次數為10,則在第10次執行到該命令時暫停,并不是該命令每執行10次就暫停一次。
(6)當下一條命令符合指定的樣式之一時暫停[Pause when nextcommand matches one of the specified patterns]。你可以使用模糊命令和操作數[imprecise commands and operands]及匹配32位寄存器RA和RB,像R32一樣,這兩個寄存器可以替代任何通用32位寄存器,但是在同一條命令中其值是不能變的。而RA和RB在同一條命令中,則一定是不同的。例如,在程序中含有 XOR EAX,EAX;XOR ESI,EDX 兩條命令,兩條命令均符合樣式XOR R32,R32;第一條命令符合樣式XORRA,RA;而等二條命令 XORESI,EDX 符合樣式XOR RA,RB。
毫無疑問,Run跟蹤需要足夠的內存,每條命令平均需要占用16到35字節,同時速度也非常慢。在500—MHZ處理器、Windows NT環境下,它每秒能跟蹤5000條指令。Windows95更慢:每秒鐘僅2200條指令。但是在許多情況下,例如當一個程序跳轉到不存在的地址的時候,這是找到原因的唯一方法。你可以在Run 跟蹤時將準線性命令序列(即序列尾部只有唯一出口)跳過。當OllyDbg遇到這些需跳過的命令序列時,會設置一個臨時斷點,然后跟進到序列中,并一次運行完。當然了,如果排除命令中返回或跳轉的地址在跟蹤范圍之外,將可能導致跟蹤發生錯誤;因此OllyDbg會檢查你想跳過的代碼塊,如果存在上述情況,會向你詢問。
在大多數情況下,你對跟蹤系統API代碼不感興趣。跟蹤選項總是跟過系統DLL[Always traceover system DLLs]允許你在 跟蹤/自動模式下跟過API函數。如果模塊在系統目錄下,OllyDbg就假設該模塊是系統的。你可以在模塊[Modules]窗口中標記任意DLL是系統的或者非系統的。
為了使執行速度更快,你可以通過設置Run跟蹤斷點,先將Run跟蹤限制在選定的命令或代碼塊上,然后再運行程序。我把這種做法稱作“強迫Run跟蹤”。一般來說,刪除Run跟蹤斷點不會移除Hit跟蹤斷點。但如果你刪除了hit跟蹤斷點,同時你也移除了Run跟蹤斷點。
跟蹤命令會保存到跟蹤緩沖區中,這個緩沖區在跟蹤開始時自動創建。你可以在選項中指定它的大小(最高64MB)。這個緩沖區是循環隊列,當滿了的時候,會丟棄老的記錄。你可以通過從OllyDbg主菜單中選擇“調試[Debug]—>打開或者清除Run跟蹤[Open or clear run trace]”,來打開或者清除Run跟蹤緩沖區。在Run跟蹤緩沖區打開后,OllyDbg 會記錄在執行過程中的所有暫停,甚至那些不是由Run跟蹤引起的暫停。例如,你可以通過按 F7 或者 F8 單步執行程序,然后通過使用+鍵和—鍵來反方向跟蹤程序的執行。注意:如果Run跟蹤緩沖區已經關閉,則用這些鍵瀏覽的是歷史[history]記錄。在你查看Run跟蹤記錄時,寄存器和信息面板會變灰,來強調它們所顯示的寄存器并不是實際的寄存器。跟蹤緩沖區并不保存棧頂或由寄存器所指向的內容。寄存器、信息和棧在Run跟蹤的時候使用實際的內存狀態來解釋寄存器的變化。
OllyDbg能夠記下每個指令在Run跟蹤緩沖區里面出現的次數。在反匯編窗口快捷菜單中,選擇是“查看[View]—>統計作為注釋[Profile as comments]”。這個命令使用統計取代了注釋欄。或者,如果列標題欄可見,則可以單擊它幾次直到它顯示統計信息。注意顯示出來的數字是動態的,而且不計算已經從跟蹤緩沖區中丟棄的指令。你還可以在單獨的統計窗口[Profile window]中,按觸發次數排序,來查看整個模塊的統計數據。
在反匯編窗口的快捷菜單中選擇“Run跟蹤[Run trace]—>添加到所有函數入口處[Add entries of all procedures]”,這樣能夠檢查每個可識別的函數被調用的次數。另一個命令“Run跟蹤[Run trace]—>添加到函數中所有的分支[Add branches in procedure]”會強行跟蹤此函數中所有識別的跳轉目的地址的內容。在這種情況下,統計功能能夠找到最頻繁執行的分支,你可以優化這部分的代碼,以提高速度。
在反匯編窗口中的某條命令上使用快捷菜單中選擇“搜索[Search for]—>Run跟蹤的最新記錄[Last record in run trace]”用于查找該命令是否被執行過,如果執行過,最后一次執行在哪里。
Run跟蹤窗口顯示跟蹤緩沖區的內容。對每個指令來說包括被指令改變的整數寄存器的內容(更準確的說是給定的記錄變成下一條記錄的變化)。如果你雙擊某條指令,窗口會選擇在跟蹤緩沖區里全部含有該命令的記錄,而且你可以通過按+和—鍵來快速的瀏覽;如果你在調試選項[Debugging options]中設置了 “跟蹤[Trace]—>同步CPU和Run跟蹤[Synchronize CPU and Run trace]”,雙擊記錄則會跟進到對應的反匯編窗口中位置。
注意:當你退出Hit跟蹤時,你同時也強行退出了Run跟蹤。
?
十七、快捷鍵
1、通用快捷鍵[Global shortcuts]
無論當前的OllyDbg窗口是什么,這些快捷鍵均有效:
Ctrl+F2—重啟程序,即重新啟動被調試程序。如果當前沒有調試的程序,OllyDbg會運行歷史列表[history list]中的第一個程序。程序重啟后,將會刪除所有內存斷點和硬件斷點。譯者注:從實際使用效果看,硬件斷點在程序重啟后并沒有移除。
Alt+F5—讓OllyDbg總在最前面。如果被調試程序在某個斷點處發生中斷,而這時調試程序彈出一個總在最前面的窗口(一般為模式消息或模式對話框[modal message or dialog]),它可能會遮住OllyDbg的一部分,但是我們又不能移動最小化這個窗口。激活OllyDbg(比如按任務欄上的標簽)并按Alt+ F5,OllyDbg將設置成總在最前面,會反過來遮住剛才那個窗口。如果你再按一下Alt+F5,OllyDbg會恢復到正常狀態。OllyDbg是否處于總在最前面狀態,將會保存,在下一次調試時依然有效。當前是否處于總在最前面狀態,會顯示在狀態欄中。
F4—運行到選定位置。作用就是直接運行到光標所在位置處暫停。
F7—單步步入到下一條命令,如果當前命令是一個函數[Call],則會停在這個函數體的第一條命令上。如果當前命令是是含有REP前綴,則只執行一次重復操作。
Shift+F7—與F7相同,但是如果被調試程序發生異常而中止,調試器會首先嘗試步入被調試程序指定的異常處理(請參考忽略Kernel32中的內存非法訪問)。
Ctrl+F7—自動步入,在所有的函數調用中一條一條地執行命令(就像你按住F7鍵不放一樣,只是更快一些)。當你執行其他一些單步命令,或者程序到達斷點,或者發生異常時,自動步入過程都會停止。每次單步步入,OllyDbg都會更新所有的窗口。所以為了提高自動步入的速度,請你關閉不必要成窗口,對于保留的窗口最好盡量的小。按Esc鍵,可以停止自動步入。
F8—單步步過到下一條命令。如果當前命令是一個函數,則一次執行完這個函數(除非這個函數內部包含斷點,或發生了異常)。如果當前命令是含有REP前綴,則會執行完重復操作,并停在下一條命令上。
Shift+F8—與F8相同,但是如果被調試程序發生異常而中止,調試器會首先嘗試步過被調試程序指定的異常處理(請參考忽略Kernel32中的內存非法訪問)。
Ctrl+F8—自動步過,一條一條的執行命令,但并不進入函數調用內部(就像你按住F8鍵不放一樣,只是更快一些)。當你執行其他一些單步命令,或者程序到達斷點,或者發生異常時,自動步過過程都會停止。每次單步步過,OllyDbg都會更新所有的窗口。所以為了提高自動步過的速度,請你關閉不必要成窗口,對于保留的窗口最好盡量的小。按Esc鍵,可以停止自動步過。
F9—讓程序繼續執行。
Shift+F9—與F9相同,但是如果被調試程序發生異常而中止,調試器會首先嘗試執行被調試程序指定的異常處理(請參考忽略Kernel32中的內存非法訪問)。
Ctrl+F9—執行直到返回,執行程序直到遇到返回,在此期間不進入子函數也不更新CPU數據。因為程序是一條一條命令執行的,所以速度可能會慢一些。按Esc鍵,可以停止跟蹤。
Alt+F9—執行直到返回到用戶代碼段,執行程序直到指令所屬于的模塊不在系統目錄中,在此期間不進入子函數也不更新CPU數據。因為程序是一條一條執行的,所以速度可能會慢一些。按Esc鍵,可以停止跟蹤。
Ctrl+F11—Run跟蹤步入,一條一條執行命令,進入每個子函數調用,并把寄存器的信息加入到Run跟蹤的存儲數據中。Run跟蹤不會同步更新CPU窗口。
F12—停止程序執行,同時暫停被調試程序的所有線程。請不要手動恢復線程運行,最好使用繼續執行快捷鍵或菜單選項(像 F9)。
Ctrl+F12—Run跟蹤步過,一條一條執行命令,但是不進入子函數調用,并把寄存器的信息加入到Run跟蹤的存儲數據中。Run跟蹤不會同步更新CPU窗口。
Esc—如果當前處于自動運行或跟蹤狀態,則停止自動運行或跟蹤;如果CPU顯示的是跟蹤數據,則顯示真實數據。
Alt+B—顯示斷點窗口。在這個窗口中,你可以編輯、刪除、或跟進到斷點處。
Alt+C—顯示CPU窗口。
Alt+E—顯示模塊列表[listof modules]。
Alt+K—顯示調用棧[Callstack]窗口。
Alt+L—顯示日志窗口。
Alt+M—顯示內存窗口。
Alt+O—顯示選項對話框[Optionsdialog]
Ctrl+P—顯示補丁窗口。
Ctrl+T—打開“暫停Run跟蹤”對話框
Alt+X—關閉 OllyDbg。
2、大多數窗口都支持以下的鍵盤命令
Alt+F3—關閉當前窗口。
Ctrl+F4—關閉當前窗口。
F5—最大化當前窗口或將當前窗口大小改為正常化。
F6—切換到下一個窗口。
Shift+F6—切換到前一個窗口。
F10—打開與當前窗口或面板相關的快捷菜單。
左方向鍵—顯示窗口左方一個字節寬度的內容。
Ctrl+左方向鍵—顯示窗口左方一欄的內容。
右方向鍵—顯示窗口右方一個字節寬度的內容
Ctrl+右方向鍵—顯示窗口右方一欄的內容
3、反匯編窗口中的快捷鍵[Disassembler shortcuts]
當CPU窗口中的反匯編面板[Disassemblerpane]處于激活狀態時,你可以使用以下快捷鍵:
回車鍵—將選中的命令添加到命令歷史[command history]中,如果當前命令是一個跳轉、函數或者是轉換表的一個部分,則進入到目的地址。
退格鍵—移除選中部分的自動分析信息。如果分析器將代碼誤識別為數據,這個快捷鍵就非常有用。請參考解碼提示[decoding hints].
Alt+退格鍵—撤消所選部分的修改,以備份數據的相應內容替換所選部分。僅當備份數據存在且與所選部分不同時可用。
Ctrl+F1—如果API幫助文件已經選擇,將打開與首個選擇行內的符號名相關聯的幫助主題。
F2—在首個選擇的命令上開關INT3 斷點[Breakpoint],也可以雙擊該行第二列。
Shift+F2—在首個選擇命令設置條件斷點,參見忽略Kernel32中內存訪問異常[Ignore memory accessviolations in Kernel32]。
F4—執行到所選行,在首個選擇的命令上設置一次性斷點,然后繼續執行調試程序,直到OllyDbg捕獲到異常或者停止在該斷點上。在程序執行到該命令之前,該一次性斷點一直有效。如有必要,可在斷點窗口[Breakpoints window]中刪除它。
Shift+F4—設置記錄斷點(一種條件斷點,當條件滿足時一些表達式的值會記錄下來), 詳情參見斷點[Breakpoint]。
Ctrl+F5—打開與首個選擇的命令相對應的源文件。
Alt+F7—轉到上一個找到的參考。
Alt+F8—轉到下一個找到參考。
Ctrl+A—分析當前模塊的代碼段。
Ctrl+B—開始二進制搜索。
Ctrl+C—復制所選內容到剪貼板。復制時會簡單地按列寬截斷不可見內容,如果希望排除不需要的列,可把這些列的寬度調整到最小。
Ctrl+E—以二進制(十六進制)格式編輯所選內容。
Ctrl+F—開始命令搜索。
Ctrl+G—轉到某地址。該命令將彈出輸入地址或表達式的窗口。該命令不會修改 EIP。
Ctrl+J—列出所有的涉及到該位置的調用和跳轉,在你用這個功能之前,你必須使用分析代碼功能。
Ctrl+K—查看與當前函數相關的調用樹[Call tree]。在你用這個功能之前,你必須使用分析代碼功能。
Ctrl+L—搜索下一個,重復上一次的搜索內容。
Ctrl+N—打開當前模塊的名稱(標簽)列表。
Ctrl+O—掃描object文件。掃描Object文件。該命令會顯示掃描Object文件對話框,你可以在該對話框中選擇Object文件或者lib文件,并掃描這個文件,試圖找到在實際代碼段中用到的目標模塊。
Ctrl+R—搜索所選命令的參考。該命令掃描激活模塊的全部可執行代碼,以找到涉及到首個選中的命令的全部相關參考(包括:常量、跳轉及調用),你可以在參考中使用快捷鍵 Alt+F7 和 Alt+F8來瀏覽這些參考。為便于你使用,被參考的命令也包含在該列表中。
Ctrl+S—命令搜索。該命令顯示命令查找[Find command]對話框供你輸入匯編命令,并從當前命令開始搜索。
星號[Asterisk](*)—轉到原始位置(激活線程的EIP處)。
Ctrl+星號(*)—指定新的起始位置,設置當前所選線程的EIP為首個選擇字節的地址。你可以在選擇EIP并撤消該操作。
加號[Plus](+)—如果run跟蹤[run trace] 沒有激活,則根據命令歷史[command history]跳到下一條運行過命令的地方;否則跳到Run跟蹤的下一個記錄。
Ctrl+加號—跳到前一個函數的開始處。(注意只是跳到,并不執行)
減號[Minus](—)—如果run跟蹤[run trace] 沒有激活,則根據命令歷史[command history]跳到前一條運行過命令的地方;否則跳到Run跟蹤的前一個記錄。
Ctrl+減號—跳到下一個函數的開始處。(注意只是跳到,并不執行)
空格[Space]—修改命令。你可在顯示對話框中以匯編語言修改實際指令或輸入新指令,這些指令將替換實際代碼,你也可以在想要修改的指令處雙擊鼠標。
冒號[Colon](:)—添加標簽。顯示添加標簽窗口[Add label]或修改標簽窗口[Change label],你可在此輸入與首個選擇的命令中的第一個字節相關聯的標簽(符號名)。注意,在多種編程語言中,冒號可以是標簽的一部分。
分號[Semicolon](;)—添加注釋[comment]。顯示添加注釋窗口[Add label]或修改注釋窗口[Change label],你可在此輸入與首條所選命令的第一個字節相關聯的注釋(注釋串會顯示在最后一列中)。注意,多種匯編語言使用分號作為注釋開始。你也可以在注釋列雙擊需要注釋的命令行。
十八、插件[Plugins]
插件是一個DLL,存放在OllyDbg的目錄中,用于增加OllyDbg的功能。你可以從OllyDbg的主頁上(http://home.t—online.de/home/Ollydbg)免費下載插件開發工具包plug110.zip。
插件可以設置斷點,增加標簽和注釋,修改寄存器和內存。插件可以添加到主菜單和很多的窗口(比如反匯編窗口、內存窗口)的快捷菜單中,也可以攔截快捷鍵。插件還可以創建MDI(多文檔界面)窗口。插件還可以根據模塊信息和OllyDbg.ini文件,將自己數據寫到.udd文件中;并能讀取描述被調試程序的各種數據結構。插件API包含了多達170個函數。
許多第三方插件都可以從Internet網上獲得,比如由網友TBD創建并維護的OllyDbg的論壇(http://ollydbg.win32asmcommunity.net)。
安裝插件的方法:將DLL復制到插件目錄[plugin directory]中,然后重新啟動Ollydbg。默認情況下,這個插件目錄為ollydbg.exe文件所在的目錄。
現在的版本中已經包含了兩個“原始”插件: 書簽[Bookmark]和命令行[Command line]. 他們的源代碼都保存在plug110.zip.文件中。這些插件都是免費的,你可以任意修改或使用它們。
十九、技巧提示[Tips and tricks]
(1)OllyDbg 可以作為二進制編輯器使用。選擇視圖[View]→文件[File]并選定需要查看的文件。文件不能大于剩余內存數量。
(2)假使你修改了內存中的執行文件,這時你想恢復修改的部分,但是你忘記哪里被修改了,你可以把原始文件當作備份進行加載,這樣你就可以找到修改的部分了。
(3)分析前,先掃描 OBJ 文件。這時OllyDbg會對已知 C 函數的參數進行解碼。
(4)一些表格中包含了隱藏數據。可以通過增加列寬來顯示出來。
(5)所有數據窗口(包括反匯編窗口),可以通過雙擊顯示相對的地址。
(6)你可以通過 Ctrl +↑ 或 Ctrl+↓ 對數據窗口翻動一個字節。
第四章 其他功能
一、調試獨立的DLL[Debugging of stand—alone DLLs]
打開DLL,也可以直接將其從資源管理器拖放到OllyDbg上。OllyDbg 會詢問你并將該文件的全路徑作為參數傳遞給loaddll.exe。然后鏈接庫被加載并停在代碼的入口(<DllEntryPoint>)。你可以設置斷點,運行或跟蹤啟動代碼,等等。在初始化完成后,應該程序會再次暫停。這次停在標簽名為 Firstbp 的位置,其在立即進入主消息循環之前。
現在,你可以調用DLL函數。從主菜單選擇“調試[Debug]—>調用DLL輸出[Call DLL export]”。這時會彈出一個對話框。由于這個對話框是無模式對話框,因此你仍然能夠使用OllyDbg的全部功能,比如查看代碼、數據,查看斷點,修改內存等等。
選擇你想調用的函數。例如我們將開始使用 USER32.DLL 里的MessageBox 函數。注意loaddll.exe 已經使用了這個鏈接庫,因此會假定這個 DLL 已經初始化而不再調用入口。MessageBox這個函數名是通用函數名,事實上,這個函數有處理 ASCII 的 MessageBoxA 和處理 Unicode 的MessageBoxW 兩種。我們繼續往下看:
?
在我們選擇這個函數后,右邊的消息框中會出現 Number of arguments: 4(有四個參數)的字樣。OllyDbg 會根據函數尾部的RET 10語句來正確識別參數的數量。RETnnn 是使用PASCAL調用約定的函數的典型特征。(參數被放入棧中,第一個參數會被最后一個壓入棧中,函數調用完畢后,參數會被遺棄)。大多數的 WindowsAPI 函數都是PASCAL形式的。
下一步,我們要設定棧中參數的個數。在這個例子中,不必做進行這個操作,因為OllyDbg已經知道了MessageBoxW函數的參數數量。但是,如果你愿意的話,也可以單擊左邊的復選框,改變成你認為合適的參數數量。
現在填寫參數列表。這個對話框中支持至多10個參數. 參數可以是任何有效的表達式,而不必使用寄存器。如果操作數指向了內存,則參數右邊的緩沖區窗口會顯示內存中的數據。Loaddll.exe 有10個大小為1K的緩沖區,這些緩沖區被標記為Arg1 .. Arg10,,你可以方便自由的使用它們。另外,對話框還支持兩個偽變量:由loaddll.exe創建的父窗口句柄<Hwnd>,以及loaddll的實例句柄<Hinst>。為了方便你的使用,在你第一次使用調用輸出函數時,OllyDbg就已經將這兩個偽變量加到了歷史列表中去了。
MessageBoxW e函數需要4個參數:
l? 父窗口句柄。 這里我們選擇<Hwnd> ;handle of owner window. Here, we simply select <Hwnd>;
l? 在消息框中UNICODE文本的地址。選擇Arg2并按回車。緩沖區窗口會以16進制的格式顯現內存中的緩沖區。這個緩沖區初始化全是0。點擊第一個字節,并按快捷鍵Ctrl+E(另外, 也可以從菜單中選擇“二進制[Binary]—>編輯[Edit]”)。這時會出現一個對話框,在對話框中鍵入“Text in box”或者其他希望顯示的字符串;
l? 消息框標題的UNICODE文本的地址。選擇Arg3并在Unicode格式的內存中寫上“Box title”;
l? 消息框的風格。使用常量MB_xxx進行組合.OllyDbg 可以識別這些常量。在這里我們鍵入:MB_OK—>MB_ICONEXCLAMATION。
這里不需要寄存器參數。
現在我們準備調用輸出函數。選項“在調用時隱藏[Hide oncall]”意思是說,當函數運行時對話框將會從屏幕消失。當我們執行一個會運行很長時間的函數,或者設置了斷點的時候,這個選項非常的有用。你也可以手動關閉對話框。當函數執行完畢后,OllyDbg會重新自動打開。“調用輸出函數”對話框。選項“在調用后暫停[Pause after call]”意思是說,在執行完函數后,loaddll將會被暫停。
按“調用[Call]按鈕”后,OllyDbg 會自動備份所有的內存、校驗、參數、寄存器等信息。并隱藏對話框,然后調用 MessageBoxW 函數。和期望的一樣,消息框在屏幕中出現了:
函數 MessageBoxW 不會修改參數。如果你調用的函數更新了內存,比如函數 GetWindowName,修改的字節將會在數據區里高亮。注意:EAX 返回值為1,表示成功。
其他的例子請訪問我的網站:http://home.t—online.de/home/Ollydbg/Loaddll.htm
不幸的是,你不能通過這種方式調試OllyDbg的插件,插件關聯到ollydbg.exe文件,Windows系統不能在同一個應用程序里加載并運行兩個可執行文件。
二、解碼提示[Decoding hints]
在某些情況下,分析器不能區分代碼和數據。讓我們看看下面的例子:
const char s[11] = "0123456789";
...for (i=0x30; i<0x3a; i++) t[i—0x30]=s[i—0x30];
好的編譯器將會將上面的代碼優化成如下樣子: e
for (i=0x30; i<0x3a; i++)(t—0x30)=(s—0x30);
這里t—0x30 和 s—0x30 都是常量,并編譯成如下形式:
MOV AL,[BYTE s_minus_30+EBX]
MOV [BYTE t_minus_30+EBX],AL
編譯器也可能將常量字符串"0123456789"插入到執行代碼中。在1.10版本中,我打算用寄存器的值來決定是否的數據或代碼。當遇到上面的命令,分析器將假定地址s_minus_30處包含字符數據。但事實上,可能那里是代碼。
萬一出現上述問題,我們應該怎么辦呢?有兩種辦法:最快最笨的辦法是:將分析錯誤的部分刪除(快捷鍵:退格鍵),這樣OllyDbg將使用默認的反匯編器進行解碼。
更好的辦法是使用解碼提示[decoding hints]。你可以告訴OllyDbg如何解釋選中的內存內容。這種方法在重新分析(Ctrl+A)時,解釋依然有效。
設置提示的方法:在反匯編窗口中,選中需要修正提示的代碼或數據,然后在快捷菜單中選擇分析[Analysis]—>在下次分析時,將選擇部分視為[During next analysis, treat selection as]。選擇以下選項之一:
命令[Command]—第一個被選中的字節開始的有效命令。這條命令,還有所有后面的部分,直到有Jump或Return命令出現,以及含有Jump或Call命令所到達位置的部分,都會被視為命令;
字節[Byte],字[Word],雙字[Doubleword]—選中的前1、2、4字節視為對應大小的數據;
所有選中命令[Commands]—全部選中部分(直到第一個無效命令)和可以到達由有效命令集組成的目的地址;
字節[Bytes],字[Words],雙字[Doublewords],—全部選中部分以1、2、或 4字節分組;
ASCII字符串[ASCII text], UNICODE字符串[UNICODE text]—全部選中部分為ASCII 或 UNICODE 字符串;
默認(移除提示)[Default (removehints)]—從選中部分中移除全面提示;
移除全部提示[Remove all hints]—從全部模塊中移除解碼提示。
OllyDbg 保存提示到.udd文件中。
?
三、表達式賦值[Evaluation of expressions]
OllyDbg能夠支持非常復雜的表達式。表達式的語法格式將在這個主題的后面進行介紹,但我想你對此不一定真的感興趣。那么我先舉幾個實例來說明:
10—常量 0x10 (無符號)。所有整數常量都認為是十六進制的,除非后面跟了點;
10.—十進制常量10(帶符號);
'A'—字符常量 0x41;
EAX—寄存器EAX的內容,解釋為無符號數;
EAX.—寄存器EAX的內容,解釋為帶符號數;
[123456]—在地址123456處的無符號雙字內容。默認情況,OllyDbg假定是雙字長操作數;
DWORD PTR [123456]—同上。關鍵字 PTR 可選;
[SIGNED BYTE123456]—在地址123456處帶符號單字節。OllyDbg支持類MASM和類IDEAL兩種內存表達式;
STRING [123456]—以地址123456作為開始,以零作為結尾的ASCII字符串。中括號是必須的,因為你要顯示內存的內容;
<123456>—在地址123456處存儲的雙字所指向的地址內的雙字內容;
2+3*4—值為14。OllyDbg 按標準C語言的優先級進行算術運行;
(2+3)*4—值為20。使用括號改變運算順序。
EAX.<0.—如果EAX在0到0x7FFFFFFF之間,則值為0,否則值為1。注意0也是有符號的。當帶符號數與無符號數比較時,OllyDbg會將帶符號數轉成無符號數。
EAX<0—總為0(假),因為無符號數永遠是正的。
MSG==111—如果消息為WM_COMMAND,則為真。0x0111是命令 WM_COMMAND 的數值。MSG只能用于設置在進程消息函數的條件斷點內。
[STRING123456]=="Brown fox"—如果從地址0x00123456開始的內存為ASCII字符串"Brownfox"、"BROWN FOX JUMPS"、 "brown fox???",或類似的串,那么其值為1。比較不區分大小寫和文本長度。
EAX=="Brownfox"—同上,EAX按指針對待。
UNICODE[EAX]=="Brown fox"—OllyDbg認為EAX是一個指向UNICODE串的指針,并將其轉換為ASCII,然后與文本常量進行比較。
[ESP+8]==WM_PAINT—i在表達式中你可以使用上百種Windows API符號常量。
([BYTE ESI+DWORDDS:[450000+15*(EAX—1)> & 0F0)!=0—這絕對是個有效的表達式。
現在我們介紹語法格式。在大括號({})內的每個元素都只能出現一次,括號內的元素順序可以交換:
表達式 = 內存中間碼|內存中間碼<二元操作符>內存中間碼
內存中間碼 = 中間碼 | ?{ 符號標志 大小標志 前綴} [表達式 ]
中間碼 =(表達式) | 一元操作符 內存中間碼 | 帶符號寄存器 | 寄存器 | FPU寄存器 | 段寄存器 | 整型常量 | 浮點常量 | 串常量 | 參數 | 偽變量
一元操作符 = ! | ?~ | ?+ | ?
帶符號寄存器 = 寄存器.
寄存器 = AL | BL |CL ... | AX | ?BX | CX... | ?EAX | EBX | ECX...
FPU寄存器 = ST | ST0 | ST1 ...
段寄存器 = CS | DS | ES | SS| FS | GS
整型常量 = <十進制常量>. | <十六進制常量> | <字符常量> | <API符號常量>
浮點常量 = <符點常量>
串常量 = "<串常量>"
符號標志 = SIGNED | UNSIGNED
大小標志 = BYTE | CHAR | WORD| SHORT | DWORD | LONG | QWORD | FLOAT | DOUBLE | FLOAT10 | STRING | UNICODE
前綴 = 中間碼:
參數 = %A | %B?????? // 僅允許在監察器[inspector] 中使用
偽變量 = MSG?????? // 窗口消息中的代碼
這個語法并不嚴格。在解釋[WORD [EAX]]或類似的表達式時會產生歧義。可以理解為以寄存器EAX所指向地址的兩字節內容為地址,所指向的雙字內容;也可以理解為以寄存器EAX所指向地址的四字節內容為地址,所指向的兩字節內容。而OllyDbg會將修飾符盡可能的放在地址最外面,所以在這種情況下,[WORD [EAX]]等價于 WORD 。
默認情況下,BYTE、WORD 和 DWORD 都是無符號的,而CHAR、SHORT 和 LONG都是帶符號的。也可以使用明確的修飾符SIGNED 或 UNSIGNED。例如在二元操作時,如果一個操作數是浮點的,那么另外一個就要轉成浮點數;或者如果一個是無符號膽,那么另外一個要轉成無符號的。浮點類型不支持UNSIGNED。大小修飾符后面跟MASM兼容關鍵字PTR(如:BYTE PTR)也允許的,也可以不要PTR。寄存器名和大小修飾符不區分大小寫。
你可以使用下面類C的運算符(0級最高):
優先級?????? 類型?????? 運算符
0?????? 一元運算符???? ? ! ~+—
1?????? 乘除運算?????? ? */ %
2?????? 加減運算?????? ? +—
3?????? 位移動?????? ????? <<>>
4?????? 比較?????? ???????? < <= > >=
5?????? 比較?????? ???????? == !=
6?????? 按位與?????? ????? &
7?????? 按位異或?????? ? ^
8?????? 按位或????? ??????? |
9?????? 邏輯與?????? ????? &&
10?????? 邏輯或????? ????? ||
在計算時,中間結果以 DWORD 或 FLOAT10 形式保存。某些類型組合和操作是不允許的。例如:QWODRD 類型只能顯示;STRING 和 UNICODE只能進行加減操作(像C語言里的指針)以及與STRING、UNICODE 類型或串常量進行比較操作;你不能按位移動浮點[FLOAT] 類型,等等。
?
四、自定義函數描述[Custom function descriptions]
1、概論[Introduction]
OllyDbg包含(做為內部資源)1900多種標準函數以及400多種標準C函數的名稱和參數。分析器[Analyzer]用這些描述使被調試程序更加易懂。比較下面一個例子,分析器的函數CreateFont:
????? PUSH OT.00469F2A?????????????? ; ASCII "Times NewRoman"
??????? PUSH 12??????????????????????????
??????? PUSH 2??????????????????????????
??????? PUSH 0?????????????????????????
??????? PUSH 0??????????????????????????
??????? PUSH 0??????????????????????????
??????? PUSH 0???????????????????????
??????? PUSH 0??????????????????????????
??????? MOV EAX,DWORD PTR [49FA70]
??????? PUSH EAX
??????? PUSH 190????????????????????????
??????? PUSH 0
??????? PUSH 0?????????????????????????
??????? PUSH 0????????????????????????
??????? PUSH 10???????????????????????
??????? CALL <JMP.&GDI32.CreateFontA>
這是分析后的:
????? MOV EAX,DWORD PTR[49FA70]????
??????? PUSH OT.00469F2A??????? ???????? ???????? ; ?/ FaceName = "Times New Roman"
??????? PUSH 12???????????????????? ? ; ?| PitchAndFamily =VARIABLE_PITCH—>FF_ROMAN
??????? PUSH 2????????????????????? ? ; ?| Quality = PROOF_QUALITY
??????? PUSH 0???????????????????? ??? ; ?| ClipPrecision =CLIP_DEFAULT_PRECIS
????? ??PUSH 0???????????????????? ??? ; ?| OutputPrecision =OUT_DEFAULT_PRECIS
??????? PUSH 0?????????????????????? ; ?| CharSet = ANSI_CHARSET
??????? PUSH 0???????????????????? ??? ; ?| StrikeOut = FALSE
??????? PUSH 0????????????????????? ? ; ?| Underline = FALSE
??????? PUSH EAX??????????????????? ???????? ; ?| Italic => TRUE
??????? PUSH 190???????????????????? ; ?| Weight = FW_NORMAL
??????? PUSH 0?????????????????????? ; ?| Orientation = 0
??????? PUSH 0????????????????????? ? ; ?| Escapement = 0
??????? PUSH 0????? ????????????????? ; ?| Width = 0
??????? PUSH 10???????????????????? ? ; ?| Height = 10 (16.)
??????? CALL <JMP.&GDI32.CreateFontA>; ?\ CreateFontA
顯然,后面的代碼更容易理解。API函數CreateFont 有14個參數。分析器標記所有這些參數的名稱并解碼他們的值。如果寄存器跟蹤開啟,那么分析器同時會解碼參數Italic的值為地址49FA70處雙字長的內容。解碼使用參數的真實值,所以如果[49FA70]里的內容改變了,那么參數Italic的值也會隨之改變。當EIP指向跳轉或調用該函數的命令,或指向入口時,OllyDbg也會在棧中對已知函數的參數進行解碼。
OllyDbg可以對像printf()這樣參數個數可變的函數進行參數解碼:
??????? PUSH EAX???????????????????? ?????? ; / <%.*s>
??????? PUSH E8??????????????????????? ???? ; | <*> = E8 (232.)
??????? PUSH EBX?????????????????????? ??? ; | <%08X>
??????? PUSH Mymodule.004801D2???????? ??? ;| format = "Size %08X (%.*s) bytes"
??????? PUSH ESI?????????????????????? ????? ; | s
??????? CALL Mymodule.sprintf????????? ???????? ;\sprintf
你可以定義自己的函數。每次你打開某個應用程序時,OllyDbg都會重新設置函數參數表并用內嵌描述添充這個表。然后嘗試打開文件“< OllyDbg目錄>\common.arg”和“<OllyDbg目錄>\<應用程序名>.arg”,這里<應用程序名>使用8.3格式(DOS)被調試程序文件名(不帶路徑和擴展名)。
下面看一個簡單的.arg文件實例:
??????? INFO Simple .ARG file that decodesCreateHatchBrush
??????? TYPE HS_X
????????? IF 0 "HS_HORIZONTAL"
????????? IF 1 "HS_VERTICAL"
????????? IF 2 "HS_FDIAGONAL"
????????? IF 3 "HS_BDIAGONAL"
????????? IF 4 "HS_CROSS"
????????? IF 5 "HS_DIAGCROSS"
????????? ELSEINT
??????? END
??????? TYPE COLORREF
????????? IF 0 "<BLACK>"
????????? IF 00FFFFFF "<WHITE>"
????????? OTHERWISE
????????? TEXT "RGB("
????????? FIELD 000000FF
????????? UINT
????????? TEXT ","
????????? FIELD 0000FF00
????????? UINT
????????? TEXT ","
????????? FIELD 00FF0000
????????? UINT
????????? TEXT ")"
??????? END
??????? STDFUNC CreateHatchBrush
???????? ?"style" HS_X
????????? "colorref" COLORREF
??????? END
標準Windos API函數CreateHatchBrush(int style,int colorref) 有兩個參數。第一個必須是陰影風格[hatch style],第二個是常量由紅色、綠色、藍色組成,并用一個32位整數的低三字節表示。為了解碼這些參數,文件定義了兩個新的參數類型:HS_X和 COLORREF。
陰影風格是一個簡單的枚舉類型,如0表示HS_HORIZONTAL(水平風格)、1表示HS_VERTICAL(垂直風格)。IF關鍵字比較參數與第一個操作數(注意:其總是十六進制的),如果相同則顯示第二個操作數里的文本。但萬一匹配失敗會如何?關鍵字ELSEINT 會然OllyDbg會將參數解釋為一個整數。
COLORREF 更復雜一些。首先嘗試解碼兩個廣泛使用的顏色值:黑(全0組成)與白(全0xFF組成)。如果匹配失敗,COLORREF嘗試解碼顏色為一個結構包含紅、綠、藍的亮度。FIELD會用第一個操作數與參數進行邏輯與操作。然后轉換結果為整數,并同時按位右移第一個操作及該整數,直到第一個操作數的二進制個位數字為1,這時整數按位右移的結果以無符號10進制顯示出來。這個例子做了三次這樣的操作,以分離出每個顏色成份。TEXT關鍵字用于無條件顯示文本。如果參數為00030201,那么COLORREF將其解碼為RGB(1.,2.,3.)。
大多斷API函數都會從棧中移除參數并保護寄存器EBX, EBP, ESI 和 EDI。聲明這樣的函數為STDFUNC,以告訴分析器該函數做了這樣的事情。否則請其描述為FUNCTION。
萬一某個參數由多個域及比特值組成,比如上面提到的fdwPitchAndFamily ,我們該怎么辦?請看下面這個例子:
?????? ?TYPE FF_PITCH
????????? MASK 03
????????? IF 00 "DEFAULT_PITCH"
????????? IF 01 "FIXED_PITCH"
????????? IF 02 "VARIABLE_PITCH"
????????? ELSEHEX
????????? TEXT "—>"
????????? MASK 0C
????????? BIT 04 "4—>"
????????? BIT 08 "8—>"
????????? MASK FFFFFFF0
????????? IF 00 "FF_DONTCARE"
????????? IF 10 "FF_ROMAN"
????????? IF 20 "FF_SWISS"
????????? IF 30 "FF_MODERN"
????????? IF 40 "FF_SCRIPT"
????????? IF 50 "FF_DECORATIVE"
????????? ELSEHEX
??????? END
前兩個比特位(第0和等1位)表示傾斜度,必須一起解碼。我們使用 MASK 03 來提取這兩個比特并通過IF序列來解碼。增加了連接符“—>”,分別提取第2和第3個比特位,并分別單獨解碼。最后提取剩余部分并進行解碼。
OllyDbg 會移除生成串尾部的連接符“—>”、空格、冒號、逗號、分號和等號。
目前版本的分析僅能夠解碼32位參數。如你不能解碼雙精度浮點或長雙精度浮點的函數參數。
2、格式描述
自定義解碼信息由函數描述和類型描述兩部分組成。函數描述部分非常的簡單:
FUNCTION|STDFUNC [模塊名]函數名
? <第一個參數的名稱> <第一個參數的類型>
? ……
? <最后一個參數的名稱> <最后一個參數的類型>
END
如果函數從棧中移除參數并保護寄存器EBX, EBP, ESI 和 EDI,請使用關鍵字STDFUNC。大多少函數都遵循這樣的規則。其他情況則聲明為FUNCTION。模塊(EXE 或 DLL)名是可選的。如果模塊名被忽略,OllyDbg會對嘗試匹配任何模塊。模塊名不區分大小寫。
函數名稱總是區分大小寫的。有針對UNICODE的函數必須使用后綴 A 或 W 加以區分,比如SetWindowTextA.。
參數的順序又C風格的參數使用慣例一致。而16位Windows和32位API函數也是按慣例使用。如果參數名由多個字組成,或者包含特殊字符,那么請將其用兩個單引號引起來。與在C語言中一樣,省略號()是一個特殊的記錄用于表示參數數目可變。它必須在函數描述的最后。OllyDbg不會嘗試解碼這樣的參數。如果函數的參數為空,則按functionname(void)對待。
OllyDbg 僅支持32位的參數。某些參數已經預定義好了:
INT??? ???????? ???????? 以十六進制和帶符號整數兩種格式顯示值
UINT ???????? ???????? 以十六進制和無符號整數兩種格式顯示值
HEX? ???????? ???????? 以十六進制格式顯示值
BOOL ?????? ???????? TRUE或 FALSE
CHAR?????? ??? ASCII字符
WCHAR?????? UNICODE字符
FLOAT?????? ?? 32位浮點數
ERRCODE??? ?? 系統錯誤代碼(像由函數GetLastError()報告的)
ADDR, PTR?? ? 地址(特殊情況:NULL)
ASCII??????? ?? ASCII串指針
UNICODE???? UNICODE串指針
FORMAT???? ?? 在類似函數printf()(不包括wscanfW()!)使用的 ASCII 格式串
WFORMAT??? 類似函數wsprintfW()(不包括scanf()!)使用的 UNICODE 格式串
RECT?????? ???? RECT(矩形)結構指針
MESSAGE??? ? MSG(ASCII 窗口消息)結構指針
WMESSAGE? ?? MSG(UNICODE 窗口消息)結構指針
HANDLE???? ?? 句柄(特殊情況:NULL,ERROR_INVALID_HANDLE)
HWND?????? ?? 窗口句柄
HMODULE?? ?? 模塊句柄
RSRC_STRING 帶索引的資源串
NULL, DUMMY 有參數,但解碼時跳過了
你不能重定義預定義類型。自定義類型允許你將參數分離成幾個域并分別解碼。類型描述有以下幾種格式:
TYPE 類型名
? [TEXT "任何文本"]
? [<域選擇器>]
? <域解碼>
? <域解碼>
? [TEXT "任何文本"]
? [PURGE]
? ...
? <域選擇器>
? <域解碼>
? <域解碼>
? [TEXT "任何文本"]
END
類型名的程度限制在16個字符以內。 OllyDbg會無條件將"任何文本"作為生成的解碼。域選擇器提取一部分參數用于解碼。以下域選擇器,可以用于提取域:
MASK 十六進制掩碼—域等于參數同十六進制掩碼按位與(AND)的結果。
FIELD 十六進制掩碼—參數同十六進制掩碼按位與(AND)的數值,然后OllyDbg同時按位右移掩碼和計算的數值直到掩碼的二進制個位為1,這時數值按位右移的結果就是域的值。例如參數0xC250, FIELD F0,得到的結果是5。
SIGFIELD十六進制掩碼—參數同十六進制掩碼按位與(AND)的數值,然后OllyDbg同時按位右移掩碼和計算的數值直到掩碼的二進制個位為1,這時數值按位右移的結果轉成帶符號32位數就是域的值。例如參數0xC250 ,SIGFIELD FF00,得到的結果是0xFFFFFFC2。
簡單域的解碼會一次顯示整個域的內容:
HEX—以十六進制形式顯示域內容;
INT—以帶符號十進制形式顯示域內容(帶小數點);
UINT—以無符號十進制形式顯示域內容(帶小數點);
CHAR—以 ASCII 字符形式顯示域內容。
域若是一個枚舉類型,則可以使用IF序列,如果必要的話還可以在IF序列后跟關鍵字 TRYxxx 與 ELSExxx:
IF 十六進制值 "文本"—如果域等于十六進制值,則將文本作為輸出字符串;
TRYASCII—如果域是一個指向ASCII串的指針,則顯示這個串;
TRYUNICODE—如果域是一個指向UNICODE串的指針,則顯示這個串;
TRYORDINAL—如果域是一序號(有16位均為0),則會顯示為序號(“#”后跟整數);
OTHERWISE—如果前面IF語句為真,則停止解碼,否則繼續解碼;
ELSEINT—如果前面所有的 IF 和 TRYxxx 語句均失敗,則以帶符號十進制數形式(帶小數點)顯示這個域;
ELSEHEX—如果前面所有的 IF 和 TRYxxx 語句均為失敗,則以十六進制形式顯示這個域;
ELSECHAR—如果前面所有的 IF 和 TRYxxx 語句均為失敗,則以 ASCII 字符形式顯示這個域;
ELSEWCHAR—如果前面所有的 IF 和 TRYxxx 語句均為失敗,則以 UNICODE 字符形式顯示這個域。
如果域是一個二進制位集,則可以使用BIT序列,如果必要的話可以后面跟關鍵字 BITZ 與 BITHEX :
BIT 十六進制掩碼 "文本"—如果值與十六進制掩碼按位與(AND)的結果不是0,則將文本做為輸出串;
BITZ十六進制掩碼 "文本"—如果值與十六進制掩碼按位與(AND)的結果是0,則將文本做為輸出串;
BITHEX十六進制掩碼—如果值與十六進制掩碼按位與(AND)的結果不是0,則將結果以十六進制形式顯示。
特殊關鍵字 PURGE 會從輸出串尾部移除以下幾種符號:
空格?????? ' '
逗號?????? ','
或?????? '—>'
冒號?????? ':'
等于?????? '='
這會讓某些解碼情況變的簡單。關鍵字END是類型定義結尾標記并會自動運行PURGE命令。
3、預編譯類型
OllyDbg在預編譯資源時,已經包含150多種類型描述。以下列出了一部分。你可以在自定義文件中直接使用這些類型:
LANG_X—操作系統語言ID(0—未知、 9—語言、 C—法語,等等)
GENERIC_X—訪問類型(GENERIC_READ,GENERIC_WRITE...)
FILE_SHARE_X—共享類型(FILE_SHARE_READ,FILE_SHARE_WRITE)
CREATEFILE_X—文件創建模式(CREATE_NEW,OPEN_EXISTING...)
FILE_ATTRIBUTE_X—文件屬性(READONLY, SYSTEM,DELETE_ON_CLOSE...)
RT_AXX—資源類型(RT_CURSOR,RT_GROUP_ICON, ASCII string...)
RT_WXX—資源類型(RT_CURSOR,RT_GROUP_ICON, UNICODE string...)
COORD—坐標結構"(X=xxx,Y=yyy)"
STD_IO_X—標準句柄(STD_INPUT_HANDLE,STD_ERROR_HANDLE...)
GMEM_X—全局內存類型(GMEM_FIXED,GPTR...)
LMEM_X—局部內存類型(LMEM_FIXED,LPTR...)
FSEEK_X—文件查找類型(FILE_BEGIN,FILE_CURRENT...)
OF_X—文件模式(fOF_READ,OF_SHARE_COMPAT, OF_VERIFY...)
O_X—文件創建模式(O_RDONLY,O_BINARY, SH_COMPAT...)
SEMAPHORE_X—信號量類型(SEMAPHORE_ALL_ACCESS,SYNCHRONIZE...)
SLEEP_TIMEOUT—超時(INFINITE 或時間)
ROP—一些標準柵格運算標志代碼(ROP)(SRCCOPY,MERGEPAINT...)
COLORREF—RGB 顏色值("<WHITE>","RGB(rr.,gg.,bb.)"...)
WS_X—窗口風格(WS_OVERLAPPED,WS_POPUP...)
WS_EX_X—擴展窗口風格(WS_EX_DLGMODALFRAME,WS_EX_TOPMOST...)
MF_X—菜單標志(MF_BYPOSITION,MF_ENABLED...)
WM_X—ASCII窗口消息類型(WM_CREATE,WM_KILLFOCUS, CB_SETCURSEL...)
WM_W—UNICODE窗口消息類型(WM_CREATE,WM_KILLFOCUS, CB_SETCURSEL...)
VK_X—虛擬鍵盤代碼(VK_LBUTTON,VK_TAB, VK_F10...)
MB_X—message box style(MB_OK, MB_ICONHAND...)
HKEY_X—預定義注冊表句柄(HKEY_CLASSES_ROOT,HKEY_LOCAL_MACHINE...)
還有更多的預編譯類型。如果常量在它文件被定義為ABC_ xxxxxxxx,那么一般就有ABC_X預編譯類型。
注意:
1.如果OllyDbg是即時調試器,并且在Windows 95下掛接執行了DebugBreak() 的應用程序,則這個應用程序在掛接后,還會運行。在基于NT的系統下,應用程序應該會在DebugBreak()暫停。
2. 命令 SMSW (保存機器狀態字[Store Machine Status Word])。 這個命令僅接受寄存器 AX 作為參數,而程序編譯成EAX接受參數。
轉載于:https://www.cnblogs.com/lgh1992314/archive/2013/06/15/5834964.html
總結
- 上一篇: [转载]Apache之Hadoop学习:
- 下一篇: Ribbon_窗体_实现Ribbon风格