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