use gdb to look at fuzz
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,在此记录一下 :)