Exemplo n.º 1
0
    def __qemu_handshake(self):

        self.run_qemu()

        self.qemu_aux_buffer = QemuAuxBuffer(self.qemu_aux_buffer_filename)
        if not self.qemu_aux_buffer.validate_header():
            logger.error("%s Invalid header in qemu_aux_buffer.py. Abort." %
                         self)
            self.async_exit()

        while self.qemu_aux_buffer.get_state() != 3:
            logger.debug("%s Waiting for target to enter fuzz mode.." % self)
            self.run_qemu()
            result = self.qemu_aux_buffer.get_result()
            if result.exec_code == RC.ABORT:
                self.handle_habort()
            if result.exec_code == RC.HPRINTF:
                self.handle_hprintf()

        logger.debug("%s Handshake done." % self)

        # for -R = {0,1}, set reload_mode here just once
        if self.config.reload == 1:
            self.qemu_aux_buffer.set_reload_mode(True)
        else:
            self.qemu_aux_buffer.set_reload_mode(False)
        self.qemu_aux_buffer.set_timeout(self.config.timeout_hard)

        return
Exemplo n.º 2
0
def prepare_working_dir(config):

    work_dir = config.work_dir
    purge = config.purge
    resume = config.resume

    folders = [
        "/corpus/regular", "/corpus/crash", "/corpus/kasan", "/corpus/timeout",
        "/metadata", "/bitmaps", "/imports", "/snapshot", "/funky", "/traces",
        "/logs"
    ]

    if purge:
        shutil.rmtree(work_dir, ignore_errors=True)

    try:
        for folder in folders:
            os.makedirs(work_dir + folder, exist_ok=resume)
    except:
        logger.error(
            "Refuse to operate on existing work_dir without --purge or --resume"
        )
        return False

    return True
Exemplo n.º 3
0
    def send_payload(self):

        if self.exiting:
            sys.exit(0)

        # for -R > 1, count and toggle reload_mode at runtime
        if self.config.reload > 1:
            self.persistent_runs += 1
            if self.persistent_runs == 1:
                self.qemu_aux_buffer.set_reload_mode(False)
            if self.persistent_runs >= self.config.reload:
                self.qemu_aux_buffer.set_reload_mode(True)
                self.persistent_runs = 0

        result = None
        old_address = 0
        start_time = time.time()

        while True:
            self.run_qemu()

            result = self.qemu_aux_buffer.get_result()

            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()

            if result.exec_done:
                break

            if result.page_fault:
                if result.page_fault_addr == old_address:
                    logger.error(
                        "%s Failed to resolve page after second execution! Qemu status:\n%s"
                        % (self, str(result._asdict())))
                    break
                old_address = result.page_fault_addr
                self.qemu_aux_buffer.dump_page(result.page_fault_addr)

        # record highest seen BBs
        self.bb_seen = max(self.bb_seen, result.bb_cov)

        #runtime = result.runtime_sec + result.runtime_usec/1000/1000
        res = ExecutionResult(self.c_bitmap, self.bitmap_size,
                              self.exit_reason(result),
                              time.time() - start_time)

        if result.exec_code == RC.STARVED:
            res.starved = True

        #self.audit(res.copy_to_array())
        #self.audit(bytearray(self.c_bitmap))

        return res
Exemplo n.º 4
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))
Exemplo n.º 5
0
def check_cpu_num(config):

    if 'p' not in config:
        return True

    max_cpus = int(multiprocessing.cpu_count())
    if int(config.p) > max_cpus:
        logger.error("Only %d fuzzing processes are supported..." % max_cpus)
        return False
    return True
Exemplo n.º 6
0
    def handle_habort(self):
        msg = self.qemu_aux_buffer.get_misc_buf()
        msg = msg.decode('latin-1', errors='backslashreplace')
        msg = "Guest ABORT: %s" % msg

        logger.error(msg)
        if self.hprintf_log:
            with open(self.hprintf_logfile, "a") as f:
                f.write(msg)

        self.run_qemu()
        raise QemuIOException(msg)
Exemplo n.º 7
0
def check_radamsa_location(config):
    if 'radamsa' not in config or not config.radamsa:
        return True

    radamsa_path = config.radamsa_path

    if not radamsa_path or radamsa_path == "":
        logger.error("Enabling radamsa requires --radamsa-path to be set!")
        return False

    if not os.path.exists(radamsa_path):
        logger.error("Could not find Radamsa in %s. Try ./install.sh radamsa" %
                     radamsa_path)
        return False

    return True
Exemplo n.º 8
0
    def __execute(self, data, retry=0):

        try:
            self.q.set_payload(data)
            return self.q.send_payload()
        except (ValueError, BrokenPipeError, ConnectionResetError) as e:
            if retry > 2:
                # TODO if it reliably kills qemu, perhaps log to Manager for harvesting..
                logger.error("%s Aborting due to repeated SHM/socket error." %
                             self)
                raise QemuIOException("Qemu SHM/socket failure.") from e

            logger.warn("%s Qemu SHM/socket error (retry %d)" % (self, retry))
            self.statistics.event_reload("shm/socket error")
            if not self.q.restart():
                raise QemuIOException("Qemu restart failure.") from e
        return self.__execute(data, retry=retry + 1)
Exemplo n.º 9
0
def vmx_pt_get_addrn(verbose=True):

    KVMIO = 0xAE
    KVM_VMX_PT_GET_ADDRN = KVMIO << (8) | 0xe9

    try:
        fd = open("/dev/kvm", "wb")
    except:
        logger.error("KVM-PT is not loaded!")
        return 0

    try:
        ret = ioctl(fd, KVM_VMX_PT_GET_ADDRN, 0)
    except IOError:
        logger.warn("Kernel does not support multi-range tracing!")
        ret = 1
    finally:
        fd.close()
    return ret
Exemplo n.º 10
0
def test_build():
    native_path = os.path.dirname(inspect.getfile(native_pkg))
    bitmap_paths = glob.glob(native_path + "/bitmap*so")

    if len(bitmap_paths) < 1:
        logger.warn("Attempting to build native/bitmap.so ...")

        p = subprocess.Popen(("make -C " + native_path).split(" "),
                             stdout=subprocess.PIPE,
                             stdin=subprocess.PIPE,
                             stderr=subprocess.PIPE)

        if p.wait() != 0:
            logger.error("Build failed, please check..")
            return False

    bitmap_paths = glob.glob(native_path + "/bitmap*so")
    assert len(bitmap_paths) > 0, "Failed to resolve native bitmap.so library."
    return True
Exemplo n.º 11
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
Exemplo n.º 12
0
def vmx_pt_check_addrn(config):
    if config.ip3:
        ip_ranges = 4
    elif config.ip2:
        ip_ranges = 3
    elif config.ip1:
        ip_ranges = 2
    elif config.ip0:
        ip_ranges = 1
    else:
        ip_ranges = 0

    ret = vmx_pt_get_addrn()

    if ip_ranges > ret:
        logger.error(
            "Attempt to use %d PT range filters but CPU only supports %d!" %
            (ip_ranges, ret))
        return False
    return True
Exemplo n.º 13
0
def get_inputs_by_time(data_dir):
    # check if data_dir is kAFL or AFL type, then assemble sorted list of inputs/input IDs over time
    if (os.path.exists(data_dir + "/fuzzer_stats")
            and os.path.exists(data_dir + "/fuzz_bitmap")
            and os.path.exists(data_dir + "/plot_data")
            and os.path.isdir(data_dir + "/queue")):
        input_data = afl_workdir_iterator(data_dir)

    elif (os.path.exists(data_dir + "/stats")
          and os.path.isdir(data_dir + "/corpus/regular")
          and os.path.isdir(data_dir + "/metadata")):
        input_data = kafl_workdir_iterator(data_dir)
    else:
        logger.error("Unrecognized target directory type «%s». Exit." %
                     data_dir)
        sys.exit()

    # timestamps may be off slightly but payload IDs are strictly ordered by kAFL Manager
    input_data.sort(key=itemgetter(2))
    return input_data
Exemplo n.º 14
0
    def set_payload(self, payload):
        # Ensure the payload fits into SHM. Caller has to cut off since they also report findings.
        # actual payload is limited to payload_size - sizeof(uint32) - sizeof(uint8)
        assert (len(payload) <= self.payload_limit
                ), "Payload size %d > SHM limit %d. Check size/shm config" % (
                    len(payload), self.payload_limit)

        #if len(payload) > self.payload_limit:
        #    payload = payload[:self.payload_limit]
        try:
            struct.pack_into("=I", self.fs_shm, 0, len(payload))
            self.fs_shm.seek(4)
            self.fs_shm.write(payload)
            #self.fs_shm.flush()
        except ValueError:
            if self.exiting:
                sys.exit(0)
            # Qemu crashed. Could be due to prior payload but more likely harness/config is broken..
            logger.error("%s Failed to set new payload - Qemu crash?" % self)
            raise
Exemplo n.º 15
0
def copy_seed_files(working_directory, seed_directory):
    if len(os.listdir(seed_directory)) == 0:
        return False

    if len(os.listdir(working_directory)) == 0:
        return False

    i = 0
    for (directory, _, files) in os.walk(seed_directory):
        for f in files:
            path = os.path.join(directory, f)
            if os.path.exists(path):
                try:
                    copyfile(path,
                             working_directory + "/imports/" + "seed_%05d" % i)
                    i += 1
                except PermissionError:
                    logger.error("Skipping seed file %s (permission denied)." %
                                 path)
    return True
Exemplo n.º 16
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
Exemplo n.º 17
0
def check_vmx_pt():

    KVMIO = 0xAE
    KVM_VMX_PT_SUPPORTED = KVMIO << (8) | 0xe4

    try:
        fd = open("/dev/kvm", "wb")
    except:
        logger.error(
            "Unable to access /dev/kvm. Check permissions and ensure KVM is loaded."
        )
        return False

    try:
        ret = ioctl(fd, KVM_VMX_PT_SUPPORTED, 0)
    except IOError:
        logger.error("VMX_PT is not loaded!")
        return False
    fd.close()

    if ret == 0:
        logger.error("Intel PT is not supported on this CPU!")
        return False

    return True
Exemplo n.º 18
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
Exemplo n.º 19
0
def check_packages():

    deps = [
        'msgpack',
        'mmh3',
        'lz4',
        'psutil',
        'fastrand',
        'inotify',
        'pgrep',
        'pygraphviz',
        'toposort',
    ]

    import importlib
    for pkg in deps:
        try:
            importlib.import_module(pkg)
        except (ImportError):
            logger.error("Failed to import package %s - check dependencies!" %
                         pkg)
            return False

    return True
Exemplo n.º 20
0
def check_qemu_version(config):
    qemu_path = config.qemu_path

    if not qemu_path or not os.path.exists(qemu_path):
        logger.error("Could not find QEMU at %s..." % qemu_path)
        return False

    output = ""
    try:
        proc = subprocess.Popen([qemu_path, "-version"],
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE)
        output = str(proc.stdout.readline())
        proc.wait()
    except:
        logger.error("Failed to execute %s...?" % qemu_path)
        return False
    if not ("QEMU-PT" in output and "(kAFL)" in output):
        logger.error("Qemu executable at %s is missing the kAFL patch?" %
                     qemu_path)
        return False

    return True
Exemplo n.º 21
0
def check_version():
    if sys.version_info < (3, 6, 0):
        logger.error("This script requires python 3!")
        return False
    return True
Exemplo n.º 22
0
def generate_traces_worker(config, pid, work_queue):

    dump_mode = True

    def sigterm_handler(signal, frame):
        if q:
            q.async_exit()
        sys.exit(0)

    pname = mp.current_process().name
    pnum = mp.current_process()._identity[0]

    ptdump_path = config.ptdump_path

    if config.resume:
        # spawn worker in same workdir, picking up snapshot + page_cache
        config.purge = False  # not needed?
        qemu_id = int(pnum)  # get unique qemu ID != {0,1337}
    else:
        # spawn worker in separate workdir, booting a new VM state
        config.work_dir += "_%s" % pname
        config.purge = True  # not needed?
        qemu_id = 1337  # debug instance

    prepare_working_dir(config)

    work_dir = config.work_dir
    trace_dir = config.input + "/traces/"

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

    # FIXME: really ugly switch between -trace and -dump_pt
    if dump_mode:
        print("Tracing in '-trace' mode..")
        # new dump_pt mode - translate to edge trace in separate step
        config.trace = True
        config.trace_cb = False
    else:
        # traditional -trace mode - more noisy and no bitmap to check
        print("Tracing in legacy '-trace_cb' mode..")
        config.trace = False
        config.trace_cb = True

    q = qemu(qemu_id, config, debug_mode=False)
    if not q.start():
        logger.error("%s: Could not start Qemu. Exit." % pname)
        return None

    pbar = tqdm(total=len(work_queue),
                desc=pname,
                dynamic_ncols=True,
                smoothing=0.1,
                position=pid + 1)

    f = tempfile.NamedTemporaryFile(delete=False)
    tmpfile = f.name
    f.close()

    try:
        for input_path, dump_file, trace_file in work_queue:
            print("\nProcessing %s.." % os.path.basename(input_path))

            if dump_mode:
                # -trace mode (pt dump)
                if not os.path.exists(dump_file):
                    qemu_file = work_dir + "/pt_trace_dump_%d" % qemu_id
                    if simple_trace_run(q, read_binary_file(input_path),
                                        q.send_payload):
                        with open(qemu_file, 'rb') as f_in:
                            with lz4.LZ4FrameFile(
                                    dump_file,
                                    'wb',
                                    compression_level=lz4.
                                    COMPRESSIONLEVEL_MINHC) as f_out:
                                shutil.copyfileobj(f_in, f_out)

                if not os.path.exists(trace_file):
                    with tempfile.NamedTemporaryFile(delete=False) as pt_tmp:
                        with lz4.LZ4FrameFile(dump_file, 'rb') as pt_dump_lz4:
                            shutil.copyfileobj(pt_dump_lz4, pt_tmp)
                        pt_tmp.close()

                        cmd = [
                            ptdump_path, work_dir + "/page_cache", pt_tmp.name,
                            tmpfile
                        ]
                        for i in range(2):
                            key = "ip" + str(i)
                            if getattr(config, key, None):
                                range_a = hex(getattr(config, key)[0]).replace(
                                    "L", "")
                                range_b = hex(getattr(config, key)[1]).replace(
                                    "L", "")
                                cmd += [range_a, range_b]

                        try:
                            subprocess.run(cmd, timeout=180)
                            os.unlink(pt_tmp.name)
                        except subprocess.TimeoutExpired as e:
                            print(e)
                            os.unlink(pt_tmp.name)
                            continue

                        with open(tmpfile, 'rb') as f_in:
                            with lz4.LZ4FrameFile(
                                    trace_file,
                                    'wb',
                                    compression_level=lz4.
                                    COMPRESSIONLEVEL_MINHC) as f_out:
                                shutil.copyfileobj(f_in, f_out)

            else:
                # -trace_cb mode (libxdc callback)
                if not os.path.exists(trace_file):
                    qemu_file = work_dir + "/redqueen_workdir_%d/pt_trace_results.txt" % qemu_id
                    if simple_trace_run(q, read_binary_file(input_path),
                                        q.send_payload):
                        with open(qemu_file, 'rb') as f_in:
                            with lz4.LZ4FrameFile(
                                    trace_file,
                                    'wb',
                                    compression_level=lz4.
                                    COMPRESSIONLEVEL_MINHC) as f_out:
                                shutil.copyfileobj(f_in, f_out)
            pbar.update()
    except Exception:
        q.async_exit()
        raise
    finally:
        os.unlink(tmpfile)
    q.shutdown()
Exemplo n.º 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