## 6.1 用 PyDbg 實現 Soft Hooking
第一個例子就是在應用層嗅探加密的網絡傳輸。平時為了明白客戶端和服務器之間的工 作流程,我們都會使用一個網絡分析器列如 Wireshark。很不幸的是,Wireshark 獲得的數據 經常都是加密過的,使得協議分析變得模糊。用 soft hooking 你能夠在數據加密前或者接受 并解密后捕獲它們。
實驗目標就是最流行的開源瀏覽器 Mozilla Firefox。為了這次實驗,我們假設 Firefox 是閉源的(否則會相當沒趣)。我們的任務就是在 firefox.exe 進程加密數據前嗅探出數據。 現在最通用的網絡加密協議就是 SSL,這次的主要目標就是解決她。
為了跟蹤函數的調用(未加密數據的傳遞),需要使用記錄模塊間調用的技巧 [http://forum.immunityinc.com/index.php?topic=35.0](http://forum.immunityinc.com/index.php?topic=35.0) )。現在首要解決的問題就是在什么地方 設置 hook。我們先假定將 hook 設置在 PR_Write 函數上(由 nspr4.dll.導出)。當這個函數被 執行的時候,堆棧[ ESP + 8 ]指向 ASCII 字符串(包含我們提交的但未加密的數據)。 ESP + 8 說明它是 PR_Write 的第二個函數,也是我們需要的,記錄它,恢復程序。
首先打開 Firefox,輸入網址 [https://www.openrce.org/](http://www.openrce.org/)。一旦你接收了 SSl 證書,頁面就 加載成功。接著 Immunity 附加到 firefox.exe 進程在 nspr4.PR_Write 設置斷點。在 OpenRCE 網站右上角有一個登錄窗口,設置用戶名為 test 和密碼 test,點擊 Login 按鈕。設置的斷點 立刻被觸發;再按 F9,斷點再次觸發。最后,你將在棧看到如下的內容:
```
[ESP + 8] => ASCII "username=test&password=test&remember_me=on"
```
很好,我們很清晰的看到了用戶名和密碼。但是如果從網絡層看傳輸的數據,將是一堆 經過 SSL 加密的無意義的數據。這種方法不僅對 OpenRCE 有效。當你瀏覽任何一個需要傳 輸敏感數據的網站的時候,這些數據都將很容易的被捕捉到。現在再也不用手工操作調試器 去捕捉了,自動化才是王道。
在用 PyDbg 定義 soft hook 之前,需要先定義一個包含說有 hook 目標的容器。如下初始 化容器:
```
hooks = utils.hook_container()
```
使用 hook_container 類的 add()方法將我們定義的 hook 加進去。函數原型:
```
add( pydbg, address, num_arguments, func_entry_hook, func_exit_hook )
```
第一個參數設置成一個有效的 pydbg 目標,address 參數設置成要安裝 hook 的地址, num_arguments 設置成傳遞給 hook 的參數。func_entry_hook 和 func_exit_hook 都是回調函數。 func_entry_hook 是 hook 被觸發后立刻調用的,func_exit_hook 是被 hook 的函數將要退出之 前執行的。entry hook 用于得到函數的參數,exit hook 用于捕捉函數的返回值。
```
def entry_hook( dbg, args ):
# Hook code here
return DBG_CONTINUE
```
dbg 參數設置成有效的 pydbg 目標,args 接收一個列表,包含 hook 觸發時接收到的參 數。
exit hook 回調函數有一點不同就是多了個 ret 參數,包含了函數的返回值(EAX 的值): def exit_hook( dbg, args, ret ):
```
# Hook code here
return DBG_CONTINUE
```
接下用實例看看如何用 entry hook 嗅探加密前的數據。
```
#firefox_hook.py from pydbg import *
from pydbg.defines import *
import utils
import sys
dbg = pydbg()
found_firefox = False
# Let's set a global pattern that we can make the hook
# search for
pattern = "password"
# This is our entry hook callback function
# the argument we are interested in is args[1]
def ssl_sniff( dbg, args ):
# Now we read out the memory pointed to by the second argument
# it is stored as an ASCII string, so we'll loop on a read until
# we reach a NULL byte buffer = ""
offset = 0
while 1:
byte = dbg.read_process_memory( args[1] + offset, 1 )
if byte != "\x00":
buffer += byte offset += 1 continue
else:
break
if pattern in buffer:
print "Pre-Encrypted: %s" % buffer
return DBG_CONTINUE
# Quick and dirty process enumeration to find firefox.exe
for (pid, name) in dbg.enumerate_processes():
if name.lower() == "firefox.exe":
found_firefox = True
hooks = utils.hook_container() dbg.attach(pid)
print "[*] Attaching to firefox.exe with PID: %d" % pid
# Resolve the function address
hook_address = dbg.func_resolve_debuggee("nspr4.dll","PR_Wri
if hook_address:
# Add the hook to the container. We aren't interested
# in using an exit callback, so we set it to None.
hooks.add( dbg, hook_address, 2, ssl_sniff, None )
print "[*] nspr4.PR_Write hooked at: 0x%08x" % hook_address
break
else:
print "[*] Error: Couldn't resolve hook address."
sys.exit(-1)
if found_firefox:
print "[*] Hooks set, continuing process."
dbg.run()
else:
print "[*] Error: Couldn't find the firefox.exe process."
sys.exit(-1)
```
代碼簡潔明了:在 PR_Write 上設置 hook,當 hook 被觸發的時候,我們嘗試讀出第二個 參數指向的字符串。如果有符合的數據就打印在命令行。啟動一個新的 Firefox,接著運行 firefox_hook.py 腳本。重復之前的步驟,登錄 [https://www.openrce.org/](http://www.openrce.org/),將看到輸出如下:
```
[*] Attaching to firefox.exe with PID: 1344
[*] nspr4.PR_Write hooked at: 0x601a2760
[*] Hooks set, continuing process.
Pre-Encrypted: username=test&password=test&remember_me=on
Pre-Encrypted: username=test&password=test&remember_me=on
Pre-Encrypted: username=jms&password=yeahright!&remember_me=on
```
Listing 6-1: How cool is that! 我們能看到未加密前的用戶名密碼
我們已經看到了 soft hook 的輕量級和強大能力。這種方法能被用于所有類型的調試和 逆向過程。在上面的例子中 soft hook 的工作還算正常,如果遇到有性能限制的函數調用時, 進程馬上就會變得緩慢,行為異常,還可能崩潰。只是因為,當 INT3 被觸發的時候,會將 執行權限交給我們的 hook 代碼之后返回。這回花費非常多的事件,如果函數每秒鐘執行數 千次。接下來讓我們看看如何通過設置 hard hook 和 instrument low-level heap routines 以解決這個問題。
- 序
- 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