Exemplo n.º 1
0
def main():
    parser = argparse.ArgumentParser(description='Push sample to the karton')
    parser.add_argument('sample', help='Path to the sample')
    parser.add_argument('--start_command',
                        help='e.g. start %f, %f will be replaced by file name',
                        required=False)
    args = parser.parse_args()

    conf = Config(os.path.join(ETC_DIR, 'config.ini'))
    producer = Producer(conf)

    task = Task({"type": "sample", "stage": "recognized", "platform": "win32"})

    with open(args.sample, "rb") as f:
        sample = Resource("sample", f.read())
    task.add_resource("sample", sample)

    # Add filename
    filename = os.path.basename(args.sample)
    task.add_payload("file_name", os.path.splitext(filename)[0])

    # Extract and add extension
    extension = os.path.splitext(filename)[1][1:]
    if extension:
        task.headers['extension'] = extension

    if args.start_command is not None:
        task.add_payload("start_command", args.start_command)

    producer.send_task(task)
Exemplo n.º 2
0
    def process(self):
        # downloaded resource cache
        task_resources = dict(self.current_task.iterate_resources())
        for plugin in self.plugins:
            name = plugin.handler.__name__
            if any(
                    map(lambda r: r not in task_resources.keys(),
                        plugin.required)):
                self.log.info("Skipping %s, missing resources", name)
                continue

            try:
                self.log.debug("Running postprocess - %s",
                               plugin.handler.__name__)
                outputs = plugin.handler(self.current_task, task_resources,
                                         self.minio)

                if outputs:
                    for out in outputs:
                        self.log.debug(
                            f"Step {plugin.handler.__name__} outputted new resource: {out}"
                        )
                        res_name = os.path.join(
                            self.current_task.payload["analysis_uid"], out)
                        task_resources[out] = RemoteResource(res_name,
                                                             uid=res_name,
                                                             bucket='drakrun',
                                                             minio=self.minio)
            except Exception:
                self.log.error("Postprocess failed", exc_info=True)

        task = Task({
            "type": "analysis",
            "kind": "drakrun-processed",
        })

        for (name, resource) in task_resources.items():
            task.add_payload(name, resource)
        self.send_task(task)
Exemplo n.º 3
0
def upload():
    producer = Producer(conf)

    with NamedTemporaryFile() as f:
        request.files['file'].save(f.name)

        with open(f.name, "rb") as fr:
            sample = Resource("sample", fr.read())

    task = Task({"type": "sample", "stage": "recognized", "platform": "win32"})
    task.add_payload("override_uid", task.uid)

    # Add analysis timeout to task
    timeout = request.form.get("timeout")
    if timeout:
        task.add_payload("timeout", int(timeout))

    # Add filename override to task
    if request.form.get("file_name"):
        filename = request.form.get("file_name")
    else:
        filename = request.files['file'].filename
    if not re.fullmatch(
            r'^((?![\\/><|:&])[\x20-\xfe])+\.(?:dll|exe|doc|docm|docx|dotm|xls|xlsx|xlsm|xltx|xltm)$',
            filename,
            flags=re.IGNORECASE):
        return jsonify({"error": "invalid file_name"}), 400
    task.add_payload("file_name", os.path.splitext(filename)[0])

    # Extract and add extension
    extension = os.path.splitext(filename)[1][1:]
    if extension:
        task.headers['extension'] = extension

    # Add startup command to task
    start_command = request.form.get("start_command")
    if start_command:
        task.add_payload("start_command", start_command)

    task.add_resource("sample", sample)

    producer.send_task(task)

    return jsonify({"task_uid": task.uid})
Exemplo n.º 4
0
def upload():
    producer = Producer(conf)

    with NamedTemporaryFile() as f:
        request.files['file'].save(f.name)

        with open(f.name, "rb") as fr:
            sample = Resource("sample", fr.read())

    task = Task({"type": "sample", "stage": "recognized", "platform": "win32"})
    task.add_payload("override_uid", task.uid)

    # Add analysis timeout to task
    timeout = request.form.get("timeout")
    if timeout:
        task.add_payload("timeout", int(timeout))

    # Add filename override to task
    if request.form.get("file_name"):
        filename = request.form.get("file_name")
    else:
        filename = request.files['file'].filename
    task.add_payload("file_name", os.path.splitext(filename)[0])

    # Extract and add extension
    extension = os.path.splitext(filename)[1][1:]
    if extension:
        task.headers['extension'] = extension

    # Add startup command to task
    start_command = request.form.get("start_command")
    if start_command:
        task.add_payload("start_command", start_command)

    task.add_resource("sample", sample)

    producer.send_task(task)

    return jsonify({"task_uid": task.uid})
Exemplo n.º 5
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 60 * 10
        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

        analysis_uid = self.current_task.uid
        override_uid = self.current_task.payload.get('override_uid')

        self.log.info(f"analysis UID: {analysis_uid}")

        if override_uid:
            analysis_uid = override_uid
            self.log.info(f"override UID: {override_uid}")
            self.log.info(
                "note that artifacts will be stored under this overriden identifier"
            )

        self.rs.set(f"drakvnc:{analysis_uid}", INSTANCE_ID, ex=3600)  # 1h

        workdir = '/tmp/drakrun/vm-{}'.format(int(INSTANCE_ID))

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

        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(INSTANCE_ID))
                watcher_dnsmasq = start_dnsmasq(
                    INSTANCE_ID,
                    self.config.config['drakrun'].get('dns_server', '8.8.8.8'))

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

                watcher_tcpdump = start_tcpdump_collector(INSTANCE_ID, outdir)

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

                kernel_profile = os.path.join(PROFILE_DIR, "kernel.json")
                runtime_profile = os.path.join(PROFILE_DIR, "runtime.json")
                with open(runtime_profile, 'r') as runtime_f:
                    rp = json.loads(runtime_f.read())
                    inject_pid = rp['inject_pid']
                    kpgd = rp['vmi_offsets']['kpgd']

                hooks_list = os.path.join(ETC_DIR, "hooks.txt")
                dump_dir = os.path.join(outdir, "dumps")
                drakmon_log_fp = os.path.join(outdir, "drakmon.log")

                injector_cmd = [
                    "injector", "-o", "json", "-d",
                    "vm-{vm_id}".format(vm_id=INSTANCE_ID), "-r",
                    kernel_profile, "-i", inject_pid, "-k", kpgd, "-m",
                    "writefile", "-e", f"%USERPROFILE%\\Desktop\\{file_name}",
                    "-B",
                    os.path.join(workdir, file_name)
                ]

                self.log.info("Running injector...")
                injector = subprocess.Popen(injector_cmd,
                                            stdout=subprocess.PIPE)
                outs, errs = injector.communicate(b"", 20)

                if injector.returncode != 0:
                    raise subprocess.CalledProcessError(
                        injector.returncode, injector_cmd)

                injected_fn = json.loads(outs)['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
                cur_start_command = f"cd {cwd} & " + cur_start_command

                if net_enable:
                    cur_start_command = "ipconfig /renew & " + cur_start_command

                full_cmd = subprocess.list2cmdline(
                    ["cmd.exe", "/C", cur_start_command])
                self.log.info("Using command: %s", full_cmd)

                drakvuf_cmd = [
                    "drakvuf", "-o", "json", "-x", "poolmon", "-x", "objmon",
                    "-x", "socketmon", "-j", "5", "-t",
                    str(timeout), "-i", inject_pid, "-k", kpgd, "-d",
                    "vm-{vm_id}".format(vm_id=INSTANCE_ID), "--dll-hooks-list",
                    hooks_list, "--memdump-dir", dump_dir, "-r",
                    kernel_profile, "-e", full_cmd
                ]

                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(INSTANCE_ID),
                    exc_info=True)
            finally:
                try:
                    subprocess.run(
                        ["xl", "destroy", "vm-{}".format(INSTANCE_ID)],
                        cwd=workdir,
                        check=True)
                except subprocess.CalledProcessError:
                    self.log.info(
                        "Failed to destroy VM {}".format(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 os.path.exists("/opt/procdot/procmon2dot"):
            self.generate_graphs(outdir)
        self.slice_logs(outdir)
        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": analysis_uid}
        payload.update(metadata)

        t = Task(
            {
                "type": "analysis",
                "kind": "drakrun",
                "quality": self.current_task.headers.get("quality", "high")
            },
            payload=payload)

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

        t.add_payload('sample', sample)
        self.send_task(t)
Exemplo n.º 6
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))

        analysis_uid = self.current_task.uid
        override_uid = self.current_task.payload.get('override_uid')

        self.log.info(f"analysis UID: {analysis_uid}")

        if override_uid:
            analysis_uid = override_uid
            self.log.info(f"override UID: {override_uid}")
            self.log.info(
                "note that artifacts will be stored under this overriden identifier"
            )

        workdir = '/tmp/drakrun/vm-{}'.format(int(INSTANCE_ID))
        extension = self.current_task.headers.get("extension", "exe").lower()
        start_command = 'start malwar.{}'.format(extension)

        if extension == 'dll':
            run_command = self._get_dll_run_command(sample.content)

            if not run_command:
                self.log.info(
                    'Unable to run malware sample, could not generate any suitable command to run it.'
                )
                return

            self.log.info('Using command: {}'.format(run_command))
            start_command = 'start {}'.format(run_command)

        try:
            shutil.rmtree(workdir)
        except Exception as e:
            print(e)

        outdir = os.path.join(workdir, 'output')
        os.makedirs(workdir, exist_ok=True)
        os.mkdir(outdir)
        os.mkdir(os.path.join(outdir, 'dumps'))

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

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

        with open(os.path.join(workdir, 'run.bat'), 'wb') as f:
            f.write(
                b'ipconfig /renew\r\nxcopy D:\\malwar.' +
                extension.encode('ascii') +
                b' %USERPROFILE%\\Desktop\\\r\nC:\r\ncd %USERPROFILE%\\Desktop\r\n'
                + start_command.encode('ascii'))

        with open(os.path.join(workdir, 'malwar.{}'.format(extension)),
                  'wb') as f:
            f.write(sample.content)

        try:
            subprocess.run([
                "genisoimage", "-o",
                os.path.join(workdir, 'malwar.iso'),
                os.path.join(workdir, 'malwar.{}'.format(extension)),
                os.path.join(workdir, 'run.bat')
            ],
                           cwd=workdir,
                           check=True)
        except subprocess.CalledProcessError as e:
            logging.exception(
                "Failed to generate CD ISO image. Please install genisoimage")
            raise e

        watcher_tcpdump = None
        watcher_dnsmasq = None

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

                d_run.ETC_DIR = ETC_DIR
                d_run.LIB_DIR = LIB_DIR
                d_run.logging = self.log
                d_run.run_vm(INSTANCE_ID)

                watcher_tcpdump = start_tcpdump_collector(INSTANCE_ID, outdir)

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

                kernel_profile = os.path.join(LIB_DIR, "profiles/kernel.json")
                runtime_profile = os.path.join(LIB_DIR,
                                               "profiles/runtime.json")
                with open(runtime_profile, 'r') as runtime_f:
                    rp = json.loads(runtime_f.read())
                    inject_pid = rp['inject_pid']
                    kpgd = rp['vmi_offsets']['kpgd']

                hooks_list = os.path.join(ETC_DIR, "hooks.txt")
                dump_dir = os.path.join(outdir, "dumps")
                drakmon_log_fp = os.path.join(outdir, "drakmon.log")

                drakvuf_cmd = [
                    "drakvuf", "-o", "json", "-x", "poolmon", "-x", "objmon",
                    "-j", "5", "-t", "600", "-i", inject_pid, "-k", kpgd, "-d",
                    "vm-{vm_id}".format(vm_id=INSTANCE_ID), "--dll-hooks-list",
                    hooks_list, "--memdump-dir", dump_dir, "-r",
                    kernel_profile, "-e", "D:\\run.bat"
                ]

                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(660)
                    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(INSTANCE_ID),
                    exc_info=True)
            finally:
                try:
                    subprocess.run(
                        ["xl", "destroy", "vm-{}".format(INSTANCE_ID)],
                        cwd=workdir,
                        check=True)
                except subprocess.CalledProcessError:
                    self.log.info(
                        "Failed to destroy VM {}".format(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 os.path.exists("/opt/procdot/procmon2dot"):
            self.generate_graphs(outdir)
        self.slice_logs(outdir)
        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": analysis_uid}
        payload.update(metadata)

        t = Task(
            {
                "type": "analysis",
                "kind": "drakrun",
                "quality": self.current_task.headers.get("quality", "high")
            },
            payload=payload)

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

        t.add_payload('sample', sample)
        self.send_task(t)
Exemplo n.º 7
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)