Beispiel #1
0
    def collect(self, times=300, corpus=[]):
        """执行指定次数,收集初始测试用例

        :param times: 运行次数,默认为 10 次
        :param corpus: 用户指定的初始测试用例
        """

        self.data_pool.clear()
        self.data_pool.extend(corpus)

        # 静态分析阶段,提取主程序字符串常量
        self.curr_code, self.curr_ast, self.curr_sde_visitor = self.load_code(self.entry_path)
        # visitor = SDEVisitor()
        # visitor.visit(self.curr_ast)
        # self.data_pool.update(set(visitor.get_all_const_str()))
        
        if len(self.data_pool) < 1:
            self.data_pool.append('a')
        logger.debug("Initial data_pool = {}".format(self.data_pool))
        cnt = 0
        
        self.tracer_debugger = sys.gettrace()
        self.cov.start()
        self.tracer_cov = sys.gettrace()
        
        sys.settrace(self.trace_dispatch)
        threading.settrace(self.trace_dispatch)

        while cnt < times and len(self.data_pool) > 0:
            selected_idx = random.randint(0, len(self.data_pool) - 1)
            selected_ipt = self.data_pool[selected_idx]
            logger.info("Using input [{}] to run the test program".format(selected_ipt))
            self.run_once(selected_ipt)

            # 先执行,后 pop 掉本次执行使用的数据,避免重复
            self.data_pool.pop(selected_idx)
            if not self.quitting:
                self.used_data_pool.append(selected_ipt)
            else:
                logger.error("Input data \033[1;33m{}\033[0m caused a crash!".format(selected_ipt))
            cnt += 1
        
        self.cov.stop()
        self.cov.save()
        # logger.info("Generating Coverage report")
        # self.cov.report()

        sys.settrace(self.tracer_debugger)
        threading.settrace(self.tracer_debugger)
Beispiel #2
0
    def dispatch_line(self, frame: FrameType):
        """代码行跟踪处理

        """
        logger.debug('{}:{}\t{}\t{}'.format(
            os.path.basename(self.curr_filename),
            frame.f_lineno,
            self.curr_ast.line_node[frame.f_lineno] if len(self.curr_ast.line_node) > 0 else '',
            self.curr_code[frame.f_lineno-1])
        )

        # 监视变量值
        for var_name in self.curr_watch_vars:
            name_split = var_name.split('.')
            try:
                obj = frame.f_locals[name_split[0]]
                val = eval("obj.{}".format('.'.join(name_split[1:]))) if len(name_split) > 1 else obj
                if isinstance(val, str) and \
                    val not in self.data_pool and \
                    val not in self.used_data_pool and \
                    len(val) < self.max_len:
                        logger.info("New str DETECTED during running: ({}:{}) {} = {}".format(
                            self.curr_filename, self.curr_lineno, var_name, val
                        ))
                        self.data_pool.append(val)
            except (KeyError, NameError, AttributeError):
                pass
        
        # 获取当前完整函数名称
        func_name = self.get_curr_func_name()
        if func_name:
            logger.debug("FULL_FUNC_NAME: {}".format(func_name))
            var_inputs: dict = self.curr_sde_visitor.analyzed_functions.get(func_name).get('var_input')
            assert var_inputs is not None

            # 清空监视变量
            self.curr_watch_vars.clear()
            for _, vars in var_inputs.items():
                self.curr_watch_vars.update(vars)
            logger.debug("NEXT_WATCH_VARS: {}".format(self.curr_watch_vars))

        
        return self.trace_dispatch
Beispiel #3
0
    def load_code(self, path):
        """读取源代码并进行缓存

        TODO: 缓存使用 LRU 等算法进行优化
        :param path: 源代码文件路径
        :return: 源代码字符串,解析后的 AST 以及 SDEVisitor 对象
        """
        code_lis = []
        curr_ast = None
        if path in self.code_cache:
            code_lis = self.code_cache[path]
            curr_ast = self.ast_cache[path]
            visitor2 = self.visitor_cache[path]
        else:
            logger.info("Detected new source file [{}]".format(path))
            with open(path, 'r') as f:
                code_src = f.read()
            code_lis = code_src.replace('\r\n', '\n').split('\n')
            self.code_cache[path] = code_lis

            logger.info("Building AST...")
            curr_ast = ast.parse(code_src, mode='exec')
            self.ast_cache[path] = curr_ast

            # 将 LineVisitor 解析的 line_node 添加到 curr_ast 对象中
            visitor = LineVisitor()
            visitor.visit(curr_ast)
            curr_ast.line_node = visitor.get_line_node()

            # 静态分析提取敏感数据
            visitor2 = SDEVisitor()
            visitor2.visit(curr_ast)
            self.visitor_cache[path] = visitor2
            new_data_pool: set = set(visitor2.get_all_const_str()) - set(self.data_pool)
            logger.debug("New data detected ({}): {}".format(len(new_data_pool), new_data_pool))
            self.data_pool.extend(new_data_pool)
            # 保存静态分析结果到 curr_ast 对象中
            # curr_ast.static_result = visitor2.analyzed_functions

            logger.info("AST line_node length = {}".format(len(curr_ast.line_node)))
            logger.info("Finished building AST")
            logger.debug(visitor2.analyzed_functions)
        return code_lis, curr_ast, visitor2
Beispiel #4
0
    def run_once(self, one_input):
        if self.entry_func is not None:
            target = self.entry_func
        elif self.entry_compiled is not None:
            target = self.entry_compiled
        else:
            raise RuntimeError("Must set entry before run")

        self.quitting = False

        run_out = StringIO()
        run_err = StringIO()
        old_stdout = sys.stdout
        old_stderr = sys.stderr

        sys.stdout = run_out
        sys.stderr = run_err

        # sys.settrace(self.trace_dispatch)
        # threading.settrace(self.trace_dispatch)
        try:
            if isinstance(target, FunctionType):
                logger.info("Start running with function mode")
                target(one_input)
            else:
                self.namespace['__oneinput__'] = one_input
                logger.info("Start running with compile mode")
                exec(self.entry_compiled, self.namespace)
        except Exception as e:
            logger.error("Execution failed with exception")
            self.quitting = True
            logger.error(traceback.format_exc())
        finally:
            pass
            # self.cov.stop()
            # sys.settrace(self.tracer_debugger)
            # threading.settrace(self.tracer_debugger)
        
        sys.stdout = old_stdout
        sys.stderr = old_stderr

        with open('fuzz_out.log', 'a+') as f:
            f.write(run_out.getvalue())
        with open('fuzz_err.log', 'a+') as f:
            f.write(run_err.getvalue())

        if not self.quitting:
            logger.info("Execution finished")
Beispiel #5
0
 def set_entry_dir(self, entry_dir):
     self.entry_dir = os.path.abspath(entry_dir)
     logger.info("entry_dir set to [{}]".format(self.entry_dir))
Beispiel #6
0
    def __init__(self, entry_func=None, trace_native_libs=False, black_filenames=None, white_filenames=None, verbose=False, max_len=4096):
        """初始化 SDERunner

        :param target: 待执行的函数,若要执行文件,无需传入该参数,调用 load 方法即可
        :param trace_native_libs: 是否跟踪内部库,默认为 False
        :param black_filenames: 文件路径关键词黑名单列表,默认为 None
        :param white_filenames: 文件路径关键词白名单列表 (优先于黑名单),默认为 None
        :param max_len: 单个测试用例的最大长度,默认为 4096
        :return:
        """

        self.quitting = False
        self.max_len = max_len
        self.entry_path = None
        self.entry_compiled = None
        self.entry_dir = None

        self.curr_filename = None
        self.curr_lineno = 0
        self.curr_code = None
        self.curr_ast = None
        self.curr_sde_visitor = None

        self.curr_watch_vars = set()

        self.namespace = dict()

        # 缓存代码和 AST,key 为文件路径
        self.code_cache = dict()
        self.ast_cache = dict()
        self.visitor_cache = dict()

        # 测试数据池
        self.data_pool = list()
        self.used_data_pool = list()
        
        # 额外的 tracer (用于覆盖率或调试器)
        self.cov = coverage.Coverage(branch=True, cover_pylib=True)
        self.tracer_cov = None
        self.tracer_debugger = None

        # 执行函数
        self.entry_func = None
        if isinstance(entry_func, FunctionType):
            self.entry_func = entry_func
            self.entry_path = os.path.abspath(entry_func.__code__.co_filename)
            self.set_entry_dir(os.path.dirname(entry_func.__code__.co_filename))

        # 详细模式
        if verbose:
            SDELogger.enable_debug()

        # 准备关键词列表
        self.black_fns = []
        if not trace_native_libs:
            self.black_fns.extend(['site-packages', 'lib/python'])
        if black_filenames:
            self.black_fns.extend(black_filenames)
        logger.info("black_fns set to {}".format(self.black_fns))

        self.white_fns = [self.entry_path]
        if white_filenames:
            self.white_fns.extend(white_filenames)
        logger.info("white_fns set to {}".format(self.white_fns))