本文是本菜鳥對 ida 靜態分析的一次實踐。對於本文中所涉及ida 的使用知識,請參閱壇主《加密與解密》第三版 3.2.1--3.2.14 中的相關內容。
閑話不敘。
癥狀:程式運行一小時後,運行開始不正常。一些字元開始跳動。無其它提示。
思路:從擷取系統時間的 API 入手,尋找時間比較的相關代碼。
開工。
查殼,無。開發語言: Borland c++ builder 1999
運行以加深對程式的瞭解。啟動程式,直接將系統時間後移一小時,程式出現異常。說明修改系統時間可影響到程式對已耗用時間的判斷。
將程式用ida 載入。在 "functions window" 視窗( 找不到則 菜單 windows-> functions window 或 alt+ 1),尋找時間相關的API。在 functions window 視窗中,系統api 用紅底加粗標識。對 "start"列進行排序,可將 api 集中起來,方便尋找。(原理?大概因為api 都在匯入表中吧)
找到了兩個可能相關的函數: getlocaltime 和 getticktime 。前者用來擷取系統目前時間,後者用來擷取系統啟動以來的時間。後者不受修改系統時間的影響。結合前面測試得知程式受修改系統時間的影響,故不考慮 getticktime。
在 getlocaltime 上右鍵,下斷點。
f9 運行程式,程式被斷下來。
中,code xref : 對此函數進行了調用的函數。綠色部分列舉了兩個。在函數名上右鍵,"jump to xref to operand "。可以看到共有四處調用:
以上四處,第二處 Sysutils::CurrentYear 顧名思義,只能取到 年份,幫不考慮。其它的三處,需要繼續上溯,直到使用者代碼。
先看 sysutils:now ,轉到了
繼續上溯對 now 的調用。
可以看到調用比較多。通常情況下,我們需要對每一個進行跟蹤。但這時我們看到了一個高度興趣的東東: FormShow。筆者編寫過 vb ,知道寫圖形介面程式的第一步就是 FormShow。估計delphi 也是這樣吧。不需要做艱難的決定。直接跟蹤。雙擊“_TFNewGoMain_FormShow+33”。到達下面的介面。
.text:00408E43 call @Sysutils@Now$qqrv ; Sysutils::Now(void)
.text:00408E48 fstp dbl_CEEC9C
查閱手冊得知,Sysutils::Now() 返回了一個浮點數值。整數部分儲存1900年以來的天數,小數部分儲存今天已經消逝的時間比例。在 delphi 語言中,浮點數是儲存在浮點寄存器 fpu registers 中的。
fstp 指令的意思是將浮點數指令寄存器中的資料儲存到 變數 dbl_CEEC9C 中。
右鍵 dbl_CEEC9C,選擇 rename, 將變數dbl_CEEC9C 標記為 my_dbl_CEEC9_now,以方便識別。
現在我們對程式的設計思路比較清楚了。按現在的分析,程式在啟動時記錄了系統時間。並不斷地檢查,如果超過了一小時,就開始搗亂。我們只需要找到讀取my_dbl_CEEC9_now 的代碼,就能跟蹤到關鍵代碼了。
右鍵 my_dbl_CEEC9_now ,"jump to xref operator",看到了如下的八處調用。
根據 type 列知道,對 my_dbl_CEEC9_now 的操作,有四處讀(r),三處寫(w),我們首先考慮 讀的代碼。
幸運的是,我們第一次就到了合適的位置。見到了經典的比較和跳轉。
以上三處代碼:
.text:00404195 call @Sysutils@Now$qqrv ; Sysutils::Now(void)
.text:0040419A fstp [ebp+var_380] ; 取目前時間
.text:004041A0 fld my_dbl_CEEC9C_now ;取啟動時儲存的時間
.text:004041A6 fadd ds:dbl_404AA0 ;啟動時儲存的時間加上一個值(一個多小時)
......
.text:004041ED fxch st(1) ;交換目前時間(st0) 和啟動時儲存的時間(st1)
.text:004041EF fucompp ;比較!
......
.text:004041F4 setnbe dl
.text:004041F7 and edx, 1
.text:004041FA test dl, dl
.text:004041FC jz short loc_40422A ;小等於則跳!.text:004041FE call sub_4AD208 ; 幹擾函數
以上代碼中,ds:dbl_404AA0 是一個全域變數。儲存了限定的時間長度。
00404AA0 dbl_404AA0 dq 0.04166666666666666 ; DATA XREF: sub_403E30+376r
0.04166666666666666 = 1/24,正好一小時,即我們想要的值。 觀察對 my_dbl_CEEC9_now 的其它幾處調用。可以看到,其它的三處 read 和上面的代碼基本一樣。三處 write 查了查,沒什麼關係,就不研究了。
對此程式的分析到此結果,接下來只需要對幾處變數對修改即可。本文主要介紹 ida 的靜態分析,代碼修改就不贅述了。
如果你使用 hex-rays 的反編譯功能,分析時要注意的是delphi 語言的參數傳遞的一個特點。delphi 語言中,函數參數的傳遞是按照 eax,edx,ecx,棧變數 的順序來傳遞的。可以看到,一些明明沒有參數的函數,hex-rays 反編譯的c 代碼中卻添加了三個參數。這就是 eax,edx,edx。所以,在反編譯的c 代碼中,要認真辨別相關函數是否確實通過這些寄存器傳遞了參數。 改成三小時( 3/24 = 0.125 )試試。
查看記憶體 00404AA0 55 55 55 55 55 55 A5 3F改為00404AA0 00 00 00 00 00 00 c0 3F
參考資料:
線上浮點數轉換
http://babbage.cs.qc.edu/IEEE-754/Decimal.html delphi 的函數呼叫慣例,浮點數的處理都和不同