## 11.3 腳本例子
我們先創建一些在逆向時候會經常用到的腳本。之后,大家可以在此基礎上擴展它們,進一步完成功能更強大,針對性更強的腳步。接下來的腳本將展示如何收集危險函數的調用 信息,以及用 IDA 的 debugger hook 監視函數的代碼覆蓋率,還有所有函數的棧的大小。
### 11.3.1 收集危險函數的調用信息
當一個開發者在尋找軟件漏洞 bug 的時候,首先會找一些常用的而且容易被錯誤使用的 函數。比如危險的字符串拷貝函數 (strcpy, sprintf),內存拷貝函數(memcpy)等。在我們審核 程序的時候,需要很簡單的就找出這些函數。下面的腳本,將跟蹤這些危險的函數,找出調 用它們的地方,之后在這些地方的背景色設置成不同的顏色,我們在 IDA 窗口中就能很方 便的看出來。
```
#cross_ref.py
from idaapi import *
danger_funcs = ["strcpy","sprintf","strncpy"]
for func in danger_funcs:
addr = LocByName( func )
if addr != BADADDR:
# Grab the cross-references to this address
cross_refs = CodeRefsTo( addr, 0 )
print "Cross References to %s" % func
print "-------------------------------"
for ref in cross_refs:
print "%08x" % ref
# Color the call RED
SetColor( ref, CIC_ITEM, 0x0000ff)
```
我們先獲得危險函數的地址,然后測試這些地址的有效性。接著獲得這些函數的交叉引用信息,確認什么地方調用了它們,最后把它們打印出來,并在 IDA 中給它們上色。用之 前編譯好的 war-ftpd.exe 做測試目標,將看到如下的輸出:
```
Cross References to sprintf
-------------------------------
004043df
00404408
004044f9
00404810
00404851
00404896
004052cc
0040560d
0040565e
004057bd
004058d7
...
```
Listing 11-1: cross_ref.py 的輸出
上面這些被列出來的地址都是 sprintf 被調用的地方,如果在 IDA 中瀏覽這些地方 會看到它們都被上了色,如圖 11-3。

Figure 11-3: sprintf 調用通過 cross_ref.py 上色之后
### 11.3.2 函數覆蓋率
在執行動態分析的時候,明白我們真正進行的操作是由什么代碼執行的,非常重要。無論是測試網絡程序發送一個數據包,還是使用文檔閱讀器代開一份文檔,代碼覆蓋率都能幫 我們很好的了解,程序做了什么。下面,我們將用 IDAPython 獲取目標程序的所有函數, 并且在再每個函數的開始處都設置好斷點。之后運行 IDA 調試器,debugger hook 會把每一 次斷點觸發的情況通知我們。
```
#func_coverage.py
from idaapi import *
class FuncCoverage(DBG_Hooks):
# Our breakpoint handler
def dbg_bpt(self, tid, ea):
print "[*] Hit: 0x%08x" % ea
return
# Add our function coverage debugger hook
debugger = FuncCoverage()
debugger.hook()
current_addr = ScreenEA()
# Find all functions and add breakpoints
for function in Functions(SegStart( current_addr ), SegEnd( current_addr )):
AddBpt( function )
SetBptAttr( function, BPTATTR_FLAGS, 0x0 )
num_breakpoints = GetBptQty()
print "[*] Set %d breakpoints." % num_breakpoints
```
第一步安裝 debugger hook ,調試事件發生的時候就會調用它。接著循環獲取所有函數 的地址,在每個地址上設置斷點。SetBptAttr 告訴調試器,遇到斷點后,不用停下來,繼續 執行;如果沒有這樣做,那我們就得手工恢復調試器了,不累死也得煩死。最后一部就是打 印出所有斷點的數量。當一個斷點被觸發的時候, debugger hook 里的斷點處理函數就會打 印出當前的地址,這個地址由變量 ea 提供,它引用當前 EIP 寄存器的值。現在運行調試器(熱鍵 F9),你將清楚的看到什么函數被執行了,以及它們執行的順序。
### 11.3.3 計算棧大小
有時當我們對一個程序進行漏洞評估的時候,了解函數調用的棧的大小是很重要的。我們必須明確的知道,傳遞給函數的是一個指針還是申請好的棧緩沖區,如果是后者,我們就 會很感興趣,能傳遞多少數據給它,要知道溢出可是個精活,空間太小了盡管有漏洞也很難利 用。下面我們用一段簡短的代碼完成這項任務:枚舉程序中所有的函數,然后收集這些函數的棧信息,如果棧緩沖區大小符合我們的要求,就打印出來。將這些和前面的腳本合并起來, 我們就能在調試程序的時候,很好的跟蹤調試感興趣的函數 。
```
#stack_calc.py
from idaapi import *
var_size_threshold = 16 current_address = ScreenEA()
for function in Functions(SegStart(current_address), SegEnd(current_address) ):
stack_frame = GetFrame( function )
frame_counter = 0
prev_count = -1
frame_size = GetStrucSize( stack_frame )
while frame_counter < frame_size:
stack_var = GetMemberName( stack_frame, frame_counter )
if stack_var != "":
if prev_count != -1:
distance = frame_counter - prev_distance
if distance >= var_size_threshold:
print "[*] Function: %s -> Stack Variable: %s (%d bytes)" % ( GetFunctionName(function), prev_member, distance )
else:
prev_count = frame_counter
prev_member = stack_var
try:
frame_counter = frame_counter + GetMemberSize(stack_frame, frame_counter)
except:
frame_counter += 1
else:
frame_counter += 1
```
我們設置了一個閾值,用來衡量一個棧變量的大小是不適合我們的需求;這里設置成 16 個字節,不過大家也可以實驗下各種不同的大小看看得出的結果。首先,循環獲取所有 的函數,得到每個函數的棧框架對象。調用 GetStrucSize 計算出棧框架的大小。接著循環 獲取棧中的變量。如果找到變量,就將當前變量的位置減去前一個變量的位置。然后通過之 間的差值計算出變量占據的空間大小。如果大小夠大,就打印出來,如果不夠大,就嘗試計 算當前變量的大小,然后加上當前的位置,得到下一個變量的位置。如果無法確認變量的大 小,就在當前的位置簡單的加一個字節,移動到下一個位置,然后繼續循環。在腳本運行后, 我們就能看看難道類似如下的輸出。
```
[*] Function: sub_1245 -> Stack Variable: var_C(1024 bytes)
[*] Function: sub_149c -> Stack Variable: Mdl (24 bytes)
[*] Function: sub_a9aa -> Stack Variable: var_14 (36 bytes)
```
Listing 11-2: stack_calc.py 的輸出
現在我們有了 IDAPython 的基礎知識,同時也動手實現了幾個很容易擴展的腳本。這 些小小的腳本,將幫我們節省非常多的時間,在逆向工程中,最事件就是一切。下一章讓我 們看一看 IDAPython 的實際應用:PyEmu,一個基于 Python 的 x86 仿真器。
- 序
- 1 搭建開發環境
- 1.1 操作系統準備
- 1.2 獲取和安裝 Python2.5
- 1.3 配置 Eclipse 和 PyDev
- 2 調試器設計
- 2.1 通用 CPU 寄存器
- 2.2 棧
- 2.3 調試事件
- 2.4 斷點
- 3 自己動手寫一個 windows 調試器
- 3.2 獲得 CPU 寄存器狀態
- 3.3 實現調試事件處理
- 3.4 全能的斷點
- 4 PyDBG---純 PYTHON 調試器
- 4.1 擴展斷點處理
- 4.2 處理訪問違例
- 4.3 進程快照
- 5 IMMUNITY----最好的調試器
- 5.1 安裝 Immunity 調試器
- 5.2 Immunity Debugger 101
- 5.3 Exploit 開發
- 5.4 搞定反調試機制
- 6 HOOKING
- 6.1 用 PyDbg 實現 Soft Hooking
- 6.2 Hard Hooking
- 7 Dll 和代碼注入
- 7.1 創建遠線程
- 7.2 邪惡的代碼
- 8 FUZZING
- 8.1 Bug 的分類
- 8.2 File Fuzzer
- 8.3 改進你的 Fuzzer
- 9 SULLEY
- 9.1 安裝 Sulley
- 9.2 Sulley primitives
- 9.3 獵殺 WarFTPD
- 10 Fuzzing Windows 驅動
- 10.1 驅動通信
- 10.2 用 Immunity fuzzing 驅動
- 10.4 構建 Driver Fuzzer
- 11 IDAPYTHON --- IDA 腳本
- 11.1 安裝 IDAPython
- 11.2 IDAPython 函數
- 11.3 腳本例子
- 12 PyEmu
- 12.1 安裝 PyEmu
- 12.2 PyEmu 一覽
- 12.3 IDAPyEmu