## 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。
![image](https://box.kancloud.cn/2016-03-11_56e2361b21742.gif)
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