def __init__(self, opts): """ :opts: { debug: (boolean) Whether to print debug output, coverage: (boolean) Coverage flag is passed for instrumentation module, jobs: (int) Number of jobs to run concurrently, input_module: (string) Module to use for input generation, fuzzing_module: (string) Module to use for fuzzing the data, preprocessing_module: (string) Module to use for preprocessing the data, instrumentation_module: (string) Module to use for instrumentation } """ self._init_logging() self.scheduled_functions = [] self.process_management = ProcessManagement(opts) self.process_management.log = self.log self.process_management.new_process_handler = self.new_process_handler self.process_management.end_process_handler = self.end_process_handler # Load modules for key, item in opts.items(): if key.endswith("_module"): getattr(self, "_load_%s" % key)(item) signal.signal(signal.SIGINT, self.exit)
class Fuzzer(): """ The base fuzzer class provides a standard handler interfaces to be used from external libraries like ProcessManagement. """ def __init__(self, opts): """ :opts: { debug: (boolean) Whether to print debug output, coverage: (boolean) Coverage flag is passed for instrumentation module, jobs: (int) Number of jobs to run concurrently, input_module: (string) Module to use for input generation, fuzzing_module: (string) Module to use for fuzzing the data, preprocessing_module: (string) Module to use for preprocessing the data, instrumentation_module: (string) Module to use for instrumentation } """ self._init_logging() self.scheduled_functions = [] self.process_management = ProcessManagement(opts) self.process_management.log = self.log self.process_management.new_process_handler = self.new_process_handler self.process_management.end_process_handler = self.end_process_handler # Load modules for key, item in opts.items(): if key.endswith("_module"): getattr(self, "_load_%s" % key)(item) signal.signal(signal.SIGINT, self.exit) def _init_logging(self): """ Initialize standardized logging. """ logging.basicConfig(format="[%(asctime)s] %(levelname)s: %(message)s", datefmt="%H:%M:%S") self.log = logging.getLogger(__name__) self.log.setLevel(logging.INFO) if self.debug: self.log.setLevel(logging.DEBUG) def exit(self, *args): """ When exiting, it is necessary to signal other threads that the program is exiting. This is so far done by using "running" flags. """ self.process_management.running = False self.process_management.kill_processes() if hasattr(self, "_exit"): self._exit() sys.exit() def _load_module(self, path, module): """ Load specified modules from the ./modules directory. If module is not specified, then is should be using "default" module which does nothing. """ if not module: module = "default" self.log.debug("Using %s.%s module" % (path, module)) try: return importlib.import_module("%s.%s" % (path, module)) except ImportError as e: self.log.debug(path) self.log.debug(module) self.log.error(e) self.exit() def _load_preprocessing_module(self, module): """ See _load_module. """ preprocessing_module = self._load_module("modules.preprocessing", module) self.preprocessor = preprocessing_module.PreprocessingModule(self.log) def _load_input_module(self, module): """ See _load_module. """ input_module = self._load_module("modules.input", module) self.inputer = input_module.InputModule(self.log) def _load_fuzzing_module(self, module): """ See _load_module. """ fuzzing_module = self._load_module("modules.fuzzing", module) self.fuzzer = fuzzing_module.FuzzingModule(self.log) def _load_instrumentation_module(self, module): """ See _load_module. """ instrumentation_module = self._load_module("modules.instrumentation", module) self.instrumentation = instrumentation_module.InstrumentationModule(self.log, coverage=self.coverage) def new_process_handler(self, process): """ This handler specifies how new processes are created. Additional attributes for the Process object can be added as needed in the _new_process_handler() function. """ if not hasattr(self, "_new_process_handler"): self.log.error("_new_process_handler() does not exists") self.exit() if not hasattr(process, "instrumentation"): process.instrumentation = copy.copy(self.instrumentation) return self._new_process_handler(process) def post_start_handler(self, process): """ After the subprocess starts, pass control to the _post_start_handler() for required post-start processing. """ return self._post_start_handler(process) def data_handler(self, data, index): """ By default pass data through preprocessing and fuzzing modules. Pass all data to _data_handler() which must return single data to the caller. """ processed = self.preprocessor.preprocess(data) malformed = self.fuzzer.fuzz(processed) return self._data_handler(index, data, processed, malformed) def end_process_handler(self, process): """ When processes have ended, they are passed first to the instrumentation module and then to the _end_process_handler. Afterwards the process object will kill and replace the subprocess. NOTE The instrumentation module must make do with all the attributes of the process object. """ if not hasattr(self, "_end_process_handler"): self.log.error("end_process() function does not exists") self.exit() process.instrumentation.instrument(process) self._end_process_handler(process) def main_loop(self): """ Fuzzers that inherit the base Fuzzer class should finally pass control to the main_loop() function and if necessary, set scheduled functions to run at one second intervals. """ try: self.process_management.jobs = int(self.jobs) self.process_management.start() except TypeError: pass while True: time.sleep(1) for function in self.scheduled_functions: self.log.debug("Calling scheduled functions") function()