Пример #1
0
    def analyze_sample(self, sample_path, workdir, outdir, start_command,
                       timeout):
        analysis_info = dict()

        dns_server = self.config.config['drakrun'].get('dns_server', '8.8.8.8')
        drakmon_log_fp = os.path.join(outdir, "drakmon.log")

        with self.run_vm() as vm, \
             graceful_exit(start_dnsmasq(self.instance_id, dns_server)), \
             graceful_exit(start_tcpdump_collector(self.instance_id, outdir)), \
             open(drakmon_log_fp, "wb") as drakmon_log:

            analysis_info[
                'snapshot_version'] = vm.backend.get_vm0_snapshot_time()

            kernel_profile = os.path.join(PROFILE_DIR, "kernel.json")

            self.log.info("Copying sample to VM...")
            injector = Injector(self.vm_name, self.runtime_info,
                                kernel_profile)
            result = injector.write_file(
                sample_path,
                f"%USERPROFILE%\\Desktop\\{os.path.basename(sample_path)}")
            injected_fn = json.loads(result.stdout)['ProcessName']

            if "%f" not in start_command:
                self.log.warning("No file name in start command")
            cur_start_command = start_command.replace("%f", injected_fn)

            # don't include our internal maintanance commands
            analysis_info['start_command'] = cur_start_command
            self.log.info("Using command: %s", cur_start_command)

            if self.net_enable:
                self.log.info("Setting up network...")
                injector.create_process("cmd /C ipconfig /renew >nul",
                                        wait=True,
                                        timeout=120)

            drakvuf_cmd = self.build_drakvuf_cmdline(
                timeout=timeout,
                cwd=subprocess.list2cmdline([ntpath.dirname(injected_fn)]),
                full_cmd=cur_start_command,
                dump_dir=os.path.join(outdir, "dumps"),
                ipt_dir=os.path.join(outdir, "ipt"),
                workdir=workdir,
            )

            try:
                subprocess.run(drakvuf_cmd,
                               stdout=drakmon_log,
                               check=True,
                               timeout=timeout + 60)
            except subprocess.TimeoutExpired:
                self.log.exception("DRAKVUF timeout expired")

        return analysis_info
Пример #2
0
    def __init__(self, vm_id: int, dns: str):
        install_info = InstallInfo.load()
        backend = get_storage_backend(install_info)

        generate_vm_conf(install_info, vm_id)
        self.vm = VirtualMachine(backend, vm_id)

        with open(Path(PROFILE_DIR) / "runtime.json", 'r') as f:
            self.runtime_info = RuntimeInfo.load(f)
        self.desktop = WinPath(r"%USERPROFILE%") / "Desktop"

        self.kernel_profile = Path(PROFILE_DIR) / "kernel.json"
        self.injector = Injector(
            self.vm.vm_name,
            self.runtime_info,
            self.kernel_profile,
        )
        setup_vm_network(vm_id, True, find_default_interface(), dns)
Пример #3
0
def create_rekall_profiles(injector: Injector):
    tmp = None

    for file in dll_file_list:
        try:
            logging.info(f"Fetching rekall profile for {file.path}")

            local_dll_path = os.path.join(PROFILE_DIR, file.dest)
            guest_dll_path = str(PureWindowsPath("C:/", file.path))

            cmd = injector.read_file(guest_dll_path, local_dll_path)
            out = json.loads(cmd.stdout.decode())
            if out["Status"] == "Error" and out["Error"] in [
                    "ERROR_FILE_NOT_FOUND", "ERROR_PATH_NOT_FOUND"
            ]:
                raise FileNotFoundError
            if out["Status"] != "Success":
                logging.debug("stderr: " + cmd.stderr.decode())
                logging.debug(out)
                # Take care if the error message is changed
                raise Exception("Some error occurred in injector")

            guid = pdb_guid(local_dll_path)
            tmp = fetch_pdb(guid["filename"], guid["GUID"], PROFILE_DIR)

            logging.debug("Parsing PDB into JSON profile...")
            profile = make_pdb_profile(tmp)
            with open(os.path.join(PROFILE_DIR, f"{file.dest}.json"),
                      'w') as f:
                f.write(profile)
        except json.JSONDecodeError:
            logging.debug(f"stdout: {cmd.stdout}")
            logging.debug(f"stderr: {cmd.stderr}")
            logging.debug(traceback.format_exc())
            raise Exception(f"Failed to parse json response on {file.path}")
        except FileNotFoundError:
            logging.warning(f"Failed to copy file {file.path}, skipping...")
        except RuntimeError:
            logging.warning(
                f"Failed to fetch profile for {file.path}, skipping...")
        except Exception as e:
            # Take care if the error message is changed
            if str(e) == "Some error occurred in injector":
                raise
            else:
                logging.warning(
                    f"Unexpected exception while creating rekall profile for {file.path}, skipping..."
                )
                # Can help in debugging
                logging.debug("stderr: " + cmd.stderr.decode())
                logging.debug(out)
                logging.debug(traceback.format_exc())
        finally:
            safe_delete(local_dll_path)
            # was crashing here if the first file reached some exception
            if tmp is not None:
                safe_delete(os.path.join(PROFILE_DIR, tmp))
Пример #4
0
def create_missing_profiles():
    """
    Creates usermode profiles by restoring vm-1 and extracting the DLLs.
    Assumes that injector is configured properly, i.e. kernel and runtime
    profiles exist and that vm-1 is free to use.
    """

    # Prepare injector
    with open(os.path.join(PROFILE_DIR, "runtime.json"), "r") as runtime_f:
        runtime_info = RuntimeInfo.load(runtime_f)
    kernel_profile = os.path.join(PROFILE_DIR, "kernel.json")
    injector = Injector("vm-1", runtime_info, kernel_profile)

    # restore vm-1
    out_interface = conf["drakrun"].get("out_interface", "")
    dns_server = conf["drakrun"].get("dns_server", "")
    install_info = InstallInfo.load()
    backend = get_storage_backend(install_info)

    generate_vm_conf(install_info, 1)
    setup_vm_network(vm_id=1,
                     net_enable=False,
                     out_interface=out_interface,
                     dns_server=dns_server)
    vm = VirtualMachine(backend, 1)
    vm.restore()

    # Ensure that all declared usermode profiles exist
    # This is important when upgrade defines new entries in dll_file_list and compulsory_dll_file_list
    for profile in compulsory_dll_file_list:
        if not profiles_exist(profile.dest):
            create_rekall_profile(injector, profile, True)

    for profile in dll_file_list:
        if not profiles_exist(profile.dest):
            try:
                create_rekall_profile(injector, profile)
            except Exception:
                log.exception(
                    "Unexpected exception from create_rekall_profile!")

    build_os_info(APISCOUT_PROFILE_DIR, vmi_win_guid(vm.vm_name), backend)

    dll_basename_list = [dll.dest for dll in dll_file_list]
    static_apiscout_profile = build_static_apiscout_profile(
        APISCOUT_PROFILE_DIR, dll_basename_list)
    with open(
            Path(APISCOUT_PROFILE_DIR) / "static_apiscout_profile.json",
            "w") as f:
        json.dump(static_apiscout_profile, f)

    vm.destroy()
    delete_vm_network(vm_id=1,
                      net_enable=False,
                      out_interface=out_interface,
                      dns_server=dns_server)
Пример #5
0
def postupgrade():
    if not check_root():
        return

    with open(os.path.join(ETC_DIR, 'scripts/cfg.template'), 'r') as f:
        template = f.read()

    passwd_characters = string.ascii_letters + string.digits
    passwd = ''.join(secrets.choice(passwd_characters) for _ in range(20))
    template = template.replace('{{ VNC_PASS }}', passwd)

    with open(os.path.join(ETC_DIR, 'scripts', 'cfg.template'), 'w') as f:
        f.write(template)

    detect_defaults()

    install_info = InstallInfo.try_load()
    if not install_info:
        logging.info("Postupgrade done. DRAKVUF Sandbox not installed.")
        return

    # Prepare injector
    with open(os.path.join(PROFILE_DIR, "runtime.json"), 'r') as runtime_f:
        runtime_info = RuntimeInfo.load(runtime_f)
    kernel_profile = os.path.join(PROFILE_DIR, "kernel.json")
    injector = Injector('vm-1', runtime_info, kernel_profile)

    stop_all_drakruns()

    # Use vm-1 for generating profiles
    out_interface = conf['drakrun'].get('out_interface', '')
    dns_server = conf['drakrun'].get('dns_server', '')
    setup_vm_network(vm_id=1,
                     net_enable=False,
                     out_interface=out_interface,
                     dns_server=dns_server)
    backend = get_storage_backend(install_info)
    vm = VirtualMachine(backend, 1)
    vm.restore()

    create_missing_profiles(injector)

    vm.destroy()
    delete_vm_network(vm_id=1,
                      net_enable=False,
                      out_interface=out_interface,
                      dns_server=dns_server)
    start_enabled_drakruns()
Пример #6
0
def create_missing_profiles():
    """
    Creates usermode profiles by restoring vm-1 and extracting the DLLs.
    Assumes that injector is configured properly, i.e. kernel and runtime
    profiles exist and that vm-1 is free to use.
    """

    # Prepare injector
    with open(os.path.join(PROFILE_DIR, "runtime.json"), "r") as runtime_f:
        runtime_info = RuntimeInfo.load(runtime_f)
    kernel_profile = os.path.join(PROFILE_DIR, "kernel.json")
    injector = Injector("vm-1", runtime_info, kernel_profile)

    # restore vm-1
    out_interface = conf["drakrun"].get("out_interface", "")
    dns_server = conf["drakrun"].get("dns_server", "")
    install_info = InstallInfo.load()
    backend = get_storage_backend(install_info)

    generate_vm_conf(install_info, 1)
    setup_vm_network(vm_id=1,
                     net_enable=False,
                     out_interface=out_interface,
                     dns_server=dns_server)
    vm = VirtualMachine(backend, 1)
    vm.restore()

    # Ensure that all declared usermode profiles exist
    # This is important when upgrade defines new entries in dll_file_list and compulsory_dll_file_list
    for profile in compulsory_dll_file_list:
        if not profile_exists(profile):
            create_rekall_profile(injector, profile, True)

    for profile in dll_file_list:
        if not profile_exists(profile):
            try:
                create_rekall_profile(injector, profile)
            except Exception:
                # silence per-dll errors
                pass

    vm.destroy()
    delete_vm_network(vm_id=1,
                      net_enable=False,
                      out_interface=out_interface,
                      dns_server=dns_server)
Пример #7
0
class DrakmonShell:
    def __init__(self, vm_id: int, dns: str):

        self.cleanup(vm_id)

        install_info = InstallInfo.load()
        backend = get_storage_backend(install_info)

        generate_vm_conf(install_info, vm_id)
        self.vm = VirtualMachine(backend, vm_id)
        self._dns = dns

        with open(Path(PROFILE_DIR) / "runtime.json", "r") as f:
            self.runtime_info = RuntimeInfo.load(f)
        self.desktop = WinPath(r"%USERPROFILE%") / "Desktop"

        self.kernel_profile = Path(PROFILE_DIR) / "kernel.json"
        self.injector = Injector(
            self.vm.vm_name,
            self.runtime_info,
            self.kernel_profile,
        )
        setup_vm_network(vm_id, True, find_default_interface(), dns)

    def cleanup(self, vm_id: int):

        logging.info(f"Ensuring that drakrun@{vm_id} service is stopped...")
        try:
            subprocess.run(
                ["systemctl", "stop", f"drakrun@{vm_id}"],
                stderr=subprocess.STDOUT,
                check=True,
            )
        except subprocess.CalledProcessError:
            raise Exception(f"drakrun@{vm_id} not stopped")

    def drakvuf(self, plugins, timeout=60):
        d = tempfile.TemporaryDirectory(prefix="drakvuf_")
        workdir = Path(d.name)

        log = open(workdir / "drakmon.log", "wb")

        cmd = ["drakvuf"]
        cmd.extend([
            "-o",
            "json",
            "F",
            "-j",
            "5",
            "-t",
            str(timeout),
            "-i",
            str(self.runtime_info.inject_pid),
            "-k",
            str(self.runtime_info.vmi_offsets.kpgd),
            "-r",
            self.kernel_profile,
            "-d",
            self.vm.vm_name,
            "--dll-hooks-list",
            Path(ETC_DIR) / "hooks.txt",
        ])

        if "memdump" in plugins:
            dumps = workdir / "dumps"
            dumps.mkdir()
            cmd.extend(["--memdump-dir", dumps])

        if "ipt" in plugins:
            ipt = workdir / "ipt"
            ipt.mkdir()
            cmd.extend(["--ipt-dir", ipt])

        for chk in (["-a", plugin] for plugin in plugins):
            cmd.extend(chk)

        subprocess.run(cmd, stdout=log)

        return d

    def help(self):
        usage = dedent("""\
        Available commands:
        - copy(file_path)   # copy file onto vm desktop
        - mount(iso_path)   # mount iso, useful for installing software, e.g. office
        - drakvuf(plugins)  # start drakvuf with provided set of plugins
        - run(cmd)          # run command inside vm
        - exit()            # exit playground
        """)
        print(usage)

    def copy(self, local):
        local = Path(local)
        self.injector.write_file(local, self.desktop / local.name)

    def mount(self, local_iso_path, drive=FIRST_CDROM_DRIVE):
        local_iso_path = Path(local_iso_path)
        insert_cd(self.vm.vm_name, drive, local_iso_path)

    def run(self, cmd):
        self.injector.create_process(cmd)

    def __enter__(self):
        self.vm.restore()
        return self

    def __exit__(self, exc_type, exc_value, exc_traceback):
        self.vm.destroy()
        delete_vm_network(self.vm.vm_id, True, find_default_interface(),
                          self._dns)
Пример #8
0
    def process(self):
        sample = self.current_task.get_resource("sample")
        self.log.info("hostname: {}".format(socket.gethostname()))
        sha256sum = hashlib.sha256(sample.content).hexdigest()
        magic_output = magic.from_buffer(sample.content)
        self.log.info("running sample sha256: {}".format(sha256sum))

        timeout = self.current_task.payload.get(
            'timeout') or self.default_timeout
        hard_time_limit = 60 * 20
        if timeout > hard_time_limit:
            self.log.error(
                "Tried to run the analysis for more than hard limit of %d seconds",
                hard_time_limit)
            return

        self.log.info(f"analysis UID: {self.analysis_uid}")
        self.rs.set(f"drakvnc:{self.analysis_uid}", self.instance_id,
                    ex=3600)  # 1h

        workdir = f"/tmp/drakrun/{self.vm_name}"

        extension = self.current_task.headers.get("extension", "exe").lower()
        if '(DLL)' in magic_output:
            extension = 'dll'
        self.log.info("Running file as %s", extension)

        file_name = self.current_task.payload.get("file_name",
                                                  "malwar") + f".{extension}"
        # Alphanumeric, dot, underscore, dash
        if not re.match(r"^[a-zA-Z0-9\._\-]+$", file_name):
            self.log.error("Filename contains invalid characters")
            return
        self.log.info("Using file name %s", file_name)

        # Save sample to disk here as some branches of _get_start_command require file path.
        try:
            shutil.rmtree(workdir)
        except Exception as e:
            print(e)
        os.makedirs(workdir, exist_ok=True)
        with open(os.path.join(workdir, file_name), 'wb') as f:
            f.write(sample.content)

        start_command = self.current_task.payload.get(
            "start_command",
            self._get_start_command(extension, sample,
                                    os.path.join(workdir, file_name)))
        if not start_command:
            self.log.error(
                "Unable to run malware sample, could not generate any suitable command to run it."
            )
            return

        outdir = os.path.join(workdir, 'output')
        os.mkdir(outdir)
        os.mkdir(os.path.join(outdir, 'dumps'))
        os.mkdir(os.path.join(outdir, 'ipt'))

        metadata = {
            "sample_sha256": sha256sum,
            "magic_output": magic_output,
            "time_started": int(time.time())
        }

        with open(os.path.join(outdir, 'sample_sha256.txt'), 'w') as f:
            f.write(hashlib.sha256(sample.content).hexdigest())

        watcher_tcpdump = None
        watcher_dnsmasq = None

        for _ in range(3):
            try:
                self.log.info("Running VM {}".format(self.instance_id))
                watcher_dnsmasq = start_dnsmasq(
                    self.instance_id,
                    self.config.config['drakrun'].get('dns_server', '8.8.8.8'))

                snapshot_version = self.run_vm()
                metadata['snapshot_version'] = snapshot_version

                watcher_tcpdump = start_tcpdump_collector(
                    self.instance_id, outdir)

                self.log.info("running monitor {}".format(self.instance_id))

                hooks_list = os.path.join(ETC_DIR, "hooks.txt")
                kernel_profile = os.path.join(PROFILE_DIR, "kernel.json")
                dump_dir = os.path.join(outdir, "dumps")
                ipt_dir = os.path.join(outdir, "ipt")
                drakmon_log_fp = os.path.join(outdir, "drakmon.log")

                self.log.info("Copying sample to VM...")
                injector = Injector(self.vm_name, self.runtime_info,
                                    kernel_profile)
                result = injector.write_file(
                    os.path.join(workdir, file_name),
                    f"%USERPROFILE%\\Desktop\\{file_name}")

                injected_fn = json.loads(result.stdout)['ProcessName']
                net_enable = int(self.config.config['drakrun'].get(
                    'net_enable', '0'))

                if "%f" not in start_command:
                    self.log.warning("No file name in start command")

                cwd = subprocess.list2cmdline([ntpath.dirname(injected_fn)])
                cur_start_command = start_command.replace("%f", injected_fn)

                # don't include our internal maintanance commands
                metadata['start_command'] = cur_start_command

                if net_enable:
                    self.log.info("Setting up network...")
                    injector.create_process("cmd /C ipconfig /renew >nul",
                                            wait=True,
                                            timeout=120)

                full_cmd = cur_start_command
                self.log.info("Using command: %s", full_cmd)

                task_quality = self.current_task.headers.get("quality", "high")

                drakvuf_cmd = ["drakvuf"] + self.generate_plugin_cmdline(task_quality) + \
                              ["-o", "json"
                               "-j", "5",
                               "-t", str(timeout),
                               "-i", str(self.runtime_info.inject_pid),
                               "-k", hex(self.runtime_info.vmi_offsets.kpgd),
                               "-d", self.vm_name,
                               "--dll-hooks-list", hooks_list,
                               "--memdump-dir", dump_dir,
                               "-r", kernel_profile,
                               "-e", full_cmd,
                               "-c", cwd]

                if self.config.config['drakrun'].getboolean('enable_ipt',
                                                            fallback=False):
                    drakvuf_cmd.extend(["--ipt-dir", ipt_dir])

                drakvuf_cmd.extend(self.get_profile_list())

                syscall_filter = self.config.config['drakrun'].get(
                    'syscall_filter', None)
                if syscall_filter:
                    drakvuf_cmd.extend(["-S", syscall_filter])

                with open(drakmon_log_fp, "wb") as drakmon_log:
                    drakvuf = subprocess.Popen(drakvuf_cmd, stdout=drakmon_log)

                    try:
                        exit_code = drakvuf.wait(timeout + 60)
                    except subprocess.TimeoutExpired as e:
                        logging.error(
                            "BUG: Monitor command doesn\'t terminate automatically after timeout expires."
                        )
                        logging.error("Trying to terminate DRAKVUF...")
                        drakvuf.terminate()
                        drakvuf.wait(10)
                        logging.error(
                            "BUG: Monitor command also doesn\'t terminate after sending SIGTERM."
                        )
                        drakvuf.kill()
                        drakvuf.wait()
                        logging.error("Monitor command was forcefully killed.")
                        raise e

                    if exit_code != 0:
                        raise subprocess.CalledProcessError(
                            exit_code, drakvuf_cmd)
                break
            except subprocess.CalledProcessError:
                self.log.info("Something went wrong with the VM {}".format(
                    self.instance_id),
                              exc_info=True)
            finally:
                try:
                    subprocess.run(["xl", "destroy", self.vm_name],
                                   cwd=workdir,
                                   check=True)
                except subprocess.CalledProcessError:
                    self.log.info("Failed to destroy VM {}".format(
                        self.instance_id),
                                  exc_info=True)

                if watcher_dnsmasq:
                    watcher_dnsmasq.terminate()
        else:
            self.log.info(
                "Failed to analyze sample after 3 retries, giving up.")
            return

        self.log.info("waiting for tcpdump to exit")

        if watcher_tcpdump:
            try:
                watcher_tcpdump.wait(timeout=60)
            except subprocess.TimeoutExpired:
                self.log.exception("tcpdump doesn't exit cleanly after 60s")

        self.crop_dumps(os.path.join(outdir, 'dumps'),
                        os.path.join(outdir, 'dumps.zip'))

        if self.config.config['drakrun'].getboolean('enable_ipt',
                                                    fallback=False):
            self.compress_ipt(os.path.join(outdir, 'ipt'),
                              os.path.join(outdir, 'ipt.zip'))

        self.log.info("uploading artifacts")

        metadata['time_finished'] = int(time.time())

        with open(os.path.join(outdir, 'metadata.json'), 'w') as f:
            f.write(json.dumps(metadata))

        payload = {"analysis_uid": self.analysis_uid}
        payload.update(metadata)

        headers = dict(self.headers)
        headers["quality"] = self.current_task.headers.get("quality", "high")

        t = Task(headers, payload=payload)

        for resource in self.upload_artifacts(self.analysis_uid, workdir):
            t.add_payload(resource.name, resource)

        t.add_payload('sample', sample)
        self.send_task(t)
Пример #9
0
    def analyze_sample(self, sample_path, workdir, outdir, start_command,
                       timeout):
        analysis_info = dict()

        dns_server = self.config.config["drakrun"].get("dns_server", "8.8.8.8")
        drakmon_log_fp = os.path.join(outdir, "drakmon.log")

        with self.run_vm() as vm, graceful_exit(
                start_dnsmasq(self.instance_id, dns_server)), graceful_exit(
                    start_tcpdump_collector(self.instance_id, outdir)), open(
                        drakmon_log_fp, "wb") as drakmon_log:

            analysis_info[
                "snapshot_version"] = vm.backend.get_vm0_snapshot_time()

            kernel_profile = os.path.join(PROFILE_DIR, "kernel.json")

            self.log.info("Copying sample to VM...")
            injector = Injector(self.vm_name, self.runtime_info,
                                kernel_profile)
            result = injector.write_file(
                sample_path,
                f"%USERPROFILE%\\Desktop\\{os.path.basename(sample_path)}")

            try:
                injected_fn = json.loads(result.stdout)["ProcessName"]
            except ValueError as e:
                self.log.error(
                    "JSON decode error occurred when tried to parse injector's logs."
                )
                self.log.error(f"Raw log line: {result.stdout}")
                raise e

            # don't include our internal maintanance commands
            start_command = start_command.replace("%f", injected_fn)
            analysis_info["start_command"] = start_command
            self.log.info("Using command: %s", start_command)

            if self.net_enable:
                self.log.info("Setting up network...")
                injector.create_process("cmd /C ipconfig /renew >nul",
                                        wait=True,
                                        timeout=120)

            task_quality = self.current_task.headers.get("quality", "high")
            requested_plugins = self.current_task.payload.get(
                "plugins", self.active_plugins["_all_"])
            analysis_info["plugins"] = self.get_plugin_list(
                task_quality, requested_plugins)

            drakvuf_cmd = self.build_drakvuf_cmdline(
                timeout=timeout,
                cwd=subprocess.list2cmdline([ntpath.dirname(injected_fn)]),
                full_cmd=start_command,
                dump_dir=os.path.join(outdir, "dumps"),
                ipt_dir=os.path.join(outdir, "ipt"),
                workdir=workdir,
                enabled_plugins=analysis_info["plugins"],
            )

            try:
                subprocess.run(drakvuf_cmd,
                               stdout=drakmon_log,
                               check=True,
                               timeout=timeout + 60)
            except subprocess.CalledProcessError as e:
                # see DRAKVUF src/exitcodes.h for more details
                INJECTION_UNSUCCESSFUL = 4

                if e.returncode == INJECTION_UNSUCCESSFUL:
                    self.log_startup_failure(drakmon_log_fp)
                raise e
            except subprocess.TimeoutExpired as e:
                self.log.exception("DRAKVUF timeout expired")
                raise e

        return analysis_info
Пример #10
0
class DrakmonShell:
    def __init__(self, vm_id: int, dns: str):

        self.cleanup(vm_id)

        install_info = InstallInfo.load()
        backend = get_storage_backend(install_info)

        generate_vm_conf(install_info, vm_id)
        self.vm = VirtualMachine(backend, vm_id)
        self._dns = dns

        with open(Path(PROFILE_DIR) / "runtime.json", 'r') as f:
            self.runtime_info = RuntimeInfo.load(f)
        self.desktop = WinPath(r"%USERPROFILE%") / "Desktop"

        self.kernel_profile = Path(PROFILE_DIR) / "kernel.json"
        self.injector = Injector(
            self.vm.vm_name,
            self.runtime_info,
            self.kernel_profile,
        )
        setup_vm_network(vm_id, True, find_default_interface(), dns)

    def cleanup(self, vm_id: int):

        logging.info(f"Ensuring that drakrun@{vm_id} service is stopped...")
        try:
            subprocess.run(['systemctl', 'stop', f'drakrun@{vm_id}'],
                           stderr=subprocess.STDOUT,
                           check=True)
        except subprocess.CalledProcessError:
            raise Exception(f"drakrun@{vm_id} not stopped")

    def drakvuf(self, plugins, timeout=60):
        d = tempfile.TemporaryDirectory(prefix="drakvuf_")
        workdir = Path(d.name)

        log = open(workdir / "drakmon.log", "wb")

        cmd = ["drakvuf"]
        cmd.extend([
            "-o",
            "json",
            "F",
            "-j",
            "5",
            "-t",
            str(timeout),
            "-i",
            str(self.runtime_info.inject_pid),
            "-k",
            str(self.runtime_info.vmi_offsets.kpgd),
            "-r",
            self.kernel_profile,
            "-d",
            self.vm.vm_name,
            "--dll-hooks-list",
            Path(ETC_DIR) / "hooks.txt",
        ])

        if "memdump" in plugins:
            dumps = workdir / "dumps"
            dumps.mkdir()
            cmd.extend(["--memdump-dir", dumps])

        if "ipt" in plugins:
            ipt = workdir / "ipt"
            ipt.mkdir()
            cmd.extend(["--ipt-dir", ipt])

        for chk in (["-a", plugin] for plugin in plugins):
            cmd.extend(chk)

        subprocess.run(cmd, stdout=log)

        return d

    def copy(self, local):
        local = Path(local)
        self.injector.write_file(local, self.desktop / local.name)

    def run(self, cmd):
        self.injector.create_process(cmd)

    def __enter__(self):
        self.vm.restore()
        return self

    def __exit__(self, exc_type, exc_value, exc_traceback):
        self.vm.destroy()
        delete_vm_network(self.vm.vm_id, True, find_default_interface(),
                          self._dns)
Пример #11
0
def postinstall(report, generate_usermode):
    if not check_root():
        return

    if os.path.exists(os.path.join(ETC_DIR, "no_usage_reports")):
        report = False

    install_info = InstallInfo.load()
    storage_backend = get_storage_backend(install_info)

    vm = VirtualMachine(storage_backend, 0)

    if vm.is_running is False:
        logging.exception("vm-0 is not running")
        return

    logging.info("Cleaning up leftovers(if any)")
    cleanup_postinstall_files()

    logging.info("Ejecting installation CDs")
    eject_cd("vm-0", FIRST_CDROM_DRIVE)
    if install_info.enable_unattended:
        # If unattended install is enabled, we have an additional CD-ROM drive
        eject_cd("vm-0", SECOND_CDROM_DRIVE)

    output = subprocess.check_output(['vmi-win-guid', 'name', 'vm-0'],
                                     timeout=30).decode('utf-8')

    try:
        version = re.search(r'Version: (.*)', output).group(1)
        pdb = re.search(r'PDB GUID: ([0-9a-f]+)', output).group(1)
        fn = re.search(r'Kernel filename: ([a-z]+\.[a-z]+)', output).group(1)
    except AttributeError:
        logging.error("Failed to obtain kernel PDB GUID/Kernel filename.")
        return

    logging.info("Determined PDB GUID: {}".format(pdb))
    logging.info("Determined kernel filename: {}".format(fn))

    logging.info("Fetching PDB file...")
    dest = fetch_pdb(fn, pdb, destdir=PROFILE_DIR)

    logging.info("Generating profile out of PDB file...")
    profile = make_pdb_profile(dest)

    logging.info("Saving profile...")
    kernel_profile = os.path.join(PROFILE_DIR, 'kernel.json')
    with open(kernel_profile, 'w') as f:
        f.write(profile)

    safe_delete(dest)

    vmi_offsets = extract_vmi_offsets('vm-0', kernel_profile)
    explorer_pid = extract_explorer_pid('vm-0', kernel_profile, vmi_offsets)
    runtime_info = RuntimeInfo(vmi_offsets=vmi_offsets,
                               inject_pid=explorer_pid)

    logging.info("Saving runtime profile...")
    with open(os.path.join(PROFILE_DIR, 'runtime.json'), 'w') as f:
        f.write(runtime_info.to_json(indent=4))

    logging.info("Saving VM snapshot...")

    # snapshot domain but don't destroy it, leave it in paused state
    subprocess.check_output('xl save -p vm-0 ' +
                            os.path.join(VOLUME_DIR, "snapshot.sav"),
                            shell=True)
    logging.info("Snapshot was saved succesfully.")

    logging.info("Snapshotting persistent memory...")
    storage_backend.snapshot_vm0_volume()

    logging.info("Unpausing VM")
    subprocess.check_output('xl unpause vm-0', shell=True)

    injector = Injector('vm-0', runtime_info, kernel_profile)
    if generate_usermode:
        try:
            for file in dll_file_list:
                create_rekall_profile(injector, file)
        except RuntimeError as e:
            logging.warning("Generating usermode profiles failed")
            logging.exception(e)

    subprocess.check_output('xl destroy vm-0', shell=True)

    if report:
        send_usage_report({
            "kernel": {
                "guid": pdb,
                "filename": fn,
                "version": version
            },
            "install_iso": {
                "sha256": install_info.iso_sha256
            }
        })

    logging.info("All right, drakrun setup is done.")
    logging.info("First instance of drakrun will be enabled automatically...")
    subprocess.check_output('systemctl enable drakrun@1', shell=True)
    subprocess.check_output('systemctl start drakrun@1', shell=True)

    logging.info("If you want to have more parallel instances, execute:")
    logging.info("  # draksetup scale <number of instances>")
Пример #12
0
def create_rekall_profile(injector: Injector, file: DLL, raise_on_error=False):
    pdb_tmp_filepath = None
    cmd = None
    out = None
    try:
        logging.info(f"Fetching rekall profile for {file.path}")

        local_dll_path = os.path.join(PROFILE_DIR, file.dest)
        guest_dll_path = str(PureWindowsPath("C:/", file.path))

        cmd = injector.read_file(guest_dll_path, local_dll_path)
        out = json.loads(cmd.stdout.decode())
        if out["Status"] == "Error" and out["Error"] in [
                "ERROR_FILE_NOT_FOUND",
                "ERROR_PATH_NOT_FOUND",
        ]:
            raise FileNotFoundError
        if out["Status"] != "Success":
            logging.debug("stderr: " + cmd.stderr.decode())
            logging.debug(out)
            # Take care if the error message is changed
            raise Exception("Some error occurred in injector")

        static_apiscout_dll_profile = make_static_apiscout_profile_for_dll(
            local_dll_path)
        with open(os.path.join(APISCOUT_PROFILE_DIR, f"{file.dest}.json"),
                  "w") as f:
            f.write(
                json.dumps(static_apiscout_dll_profile,
                           indent=4,
                           sort_keys=True))

        codeview_data = pe_codeview_data(local_dll_path)
        pdb_tmp_filepath = fetch_pdb(codeview_data["filename"],
                                     codeview_data["symstore_hash"],
                                     PROFILE_DIR)

        logging.debug("Parsing PDB into JSON profile...")
        profile = make_pdb_profile(
            pdb_tmp_filepath,
            dll_origin_path=guest_dll_path,
            dll_path=local_dll_path,
            dll_symstore_hash=codeview_data["symstore_hash"],
        )
        with open(os.path.join(PROFILE_DIR, f"{file.dest}.json"), "w") as f:
            f.write(profile)
    except json.JSONDecodeError:
        logging.debug(f"stdout: {cmd.stdout}")
        logging.debug(f"stderr: {cmd.stderr}")
        logging.debug(traceback.format_exc())
        raise Exception(f"Failed to parse json response on {file.path}")
    except FileNotFoundError as e:
        on_create_rekall_profile_failure(f"Failed to copy file {file.path}",
                                         raise_on_error, e)
    except RuntimeError as e:
        on_create_rekall_profile_failure(
            f"Failed to fetch profile for {file.path}", raise_on_error, e)
    except subprocess.TimeoutExpired as e:
        on_create_rekall_profile_failure(f"Injector timed out for {file.path}",
                                         raise_on_error, e)
    except Exception as e:
        # Take care if the error message is changed
        if str(e) == "Some error occurred in injector":
            raise
        else:
            # Can help in debugging
            if cmd:
                logging.debug("stdout: " + cmd.stdout.decode())
                logging.debug("stderr: " + cmd.stderr.decode())
                logging.debug("rc: " + str(cmd.returncode))
            logging.debug(traceback.format_exc())
            on_create_rekall_profile_failure(
                f"Unexpected exception while creating rekall profile for {file.path}",
                raise_on_error,
                e,
            )
    finally:
        safe_delete(local_dll_path)
        # was crashing here if the first file reached some exception
        if pdb_tmp_filepath is not None:
            safe_delete(os.path.join(PROFILE_DIR, pdb_tmp_filepath))