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,在此记录一下 :)

posted @ 2017-09-28 09:34  rec0rd  阅读(540)  评论(0编辑  收藏  举报