選擇 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 調試器,可以進行動態調試和靜態分析,深入了解程序的結構和運行過程,但這章未對源程序進行修改,只是在調試器中改變了標誌寄存器的值。