1. 问题背景:
在fuzz的时候,我们经常会感到很被动,似乎只能静静地等待崩溃的出现,或者不出现:(
其实这个过程中我们可以进入程序内部观测fuzz的实际效果,如果发现效果不佳可以着手调节策略。
怎么在没有崩溃的时候观察fuzz的效果呢?覆盖率似乎看不到实际的打击效果(是否饱和打击?)
这两天用简单的IDA和gdb脚本实现了一个初步的demo,能够实现以下功能:
a) .fuzz过程中用gdb挂起进程,使用gdb python脚本对感兴趣的函数执行自定义的gdb命令 bn,可以观测其是否被饱和攻击,进而判断fuzz效果
b) .可以使用IDA python脚本对感兴趣的总入口函数(API)进行分析,提取出其所有子函数,并将该子函数列表导出到文件,再使用上述gdb python脚本的bnfile命令,可以实现对多个函数是否饱和攻击的批量检测。
2. demo简介
2.1. IDA python 脚本及效果:
ListAllSubFunc.py
from idaapi import *
def get_all_sub_func(ea):
func = idaapi.get_func(ea) # return func_t struct
addr = func.startEA
list = []
while addr != func.endEA:
list1 = CodeRefsFrom(addr,0)
for i in list1:
if (not (i in list)) and (idaapi.get_func(i).startEA != func.startEA):
list.append(i)
get_all_sub_func2(list, i)# so we can do without global var
addr = NextNotTail(addr)
return list
def get_all_sub_func2(list, ea):
func = idaapi.get_func(ea) # return func_t struct
addr = func.startEA
while addr != func.endEA:
list1 = CodeRefsFrom(addr,0)
for i in list1:
if (not (i in list)) and (idaapi.get_func(i).startEA != func.startEA):
list.append(i)
get_all_sub_func2(list, i)
addr = NextNotTail(addr)
class myIdaPlugin(plugin_t):
flags=0
wanted_name="AllSubFunc"
wanted_hotkey="shift-a"
comment="AllSubFunc"
help="To Print all sub functions of specified func"
def init(self):
msg("AllSubFunc plugin init called.\n")
return PLUGIN_OK
def term(self):
msg("AllSubFunc plugin term called.\n")
def run(self,arg):#arg set in plugins.cfg
cursor = idaapi.get_screen_ea()
list = get_all_sub_func(cursor)
msg("All Sub Functions of "+GetFunctionName(cursor)+":\n")
print "-------------start--------------"
for i in list:
funcname = GetFunctionName(i)
funcprefix = funcname[0:4]
if '.' != funcname[0] and funcprefix != "sub_":
print funcname#, "0x%08x"%i
print "-------------end----------------"
def PLUGIN_ENTRY():
return myIdaPlugin()
使用效果:将光标放在IDA中你感兴趣的函数,shift-a提取其所有子函数
2.2 GDB python 脚本及效果:
# coding=utf-8
# my gdb cmd extended by python
import gdb
RESOVLE_TYPE_DONE = 0 # only need to log function info
RESOVLE_TYPE_TODO = 1 # need to disas function and handle all calling points
RESOVLE_TYPE_EVAL = 2 # calling point unresovled, need to evaluate target address
# 定义断点及其处理机制
class MyBreakpoint(gdb.Breakpoint):
def __init__(self, spec, type=gdb.BP_BREAKPOINT, wp_class=gdb.WP_WRITE, internal=False, temporary=False):
self.resolved = RESOVLE_TYPE_DONE
self.func_addr = 0
self.hit = 0
self.saturate = 0
gdb.Breakpoint.__init__(self, spec, type, wp_class, internal, temporary)
def set_funcname(self, func_name):
self.func_name = func_name
def set_funcaddr(self, func_addr):
self.func_addr = func_addr
def name(self):
return self.func_name or self.location
def add_hit(self):
self.hit += 1
def set_saturate(self,val):
self.saturate = val
def stop(self):
self.add_hit()
if self.hit == self.saturate:
s = self.func_name + " covered " + str(self.hit) + " times\n"
print(s)
return False
# 注册命令bn,记录一个函数是否被饱和命中, usage: bn memcpy 10000
class bn(gdb.Command):
"""Watch breakpoint if N times hited
Usage: bn memcpy 1000000 ,print when memcpy hit 1000000 times
bn , del all bps
"""
def __init__(self):
super(self.__class__, self).__init__("bn", gdb.COMMAND_USER)
self.bplist = []
def invoke(self, args, from_tty):
argv = gdb.string_to_argv(args)
if len(argv) == 0:#delete all bps
self.delbps()
return
if len(argv) != 2:
raise gdb.GdbError('para num err, 0 or 2 para need, eg. bn memcpy 1000')
bp = MyBreakpoint(argv[0], internal=True)#, gdb.BP_BREAKPOINT, None, True, False)
bp.silent = True # set True to suppress Breakpoint stop messages.
bp.set_funcname(argv[0])
bp.set_saturate(int(argv[1]))
self.bplist.append(bp)
def delbps(self):
for i in self.bplist:
i.delete()
del i
bn()
# 注册批量bn命令bnfile:从文件中读取函数列表,批量bn处理
class bnfile(gdb.Command):
"""
muti bn from file
eg.
bnfile funcs.txt 10000
"""
def __init__(self):
super(self.__class__, self).__init__("bnfile", gdb.COMMAND_USER)
def invoke(self, args, from_tty):
argv = gdb.string_to_argv(args)
if len(argv) != 2:
raise gdb.GdbError("para num err, 2 paras needed, eg. bnfile filename n")
fp = open(argv[0], "r")
for func in fp :
gdb.execute("bn " + func + " " + argv[1])
fp.close()
bnfile()
使用效果:观测一个函数,批量观测多个函数
(gdb) bn ape_unpack_stereo 10000
(gdb) c
Continuing.
ape_unpack_stereo covered 10000 times
(gdb) bnfile /root/func.txt 10000
(gdb) c
Continuing.
pnm_space covered 10000 times
av_free covered 10000 times
av_freep covered 10000 times
av_packet_get_side_data covered 10000 times
av_pix_fmt_desc_get covered 10000 times
av_malloc covered 10000 times
pnm_get covered 10000 times
av_frame_set_pkt_pos covered 10000 times
av_buffer_create covered 10000 times
buffer_replace covered 10000 times
av_image_check_size2 covered 10000 times
ff_get_buffer covered 10000 times
get_buffer_internal covered 10000 times
validate_avframe_allocation covered 10000 times
ff_decode_frame_props covered 10000 times
3. 总结
这里只是粗略的实现了一点监控fuzz效果的想法,依赖于强大的IDA python 和GDB python,在此记录一下 :)