選擇 IDA 調試器#
ida 支持多個調試器。

ida 支持的調試器
選擇 Local Windows debugger 調試器
打開菜單欄調試器 - 調試器選項,可以設置一些調試器的功能。

ida 調試器選項
調試器界面功能#
勾選進程入口點暫停,並選擇事件條件,點擊確定。

對之前的關鍵代碼 jinx 重命名及改色

修改後如下,按 X 鍵查看哪裡引用了這個函數:

對引用點進行著色。

在 0x00401243 處設置斷點,鼠標放到這一行,右鍵 - 添加斷點,添加斷點後變成紅色背景。

通過菜單欄調試器-啟動進程(F9),開啟調試。如果在本地調試可執行程序會彈出如下警告窗口。

注:
在加載器分析程序的時候,程序不會在本地執行,但是調試則不是。如果程序是一個病毒或者其他危險的惡意軟件時,需要特別的當心。這時候需要使用遠程調試器 (REMOTE DEBUGGER)在虛擬機中執行程序。
點擊是繼續調試。
由於設置了讓調試器在入口處暫停,如下圖,ida 暫停在入口處(0x00401000)。

調整右上角的通用寄存器和標誌寄存器窗口,以便查看。

上圖中,可以看到這些寄存器的值。調整正常之後,打開菜單欄窗口-保存桌面並勾選default選項,保存為調試器默認的窗口設置。那麼之後,運行調試器的時候,程序按照默認設置運行,如需修改也可以。
在通用寄存器下方可放置堆棧視圖。

左側和最下方是反匯編及 16 進制窗口。

在反匯編視圖下方是當前的內存地址以及文件偏移(FILE OFFSET),這個偏移是指可執行文件偏移。

在 ida 中,默認 G 鍵是轉向一個內存地址的快捷鍵,按 G 輸入 0x401389,轉向之前設置的斷點。

所有靜態分析、重命名等改變的內容都會保存。

打開菜單欄視圖 - 打開子視圖 - 段,發現加載器加載了 3 個區段。

加載了 0x401000 上的 code 字段,以及下方的 data 和.idata 字段,對這些區段的修改都會保存,除此之外的修改都不會保存,因為它們是調試器加載的區段,不會保存到 IDA 數據庫中。
上圖中的 L 標誌,表示這個程序被加載器和調試器同時加載,既能實施靜態分析,同時在動態調試時也不會丟失靜態分析的信息。
下面介紹了一些小工具欄。
1、菜單欄 view(視圖)-toolbars(工具類)-jump(跳轉),再通過保存桌面功能將它設置為默認啟用。

按返回鍵可以返回之前的程序入口處。

在菜單欄 debugger(調試器)-breakpoints(斷點)-breakpoint list(斷點列表)中,能夠查看所有的程序斷點。

點擊任意一處就可以跳轉到對應的斷點處。
條件跳轉指令與標誌寄存器#
目前調試器執行到了程序入口,並且設置了兩個斷點,按 F9 繼續運行。

此時運行了 crackme.exe 程序,打開目標程序 Help-register 菜單欄輸入用戶名和密碼。

點擊 ok

上圖中,左邊閃爍的紅色箭頭表示程序繼續執行的路徑,此時 eax 的值是 0x6c。

0x6c 轉換為字符串就是 l。


上圖中,al 寄存器與 0x41 進行比較,判斷是否小於 0x41(A),下方的綠色代碼塊中 al 寄存器也和 0x5a 進行了比較。
| asm | condition | operation |
|---|---|---|
| JA | z=0 and c=0 | jump if above(如果大於則跳轉) |
| JAE | c=0 | jump if above or equal(如果大於或等於則跳轉) |
| JB | c=1 | jump if below (如果低於則跳轉) |
| JBE | z=1 or c=1 | jump if below or equal(如果小於等於則跳轉) |
| JC | c=1 | jump if carry(如果進位則跳轉) |
| JECXZ | ecx=0 | jump if ecx is 0 (如果 ecx 為 0,則跳轉) |
| JE | z=1 | jump if equal(如果相等就跳轉) |
| JZ | z=1 | jump if zero (如果為零則跳轉) |
| JNE | z=0 | jump if not equal (如果不相等就跳轉) |
| JNZ | z=0 | jump if not zero (如果非零則跳轉) |
| JO | 超出範圍 | jump if overflow |
| JP | 有偶數個 1 位(操作結果中二進制中 1 的個數,01110000) | jump if parity |
| JPE | 偶數校驗 | jump if parity even |
| JNP | 沒有偶數個 1 位 | jump if not parity |
| JPO | 奇數校驗 | jump if parity odd |
| JS | 符號位為 1 | jump if sign(如果有標誌則跳轉) |
| JNS | 符號位為 0 | jump if not sign(如果沒有標誌則跳轉) |
| JL/JNGE | 符號位與溢出位相同 | jump if less or not greater/equal |
| JLE/JNG | z=1 or 符號位與溢出位相同 | jump if less or equal/not greater |
| JG/JNLE | z=0 and 符號位與溢出位相同 | jump is greater/not less or equal |
條件跳轉指令及跳轉條件
下面轉到 ida 標誌寄存器視圖,如下圖所示。

根據表格可知,如果 CF=0,就不跳轉,程序往綠色代碼塊執行,那麼觸發這個 C 標誌的數學條件是什麼呢?
C 標誌代表無符號整數運算有錯誤這一信息,如果把 cmp 指令看作是不保存結果的減法算法,那麼0x6c - 0x41 = 0x2b,結果是正數,如果 al 是 0x30,那麼0x30 - 0x41 = -0x11。

-0x11 是一個負數,程序只能繼續使用它對應的 16 進制運行。

如上圖所示,-0x11 的 16 進制是 0xffffffef,10 進制是 4294967279,這個值很大,而且0x30-0x41也不會是正數。
那麼如何知道操作中有沒有考慮正負號呢。這取決於使用的跳轉類型,JB 是用於無符號數比較後的跳轉指令,對應的有符號數的指令是 JL。
當使用 JB 指令時,通常是用它來檢測無符號整數是否小於某個值。如果運算結果產生了借位,就表示第二個操作數(被比較數)比第一個操作數(比較數)大,此時 JB 標誌位為 1,就可以進行跳轉。否則,如果運算結果沒有產生借位,則 JB 標誌位為 0,即不滿足條件,就不進行跳轉。
示例:
mov al, 150 ;把150賦值給 al
cmp al, 255 ;比較 al 和 255
jb smaller ;如果 al小於255,就跳轉到smaller標籤處
;執行其他操作
smaller:
;如果 al小於等於255,就會跳轉到這裡
在這個示例中,如果 al 中的值小於 255,則 CF 標誌位為 1,就會跳轉到 smaller 標籤處。否則,如果 al 中的值大於 255,則 CF 標誌位為 0,不跳轉,執行其他操作。(總而言之,如果al比255小,則跳轉)
對於比較是否帶符號需要通過它後面的條件跳轉指令來確定。
無符號跳轉
| 符號 | 描述 | 標誌位 |
|---|---|---|
| JE/JZ | Jumps if equal or zero | zf |
| JNE/JNZ | Jumps if not equal or zero | zf |
| JA/JNBE | Jumps if above or not below or equal | zf,cf |
| JB/JNAE | Jumps if below or not above or equal | cf |
| JBE/JNA | Jumps if below or equal or not above | cf,af |
無符號跳轉
上圖中的指令都不考慮正負號,每一種跳轉都有對應的有符號跳轉。
| 符號 | 描述 | 標誌位 |
|---|---|---|
| JE/JZ | Jumps if equal or zero | zf |
| JNE/JNZ | Jumps if not equal or zero | zf |
| JG/JNLE | Jumps if greater or not less or equal | zf,sf,of |
| JGE/JNL | Jumps if greater or not equal or less | sf,of |
| JL/JNGE | Jumps if less or not grater or equal | sf,of |
| JLE/JNG | Jumps if less or equal or not greater | zf,sf,of |
有符號跳轉
上面兩個圖中,JE(是否相等)出現在兩個圖中,因為在這個特例中,符號並不重要,如果兩個數相等,zf=1,意味著標誌觸發。
有符號跳轉中JG = Jumps if greater,在無符號跳轉中它對應的指令是JA,Jumps if above。
繼續調試程序發現,ida 會在每一個設置的斷點上暫停,執行了一個循環,每個循環會讀取在目標程序輸入的用戶名的一個字符並和 0x41 進行比較,如果任何一個字符小於 0x41,就會報錯,由於輸入的是 lca,每個值都比 0x41 大,所以不顯示報錯。
嘗試輸入數字看看。

此時,就跳轉到紅色區塊,也就是 jb 跳轉的地方,因為第一個比較的字符是 2(0x32),2 肯定比 0x41 小。


同時觸發了 C 標誌位,CF=1,因為 0x32 - 0x41 無符號數相減,結果是負數,這將觸發 C 標誌位。

在 C 標誌位右鍵單擊,選擇 Zero Value 將 C 標誌清 0。


按 F9 繼續進程,程序讀取 22lca 的第二個字符,左側紅色箭頭又開始閃爍。繼續將 CF 標誌位設為 0,之後的字符 lca 都會大於 0x41,不會觸發標誌位,程序都走左側紅色箭頭。
字符檢測完成後,程序來到最後一個跳轉。

上圖的 cmp 指令比較 eax 和 ebx,jz 判斷比較是否相等,無符號,程序走向紅色區塊,因為這兩個寄存器不相等。
![]()
由於不相等,所以 zf 標誌未被觸發。

如果手動觸發 zf 標誌,改變跳轉方向,程序會轉向綠色區塊,顯示註冊成功。


SET IP#
set ip 只有在調試器模式下才有。
有時候也可以不直接修改跳轉,可以將鼠標移動至想要執行的那個代碼塊上,右鍵單擊,選擇 SET EIP。
在 0x40124c 處,set eip,程序會從 0x40124c 處開始運行。

注:在 7.7 中這樣操作,執行多次後報錯如下:

提示 “嘗試執行非法指令”


報錯信息提示內存不可寫,也許讀取內容超出了範圍。
總結#
通過使用 IDA 調試器,可以進行動態調試和靜態分析,深入了解程序的結構和運行過程,但這章未對源程序進行修改,只是在調試器中改變了標誌寄存器的值。