## 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