Beispiel #1
0
    def handle_node(self, msg):
        meta_data = QueueNode.get_metadata(self.config.work_dir,
                                           msg["task"]["nid"])
        payload = QueueNode.get_payload(self.config.work_dir, meta_data)

        # fixme: determine globally based on all seen regulars
        t_dyn = self.t_soft + 1.2 * meta_data["info"]["performance"]
        self.q.set_timeout(min(self.t_hard, t_dyn))

        try:
            results, new_payload = self.logic.process_node(payload, meta_data)
        except QemuIOException:
            # mark node as crashing and free it before escalating
            logger.info("Qemu execution failed for node %d." % meta_data["id"])
            results = self.logic.create_update(meta_data["state"],
                                               {"crashing": True})
            self.conn.send_node_abort(meta_data["id"], results)
            raise

        if new_payload:
            default_info = {
                "method": "validate_bits",
                "parent": meta_data["id"]
            }
            if self.validate_bits(new_payload, meta_data, default_info):
                logger.debug(
                    "%s Stage %s found alternative payload for node %d" %
                    (self, meta_data["state"]["name"], meta_data["id"]))
            else:
                logger.warn(
                    "%s Provided alternative payload found invalid - bug in stage %s?"
                    % (self, meta_data["state"]["name"]))
        self.conn.send_node_done(meta_data["id"], results, new_payload)
Beispiel #2
0
    def loop(self):
        if not self.q.start():
            return

        logger.info("%s is ready." % self)
        while True:
            try:
                msg = self.conn.recv()
            except ConnectionResetError:
                logger.error("%s Lost connection to Manager. Shutting down." %
                             self)
                return

            if self.config.log_crashes:
                # reset logs for each new seed/input
                for _, logfile in self.qemu_logfiles.items():
                    if os.path.exists(logfile):
                        os.truncate(logfile, 0)

            if msg["type"] == MSG_RUN_NODE:
                self.handle_node(msg)
            elif msg["type"] == MSG_IMPORT:
                self.handle_import(msg)
            elif msg["type"] == MSG_BUSY:
                self.handle_busy()
            else:
                raise ValueError("Unknown message type {}".format(msg))
Beispiel #3
0
def worker_loader(pid, config):
    def sigterm_handler(signal, frame):
        if worker.q:
            worker.q.async_exit()
        sys.exit(0)

    logger.debug(("Worker-%02d PID: " % pid) + str(os.getpid()))
    # sys.stdout = open("worker_%d.out"%pid, "w")
    #config = FuzzerConfiguration()

    psutil.Process().cpu_affinity([pid + config.cpu_offset])

    connection = ClientConnection(pid, config)

    rand.reseed()

    worker = WorkerTask(pid, config, connection)

    signal.signal(signal.SIGTERM, sigterm_handler)
    os.setpgrp()

    try:
        worker.loop()
    except QemuIOException:
        # try to restart here, if Qemu is really dead?
        pass
    finally:
        if worker.q:
            worker.q.async_exit()
        logger.info("Worker-%02d Exit." % pid)
Beispiel #4
0
def generate_traces(config, nproc, input_list):

    trace_dir = config.input + "/traces/"

    # TODO What is the effect of not defining a trace region? will it trace?
    if not config.ip0:
        logger.warn("No trace region configured!")
        return None

    os.makedirs(trace_dir, exist_ok=True)

    work_queue = list()
    for input_path, nid, _ in input_list:

        # FIXME: should fully separate decode step to decide more flexibly which
        # type of traces to decode all of these can be relevant: runtime 'fuzz',
        # 'cov' (separate kafl_cov) or noise (additional traces generated from
        # noisy targets)
        # generate own cov_NNNNN.bin files for decoding
        dump_file = "%s/cov_%05d.bin.lz4" % (trace_dir, nid)
        trace_file = "%s/cov_%05d.lst.lz4" % (trace_dir, nid)
        # pickup existing fuzz_NNNNN.bin or generate them here for decoding
        dump_file = "%s/fuzz_%05d.bin.lz4" % (trace_dir, nid)
        trace_file = "%s/fuzz_%05d.lst.lz4" % (trace_dir, nid)
        work_queue.append((input_path, dump_file, trace_file))

    chunksize = ceil(len(work_queue) / nproc)
    offset = 0
    workers = list()

    try:
        for pid in range(nproc):
            sublist = work_queue[offset:offset + chunksize]
            offset += chunksize
            if len(sublist) > 0:
                worker = mp.Process(target=generate_traces_worker,
                                    args=(config, pid, sublist))
                worker.start()
                workers.append(worker)

        for worker in workers:
            while worker.is_alive():
                time.sleep(2)
            if worker.exitcode != 0:
                return None

    except KeyboardInterrupt:
        logger.info("Received Ctrl-C, closing Workers...")
        return None
    except Exception:
        return None
    finally:
        graceful_exit(workers)

    return trace_dir
Beispiel #5
0
    def trace_payload(self, data, info):
        # Legacy implementation of -trace (now -trace_cb) using libxdc_edge_callback hook.
        # This is generally slower and produces different bitmaps so we execute it in
        # a different phase as part of calibration stage.
        # Optionally pickup pt_trace_dump* files as well in case both methods are enabled.
        trace_edge_in = self.config.work_dir + "/redqueen_workdir_%d/pt_trace_results.txt" % self.pid
        trace_dump_in = self.config.work_dir + "/pt_trace_dump_%d" % self.pid
        trace_edge_out = self.config.work_dir + "/traces/fuzz_cb_%05d.lst" % info[
            'id']
        trace_dump_out = self.config.work_dir + "/traces/fuzz_cb_%05d.bin" % info[
            'id']

        logger.info("%s Tracing payload_%05d.." % (self, info['id']))

        if len(data) > self.payload_limit:
            data = data[:self.payload_limit]

        try:
            self.q.set_payload(data)
            old_timeout = self.q.get_timeout()
            self.q.set_timeout(0)
            self.q.set_trace_mode(True)
            exec_res = self.q.send_payload()

            self.q.set_trace_mode(False)
            self.q.set_timeout(old_timeout)

            if os.path.exists(trace_edge_in):
                with open(trace_edge_in, 'rb') as f_in:
                    with lz4.LZ4FrameFile(trace_edge_out + ".lz4",
                                          'wb',
                                          compression_level=lz4.
                                          COMPRESSIONLEVEL_MINHC) as f_out:
                        shutil.copyfileobj(f_in, f_out)

            if os.path.exists(trace_dump_in):
                with open(trace_dump_in, 'rb') as f_in:
                    with lz4.LZ4FrameFile(trace_dump_out + ".lz4",
                                          'wb',
                                          compression_level=lz4.
                                          COMPRESSIONLEVEL_MINHC) as f_out:
                        shutil.copyfileobj(f_in, f_out)

            if not exec_res.is_regular():
                self.statistics.event_reload(exec_res.exit_reason)
                self.q.reload()
        except Exception as e:
            logger.info("%s Failed to produce trace %s: %s (skipping..)" %
                        (self, trace_edge_out, e))
            return None

        return exec_res
Beispiel #6
0
    def handle_busy(self):
        busy_timeout = 4
        kickstart = self.config.kickstart

        if kickstart:
            logger.debug("%s No inputs in queue, attempting kickstart(%d).." %
                         (self, kickstart))
            self.q.set_timeout(self.t_hard)
            self.logic.process_kickstart(kickstart)
        else:
            logger.info("%s No inputs in queue, sleeping %ds.." %
                        (self, busy_timeout))
            time.sleep(busy_timeout)
        self.conn.send_ready()
Beispiel #7
0
def graceful_exit(workers):
    for w in workers:
        w.terminate()

    logger.info("Waiting for Worker to shutdown...")
    time.sleep(1)

    while len(workers) > 0:
        for w in workers:
            if w and w.exitcode is None:
                logger.info(
                    "Still waiting on %s (pid=%d)..  [hit Ctrl-c to abort..]" %
                    (w.name, w.pid))
                w.join(timeout=1)
            else:
                workers.remove(w)
Beispiel #8
0
def main():
    global null_hash

    print_banner("kAFL Coverage Analyzer")

    if not self_check():
        return -1

    parser = ConfigArgsParser()
    config = parser.parse_debug_options()

    if not post_self_check(config):
        return -1

    #print(repr(config))
    #sys.exit(0)

    init_logger(config)

    data_dir = config.input

    null_hash = ExecutionResult.get_null_hash(config.bitmap_size)

    nproc = min(config.processes, os.cpu_count())
    logger.info("Using %d/%d cores..." % (nproc, os.cpu_count()))

    logger.info("Scanning target data_dir »%s«..." % data_dir)
    input_list = get_inputs_by_time(data_dir)

    start = time.time()
    logger.info("Generating traces...")
    trace_dir = generate_traces(config, nproc, input_list)
    end = time.time()
    logger.info("\n\nDone. Time taken: %.2fs\n" % (end - start))

    if not trace_dir:
        return -1

    logger.info("Parsing traces...")
    trace_parser = TraceParser(trace_dir)
    trace_parser.parse_trace_list(nproc, input_list)
    # TODO: store parsed traces here here and share class with other tools

    # generate basic summary files
    trace_parser.gen_reports()

    return 0
Beispiel #9
0
    def audit(self, bitmap):

        if len(bitmap) != self.bitmap_size:
            logger.info("bitmap size: %d" % len(bitmap))

        new_bytes = 0
        new_bits = 0
        for idx in range(self.bitmap_size):
            if bitmap[idx] != 0x00:
                if self.alt_bitmap[idx] == 0x00:
                    self.alt_bitmap[idx] = bitmap[idx]
                    new_bytes += 1
                else:
                    new_bits += 1
        if new_bytes > 0:
            self.alt_edges += new_bytes
            logger.info(
                "%s New bytes: %03d, bits: %03d, total edges seen: %03d" %
                (self, new_bytes, new_bits, self.alt_edges))
Beispiel #10
0
def start(config):

    assert prepare_working_dir(config), "Failed to create work_dir %s" % config.work_dir

    if not post_self_check(config):
        return -1

    work_dir = config.work_dir
    init_logger(config)

    # Without -ip0, Qemu will not active PT tracing and Redqueen will not
    # attempt to handle debug traps. This is a requirement for modes like gdb.
    if not config.ip0:
        logger.warn("No trace region configured! Intel PT disabled!")

    max_execs = config.iterations

    try:
        # TODO: noise, benchmark, trace are working, others untested
        mode = config.action
        if   (mode == "noise"):         debug_non_det(config, max_execs)
        elif (mode == "benchmark"):     benchmark(config)
        elif (mode == "gdb"):           gdb_session(config, qemu_verbose=True)
        elif (mode == "single"):        execute_once(config, qemu_verbose=False)
        elif (mode == "trace"):         debug_execution(config, max_execs)
        elif (mode == "trace-qemu"):    debug_execution(config, max_execs, qemu_verbose=True)
        elif (mode == "printk"):        debug_execution(config, 1, qemu_verbose=True, notifiers=False)
        elif (mode == "redqueen"):      redqueen_dbg(config, qemu_verbose=False)
        elif (mode == "redqueen-qemu"): redqueen_dbg(config, qemu_verbose=True)
        elif (mode == "verify"):        verify_dbg(config, qemu_verbose=True)
        else:
            logger.error("Unknown debug mode. Exit")
        logger.info("Done. Check logs for details.")
    except KeyboardInterrupt:
        logger.info("Received Ctrl-C, aborting...")
    except Exception as e:
        raise e

    time.sleep(0.2) # Qemu can take a moment to exit
    qemu_sweep("Any remaining qemu instances should be GC'ed on exit:")

    return 0
Beispiel #11
0
    def debug_payload(self):

        self.set_timeout(0)
        #self.send_payload()
        while True:
            self.run_qemu()
            result = self.qemu_aux_buffer.get_result()
            if result.page_fault:
                logger.warn("Page fault encountered!")
            if result.pt_overflow:
                logger.warn("PT trashed!")
            if result.exec_code == RC.HPRINTF:
                self.handle_hprintf()
                continue
            if result.exec_code == RC.ABORT:
                self.handle_habort()

        logger.info("Result: %s\n" % self.exit_reason(result))
        #self.audit(result)
        return result
Beispiel #12
0
    def coverage_totals(self):
        unique_bbs = set()
        unique_edges = dict()
        unique_traces = 0

        for _, findings in self.trace_results:
            if findings:
                unique_traces += 1
                unique_bbs.update(findings['bbs'])
                edges = findings['edges']
                for edge, num in edges:
                    if edge in unique_edges:
                        unique_edges[edge] += edges[edge]
                    else:
                        unique_edges[edge] = num

        logger.info(" Processed %d traces with a total of %d BBs (%d edges)." \
                % (unique_traces, len(unique_bbs), len(unique_edges)))

        return unique_edges, unique_bbs
Beispiel #13
0
def execute_once(config, qemu_verbose=False, notifiers=True):
    payload_file = config.input
    resume = config.resume
    null_hash = ExecutionResult.get_null_hash(config.bitmap_size)

    logger.info("Execute payload %s.. " % payload_file)

    q = qemu(1337, config, debug_mode=False, notifiers=notifiers, resume=resume)
    assert q.start(), "Failed to start Qemu?"


    store_traces = config.trace
    if store_traces:
        trace_out = config.work_dir + "/redqueen_workdir_1337/pt_trace_results.txt"
        trace_dir  = config.work_dir + "/traces/"

    payload = read_binary_file(payload_file)

    payload_limit = q.get_payload_limit()
    if len(payload) > payload_limit:
        payload = payload[:payload_limit]

    q.set_payload(payload)
    #q.send_payload() ## XXX first run has different trace?!
    if store_traces:
        result = q.execute_in_trace_mode()
    else:
        result = q.send_payload()

    print("Exit reason: %s" % result.exit_reason)

    current_hash = result.hash()
    logger.info("Feedback Hash: " + current_hash)
    if null_hash == current_hash:
        logger.warn("Null hash returned!")

    if store_traces:
        shutil.copyfile(trace_out, trace_dir + "/trace_%s_%s.txt" % (os.path.basename(payload_file),current_hash))

    q.shutdown()
    return 0
Beispiel #14
0
 def wait(self, timeout=None):
     results = []
     r, w, e = select.select(self.clients, (), (), timeout)
     for sock_ready in r:
         if sock_ready == self.listener:
             c = self.listener.accept()
             self.clients.append(c)
             self.clients_seen += 1
         else:
             try:
                 msg = sock_ready.recv_bytes()
                 msg = msgpack.unpackb(msg, strict_map_key=False)
                 results.append((sock_ready, msg))
             except (EOFError, IOError):
                 sock_ready.close()
                 self.clients.remove(sock_ready)
                 logger.info("Worker disconnected (remaining %d/%d)." %
                             (len(self.clients) - 1, self.clients_seen))
                 if len(self.clients) == 1:
                     raise SystemExit("All Workers exited.")
     return results
Beispiel #15
0
def redqueen_dbg(config, qemu_verbose=False):
    global thread_done
    logger.info("Starting Redqueen debug...")

    q = qemu(1337, config, debug_mode=True)
    q.start()
    payload = read_binary_file(config.input)
    # q.set_payload(payload)

    if os.path.exists("patches"):
        shutil.copyfile("patches", "/tmp/redqueen_workdir_1337/redqueen_patches.txt")

    start = time.time()

    thread = Thread(target=lambda: redqueen_dbg_thread(q))
    thread.start()
    result = q.execute_in_redqueen_mode(payload, debug_mode=True)
    thread_done = True
    thread.join()
    requeen_print_state(q)
    end = time.time()

    if result:
        logger.info("Execution succeded!")
    else:
        logger.error("Execution failed!")

    logger.info("Time: " + str(end - start) + "t/s")

    num_muts, muts = parser.parse_rq_data(
        open("/tmp/kafl_debug_workdir/redqueen_workdir_1337/redqueen_results.txt").read(), payload)
    count = 0
    for offset in muts:
        for lhs in muts[offset]:
            for rhs in muts[offset][lhs]:
                count += 1
                logger.info(offset, lhs, rhs)
    logger.info(count)

    return 0
Beispiel #16
0
    def start(self):

        if self.exiting:
            return False

        self.persistent_runs = 0

        if self.pid not in [0, 1337]:
            final_cmdline = ""
        else:
            final_cmdline = "\n" + self.config.qemu_path
            for arg in self.cmd:
                if arg[0] == '-':
                    final_cmdline += '\n\t' + arg
                else:
                    final_cmdline += ' ' + arg

        logger.info("%s Launching virtual machine...%s" %
                    (self, final_cmdline))

        # Launch Qemu. stderr to stdout, stdout is logged on VM exit
        # os.setpgrp() prevents signals from being propagated to Qemu, instead allowing an
        # organized shutdown via async_exit()
        self.process = subprocess.Popen([self.config.qemu_path] + self.cmd,
                                        preexec_fn=os.setpgrp,
                                        stdin=subprocess.DEVNULL)
        #stdin=subprocess.PIPE,
        #stdout=subprocess.PIPE,
        #stderr=subprocess.STDOUT)

        try:
            self.__qemu_connect()
            self.__qemu_handshake()
        except (OSError, BrokenPipeError) as e:
            if not self.exiting:
                logger.error("%s Failed to launch Qemu: %s" % (self, str(e)))
                self.shutdown()
            return False

        return True
Beispiel #17
0
def benchmark(config):
    logger.info("Starting benchmark...")
    payload_file = config.input
    payload = read_binary_file(payload_file)

    q = qemu(1337, config, debug_mode=False)
    q.start()
    try:
        q.set_payload(payload)
        res = q.send_payload()

        logger.info("Payload hash: " + str(res.hash()))
        logger.info("Payload exit: " + res.exit_reason)
        logger.info("Calibrating...")

        start = time.time()
        iterations = 0
        while (time.time() - start < 1):
            q.set_payload(payload)
            q.send_payload()
            iterations += 1

        #logger.info("Calibrate to run at %d execs/s..." % iterations)
        rounds = 0
        runtime = 0
        total = 0
        while True:
            start = time.time()
            for _ in range(int(REFRESH*iterations)):
                q.set_payload(payload)
                q.send_payload()
            rounds += 1
            runtime = time.time() - start
            total += runtime
            print(color.FLUSH_LINE + "Performance: %.2f execs/s" % (iterations / runtime), end='\r')
    except Exception as e:
        logger.warn(repr(e))
    except KeyboardInterrupt:
        pass
    finally:
        print("\nPerformance Average: %.2f execs/s\n" % (rounds*iterations/total))
        q.shutdown()
    return 0
Beispiel #18
0
def gdb_session(config, qemu_verbose=True, notifiers=True):

    #from pprint import pprint
    payload_file = config.input
    resume = config.resume

    config.gdbserver = True
    q = qemu(1337, config, notifiers=notifiers, resume=resume)

    logger.info("Starting Qemu + GDB with payload %s" % payload_file)
    logger.info("Connect with gdb to release guest from reset (localhost:1234)")
    try:
        if q.start():
            q.set_payload(read_binary_file(payload_file))
            result = q.debug_payload()
            logger.info("Thank you for playing.")
            #pprint(result._asdict())
    finally:
        logger.info("Shutting down..")
        q.async_exit()
Beispiel #19
0
def debug_execution(config, execs, qemu_verbose=False, notifiers=True):
    logger.info("Starting debug execution...(%d rounds)" % execs)

    payload_file = config.input
    resume = config.resume
    null_hash = ExecutionResult.get_null_hash(config.bitmap_size)

    q = qemu(1337, config, debug_mode=True, notifiers=notifiers, resume=resume)
    assert q.start(), "Failed to start Qemu?"

    payload = read_binary_file(payload_file)
    payload_limit = q.get_payload_limit()

    if len(payload) > payload_limit:
        payload = payload[:payload_limit]

    start = time.time()
    for i in range(execs):
        logger.info("Launching payload %d/%d.." % (i+1,execs))
        if i % 3 == 0:
            q.set_payload(payload)
        # time.sleep(0.01 * rand.int(0, 9))
        # a = str(q.send_payload())
        # hexdump(a)
        result = q.send_payload()

        current_hash = result.hash()
        logger.info("Feedback Hash: " + current_hash)
        if null_hash == current_hash:
            logger.warn("Null hash returned!")

        if result.is_crash():
            q.reload()

    q.shutdown()
    end = time.time()
    logger.info("Performance: " + str(execs / (end - start)) + "t/s")

    return 0
Beispiel #20
0
    def gen_reports(self):
        unique_bbs = set()
        unique_edges = dict()
        input_to_new_bbs = list()

        plot_file = self.trace_dir + "/coverage.csv"
        edges_file = self.trace_dir + "/edges_uniq.lst"

        with open(plot_file, 'w') as f:
            num_bbs = 0
            num_edges = 0
            num_traces = 0
            for timestamp, findings in self.trace_results:
                if not findings: continue

                new_bbs = len(findings['bbs'] - unique_bbs)
                new_edges = len(set(findings['edges']) - set(unique_edges))
                unique_bbs.update(findings['bbs'])
                edges = findings['edges']
                for edge, num in edges.items():
                    if edge in unique_edges:
                        unique_edges[edge] += edges[edge]
                    else:
                        unique_edges[edge] = num

                num_traces += 1
                num_bbs += new_bbs
                num_edges += new_edges
                f.write("%d;%d;%d\n" % (timestamp, num_bbs, num_edges))

        with open(edges_file, 'w') as f:
            for edge, num in unique_edges.items():
                f.write("%s,%x\n" % (edge, num))

        logger.info(" Processed %d traces with a total of %d BBs (%d edges)." \
                % (num_traces, num_bbs, num_edges))

        logger.info(" Plot data written to %s" % plot_file)
        logger.info(" Unique edges written to %s" % edges_file)

        return unique_edges, unique_bbs
Beispiel #21
0
    def shutdown(self):
        logger.info("%s Shutting down Qemu after %d execs.." %
                    (self, self.persistent_runs))

        if not self.process:
            # start() has never been called, all files/shm are closed.
            return 0

        # If Qemu exists, try to graciously read its I/O and SIGTERM it.
        # If still alive, attempt SIGKILL or loop-wait on kill -9.
        output = ""
        try:
            self.process.terminate()
            output = strdump(self.process.communicate(timeout=1)[0],
                             verbatim=True)
        except:
            pass

        if self.process.returncode is None:
            try:
                self.process.kill()
            except:
                pass

        logger.file_log(
            "INFO", "%s exit code: %s" % (self, str(self.process.returncode)))

        if len(output) > 0:
            header = "\n=================<%s Console Output>==================\n" % self
            footer = "====================</Console Output>======================\n"
            logger.file_log("INFO", header + output + footer)

        # on full debug, also include the serial log at point of Qemu exit
        serial_out = strdump(read_binary_file(self.serial_logfile),
                             verbatim=True)
        if len(serial_out) > 0:
            header = "\n=================<%s Serial Output>==================\n" % self
            footer = "====================</Serial Output>======================\n"
            logger.file_log("INFO", header + serial_out + footer)

        try:
            # TODO: exec_res keeps from_buffer() reference to kafl_shm
            self.kafl_shm.close()
        except BufferError as e:
            pass

        try:
            self.fs_shm.close()
        except:
            pass

        try:
            os.close(self.kafl_shm_f)
        except:
            pass

        try:
            os.close(self.fs_shm_f)
        except:
            pass

        for tmp_file in [
                self.qemu_aux_buffer_filename, self.payload_filename,
                self.control_filename, self.ijonmap_filename,
                self.bitmap_filename
        ]:
            try:
                os.remove(tmp_file)
            except:
                pass

        self.redqueen_workdir.rmtree()
        return self.process.returncode
Beispiel #22
0
def debug_non_det(config, max_execs=0):
    logger.info("Starting non-deterministic...")

    delay = 0
    payload_file = config.input
    resume = config.resume
    null_hash = ExecutionResult.get_null_hash(config.bitmap_size)

    assert os.path.isfile(payload_file), "Provided -input argument must be a file."
    assert "ip0" in config, "Must set -ip0 range in order to obtain PT traces."
    payload = read_binary_file(payload_file)

    q = qemu(1337, config, debug_mode=False, resume=resume)
    assert q.start(), "Failed to launch Qemu."

    q.set_timeout(0)

    store_traces = config.trace
    if store_traces:
        trace_out = config.work_dir + "/redqueen_workdir_1337/pt_trace_results.txt"
        trace_dir  = config.work_dir + "/noise/"
        os.makedirs(trace_dir, exist_ok=True)

    payload_limit = q.get_payload_limit()

    if len(payload) > payload_limit:
        payload = payload[:payload_limit]

    hash_value = None
    first_hash = None
    hashes = dict()
    try:
        q.set_payload(payload)

        ## XXX first run has different trace?!
        #if store_traces: 
        #    exec_res = q.execute_in_trace_mode()
        #else:
        #    exec_res = q.send_payload()

        time.sleep(delay)

        if store_traces: 
            exec_res = q.execute_in_trace_mode()
        else:
            exec_res = q.send_payload()

        first_hash = exec_res.hash()
        hashes[first_hash] = 1

        logger.info("Null Hash:  " + null_hash)
        logger.info("First Hash: " + first_hash)

        if store_traces:
            shutil.copyfile(trace_out, trace_dir + "/trace_%s_%s.txt" % (os.path.basename(payload_file),first_hash))

        total = 0
        iterations = 1
        hash_mismatch = 0
        time.sleep(delay)
        while max_execs == 0 or iterations <= max_execs:
            start = time.time()
            execs = 0
            while (time.time() - start < REFRESH):
                # restart Qemu every time?
                #q.async_exit()
                #q = qemu(0, config, debug_mode=False, resume=resume)
                #assert q.start(), "Failed to launch Qemu."
                q.set_payload(payload)
                time.sleep(delay)
                if store_traces: 
                    exec_res = q.execute_in_trace_mode()
                else:
                    exec_res = q.send_payload()

                if exec_res.is_crash():
                    logger.info("\nExit reason `%s` - restarting..." % exec_res.exit_reason)
                    q.reload()

                time.sleep(delay)
                hash_value = exec_res.hash()
                if hash_value in hashes:
                    hashes[hash_value] = hashes[hash_value] + 1
                else:
                    hashes[hash_value] = 1
                    if store_traces:
                        shutil.copyfile(trace_out, trace_dir + "/trace_%s_%s.txt" % (os.path.basename(payload_file), hash_value))
                if hash_value != first_hash:
                    hash_mismatch += 1
                execs += 1
            runtime = time.time() - start
            total += runtime
            iterations += execs
            noise = hash_mismatch*100/iterations
            code = color.FAIL if (len(hashes) != 1) else color.OKGREEN
            print(color.FLUSH_LINE +
                    "Perf: %7.2f execs/s, Execs: %7d, Mismatches: %s %4d %s, Noise %3d" %
                    (execs / runtime, iterations, code, hash_mismatch, color.ENDC, noise), end='\r')

    except Exception as e:
        logger.warn(repr(e))
    except KeyboardInterrupt:
        pass
    finally:
        print("\nOverall Perf: %7.2f execs/s, Execs: %7d, Mismatches: %s %4d %s, Noise %3d" %
                (iterations / total, iterations, code, hash_mismatch, color.ENDC, noise))
        q.shutdown()

    for h in hashes.keys():
        if h == first_hash:
            logger.info("* %s: %03d" % (h, hashes[h]))
        else:
            logger.info("  %s: %03d" % (h, hashes[h]))

    return 0
Beispiel #23
0
def verify_dbg(config, qemu_verbose=False):
    global thread_done

    logger.info("Starting...")

    rq_state = RedqueenState()
    workdir = RedqueenWorkdir(1337)

    if os.path.exists("patches"):
        with open("patches", "r") as f:
            for x in f.readlines():
                rq_state.add_candidate_hash_addr(int(x, 16))
    if not rq_state.get_candidate_hash_addrs():
        logger.warn("No patches configured\nMaybe add ./patches with addresses to patch.")
    else:
        logger.info("OK: got patches %s\n", rq_state.get_candidate_hash_addrs())
    q = qemu(1337, config, debug_mode=True)

    logger.info("using qemu command:\n%s\n" % q.cmd)

    q.start()

    orig_input = read_binary_file(config.input)
    q.set_payload(orig_input)

    # result = q.send_payload()

    with open(q.redqueen_workdir.whitelist(), "w") as w:
        with open(q.redqueen_workdir.patches(), "w") as p:
            for addr in rq_state.get_candidate_hash_addrs():
                addr = hex(addr).rstrip("L").lstrip("0x") + "\n"
                w.write(addr)
                p.write(addr)

    logger.info("RUN WITH PATCHING:")
    bmp1 = q.send_payload(apply_patches=True)

    logger.info("\nNOT PATCHING:")
    bmp2 = q.send_payload(apply_patches=False)

    if bmp1 == bmp2:
        logger.warn("Patches don't seem to change anything, are checksums present?")
    else:
        logger.info("OK: bitmaps are distinct")

    q.soft_reload()

    hash = HashFixer(q, rq_state)

    logger.info("fixing hashes")
    fixed_payload = hash.try_fix_data(orig_input)
    if fixed_payload:

        logger.info("%s\n", repr("".join(map(chr, fixed_payload))))

        q.set_payload(fixed_payload)

        bmp3 = q.send_payload(apply_patches=False)

        if bmp1 == bmp3:
            logger.info("CONGRATZ, BITMAPS ARE THE SAME, all cmps fixed\n")
        else:
            logger.warn("After fixing cmps, bitmaps differ\n")
    else:
        logger.error("couldn't fix payload\n")

    start = time.time()
    return 0