Пример #1
0
class DebugServerManager(ServerManager):
    def __init__(self, config, queue_sync, queue_out, targetPort):
        ServerManager.__init__(self, config, queue_sync, queue_out, targetPort)
        self.dbg = None
        self.crashEvent = None
        self.proc = None
        self.p = None

    def _startServer(self):
        # create child via ptrace debugger
        # API: createChild(arguments[], no_stdout, env=None)
        logging.debug("START: " + str(
            serverutils.getInvokeTargetArgs(self.config, self.targetPort +
                                            1000)))
        self.pid = createChild(
            serverutils.getInvokeTargetArgs(self.config, self.targetPort),
            False,  # no_stdout
            None,
        )

        # Attach to the process with ptrace and let it run
        self.dbg = PtraceDebugger()
        self.proc = self.dbg.addProcess(self.pid, True)
        self.proc.cont()

        time.sleep(1)

        # i dont think this works here...
        # FIXME
        event = self.dbg.waitProcessEvent(blocking=False)
        if event is not None and type(event) == ProcessExit:
            logging.error("Started server, but it already exited: " +
                          str(event))
            return False

        return True

    def _stopServer(self):
        try:
            self.dbg.quit()
            os.kill(self.pid, signal.SIGTERM)
        except:
            # is already dead...
            pass

    def _waitForCrash(self):
        #subprocess.call("echo AAA1; ls -l /proc/" + str(self.pid), shell=True)
        while True:
            logging.info("DebugServer: Waiting for process event")
            event = self.dbg.waitProcessEvent()
            logging.info("DebugServer: Got event: " + str(event))
            # If this is a process exit we need to check if it was abnormal
            if type(event) == ProcessExit:
                if event.signum is None or event.exitcode == 0:
                    # Clear the event since this was a normal exit
                    event = None

            # If this is a signal we need to check if we're ignoring it
            elif type(event) == ProcessSignal:
                if event.signum == signal.SIGCHLD:
                    # Ignore these signals and continue waiting
                    continue
                elif event.signum == signal.SIGTERM:
                    # server cannot be started, return
                    event = None
                    self.queue_sync.put(("err", event.signum))

            break

        if event is not None and event.signum != 15:
            logging.info("DebugServer: Event Result: Crash")
            #subprocess.call("echo AAA2; ls -l /proc/" + str(self.pid), shell=True)
            self.crashEvent = event
            return True
        else:
            logging.info("DebugServer: Event Result: No crash")
            self.crashEvent = None
            return False

    def _getCrashDetails(self):
        event = self.crashEvent
        # Get the address where the crash occurred
        faultAddress = 0
        try:
            faultAddress = event.process.getInstrPointer()
        except Exception as e:
            # process already dead, hmm
            print(("GetCrashDetails exception: " + str(e)))

        # Find the module that contains this address
        # Now we need to turn the address into an offset. This way when the process
        # is loaded again if the module is loaded at another address, due to ASLR,
        # the offset will be the same and we can correctly detect those as the same
        # crash
        module = None
        faultOffset = 0

        try:
            for mapping in event.process.readMappings():
                if faultAddress >= mapping.start and faultAddress < mapping.end:
                    module = mapping.pathname
                    faultOffset = faultAddress - mapping.start
                    break
        except Exception as error:
            print("getCrashDetails Exception: " + str(error))
            # it always has a an exception...
            pass

        # Apparently the address didn't fall within a mapping
        if module is None:
            module = "Unknown"
            faultOffset = faultAddress

        # Get the signal
        sig = event.signum

        # Get the details of the crash
        details = None

        details = ""
        stackAddr = 0
        stackPtr = 0
        backtraceFrames = None
        pRegisters = None
        try:
            if event._analyze() is not None:
                details = event._analyze().text

            # more data
            stackAddr = self.proc.findStack()
            stackPtr = self.proc.getStackPointer()

            # convert backtrace
            backtrace = self.proc.getBacktrace()
            backtraceFrames = []
            for frame in backtrace.frames:
                backtraceFrames.append(str(frame))

            # convert registers from ctype to python
            registers = self.proc.getregs()
            pRegisters = {}
            for field_name, field_type in registers._fields_:
                regName = str(field_name)
                regValue = str(getattr(registers, field_name))
                pRegisters[regName] = regValue

        except Exception as e:
            # process already dead, hmm
            print(("GetCrashDetails exception: " + str(e)))

        vCrashData = verifycrashdata.VerifyCrashData(
            faultAddress=faultAddress,
            faultOffset=faultOffset,
            module=module,
            sig=sig,
            details=details,
            stackPointer=stackPtr,
            stackAddr=str(stackAddr),
            backtrace=backtraceFrames,
            registers=pRegisters,
        )

        asanOutput = serverutils.getAsanOutput(self.config, self.pid)
        vCrashData.setTemp(asanOutput)

        return vCrashData
Пример #2
0
def doFuzz(config,
           setupEnvironment=_setupEnvironment,
           chooseInput=_chooseInput,
           generateSeed=_generateSeed,
           runFuzzer=_runFuzzer,
           runTarget=_runTarget,
           checkForCrash=_checkForCrash,
           handleOutcome=_handleOutcome):
    seed = 0
    count = 0
    haveOutcome = False
    outcome = None
    done = False

    # Step 1: Setup environment
    setupEnvironment(config)

    print "Running fuzzer:", config["fuzzer"]

    sys.stdout.write("%8d: " % (0))
    sys.stdout.flush()

    while not done:
        # Step 2: Choose an input
        inFile = chooseInput(config)

        # We're done if no input is returned
        if inFile is None:
            print "\nNo more inputs, exiting."
            break

        # Step 3: Generate a seed
        seed = generateSeed(config)

        # Generate a name for the output file
        outExt = os.path.splitext(inFile)[1]
        outFile = os.path.join(os.getcwd(), str(seed) + outExt)

        # Step 4: Run fuzzer
        runFuzzer(config, inFile, seed, outFile, count)

        # Step 5: Run the target
        pid = runTarget(config, outFile)

        #######################################################################
        # This is where the magic happens. We monitor the process to determine
        # if it has crashed
        # Attach to the process with ptrace
        dbg = PtraceDebugger()
        proc = dbg.addProcess(pid, True)
        proc.cont()

        # Calculate the maximum time the target will be allowed to run
        endTime = time.time() + config["target_timeout"]

        outcome = None
        while True:
            try:
                # Check if there is an event pending for the target applicaiton
                # This will return immediately with either an event or None if
                # there is no event. We do this so we can kill the target after
                # it reaches the timeout
                event = dbg.waitProcessEvent(blocking=False)

                # Check if the process exited
                if type(event) == ProcessExit:
                    # Step 6: Check for crash
                    outcome = checkForCrash(config, event)

                    # The target application exited so we're done here
                    break

                elif type(event) == ProcessSignal:
                    # SIGCHLD simply notifies the parent that one of it's
                    # children processes has exited or changed (exec another
                    # process). It's not a bug so we tell the process to
                    # continue and we loop again to get the next event
                    if event.signum == SIGCHLD:
                        event.process.cont()
                        continue

                    outcome = checkForCrash(config, event)
                    break

            except KeyboardInterrupt:
                done = True
                break

            # Check if the process has reached the timeout
            if time.time() >= endTime:
                break
            else:
                # Give the CPU some timeslices to run other things
                time.sleep(0.1)

        # Step 7: Handle any crashes
        if outcome is not None:
            handleOutcome(config, outcome, inFile, seed, outFile, count)

            haveOutcome = True

        # Done with the process
        proc.terminate()

        # Delete the output
        try:
            os.remove(outFile)
        except:
            print "Failed to remove file %s!" % outFile

        # Update the counter and display the visual feedback
        count += 1
        if count % 2 == 0:
            if haveOutcome:
                sys.stdout.write("!")
                haveOutcome = False
            else:
                sys.stdout.write(".")

            sys.stdout.flush()

        if count % 100 == 0:
            sys.stdout.write("\n%8d: " % count)
            sys.stdout.flush()