1+ from typing import List , Callable
2+ from qiling .core import Qiling
3+ from unicornafl import *
4+ from qiling .exception import QlErrorNotImplemented
5+
6+ def ql_afl_fuzz (ql : Qiling ,
7+ input_file : str ,
8+ place_input_callback : Callable [["Qiling" , bytes , int ], bool ],
9+ exits : List [int ],
10+ validate_crash_callback : Callable [["Qiling" , bytes , int ], bool ] = None ,
11+ always_validate : bool = False ,
12+ persistent_iters : int = 1 ):
13+ """ Fuzz a range of code with afl++.
14+ This function wraps some common logic with unicornafl.uc_afl_fuzz.
15+ NOTE: If no afl-fuzz instance is found, this function is almost identical to ql.run.
16+ :param Qiling ql: The Qiling instance.
17+ :param str input_file: This usually is the input file name provided by the command argument.
18+ :param Callable place_input_callback: This callback is triggered every time a new child is
19+ generated. It returns True if the input is accepted, or the input would be skipped.
20+ :param list exits: All possible exits.
21+ :param Callable validate_crash_callback: This callback is triggered every time to check if we are crashed.
22+ :param bool always_validate: If this is set to False, validate_crash_callback will be only triggered if
23+ uc_emu_start (which is called internally by afl_fuzz) returns an error. Or the validate_crash_callback will
24+ be triggered every time.
25+ :param int persistent_iters: Fuzz how many times before forking a new child.
26+ :raises UcAflError: If something wrong happens with the fuzzer.
27+ """
28+
29+ def _ql_afl_place_input_wrapper (uc , input_bytes , iters , data ):
30+ (ql , cb , _ ) = data
31+
32+ return cb (ql , input_bytes , iters )
33+
34+ def _ql_afl_validate_wrapper (uc , input_bytes , iters , data ):
35+ (ql , _ , cb ) = data
36+
37+ return cb (ql , input_bytes , iters )
38+
39+ data = (ql , place_input_callback , validate_crash_callback )
40+ try :
41+ # uc_afl_fuzz will never return non-zero value.
42+ uc_afl_fuzz (ql .uc ,
43+ input_file = input_file ,
44+ place_input_callback = _ql_afl_place_input_wrapper ,
45+ exits = exits ,
46+ validate_crash_callback = _ql_afl_validate_wrapper ,
47+ always_validate = always_validate ,
48+ persistent_iters = persistent_iters ,
49+ data = data )
50+ except NameError as ex :
51+ raise QlErrorNotImplemented ("unicornafl is not installed or AFL++ is not supported on this platform" ) from ex
52+ except UcAflError as ex :
53+ if ex .errno != UC_AFL_RET_CALLED_TWICE :
54+ # This one is special. Many fuzzing scripts start fuzzing in a Unicorn UC_HOOK_CODE callback and
55+ # starts execution on the current address, which results in a duplicate UC_HOOK_CODE callback. To
56+ # make unicornafl easy to use, we handle this siliently.
57+ #
58+ # For other exceptions, we raise them.
59+ raise
0 commit comments