def test_options(self): assert parse_options("a=b") == {"a": "b"} assert parse_options("a=b,b=c") == {"a": "b", "b": "c"} assert parse_options("a,b") == {} assert emit_options({"a": "b"}) == "a=b" assert emit_options({"a": "b", "b": "c"}) == "a=b,b=c" assert parse_options(emit_options({"x": "y"})) == {"x": "y"}
def test_options(self): assert parse_options("a=b") == {"a": "b"} assert parse_options("a=b,b=c") == {"a": "b", "b": "c"} assert parse_options("a,b") == {} assert emit_options({"a": "b"}) == "a=b" assert emit_options({"a": "b", "b": "c"}) == "a=b,b=c" assert parse_options(emit_options({"x": "y"})) == {"x": "y"}
def process_task(task): db = Database() try: task_log_start(task["id"]) logger( "Starting task reporting", action="task.report", status="pending", target=task["target"], category=task["category"], package=task["package"], options=emit_options(task["options"]), custom=task["custom"] ) if task["category"] == "file" and task.get("sample_id"): sample = db.view_sample(task["sample_id"]) copy_path = cwd("storage", "binaries", sample.sha256) else: copy_path = None try: process(task["target"], copy_path, task) db.set_status(task["id"], TASK_REPORTED) except Exception as e: log.exception("Task #%d: error reporting: %s", task["id"], e) db.set_status(task["id"], TASK_FAILED_PROCESSING) log.info("Task #%d: reports generation completed", task["id"], extra={ "action": "task.report", "status": "success", }) except Exception as e: log.exception("Caught unknown exception: %s", e) finally: task_log_stop(task["id"])
def process_task(task): db = Database() if not task.dir_exists(): log.error("Task #%s directory %s does not exist, cannot process it", task.id, task.path) db.set_status(task.id, TASK_FAILED_PROCESSING) return task_log_start(task.id) if task.targets: target = task.targets[0] else: target = Target() logger("Starting task reporting", action="task.report", status="pending", target=target.target, category=target.category, package=task["package"], options=emit_options(task["options"]), custom=task["custom"]) success = False try: success = task.process() except Exception as e: log.error("Failed to process task #%s. Error: %s", task.id, e) finally: if success: log.info("Task #%d: reports generation completed", task.id, extra={ "action": "task.report", "status": "success", }) db.set_status(task.id, TASK_REPORTED) else: log.error("Failed to process task #%s", task.id, extra={ "action": "task.report", "status": "failed", }) db.set_status(task.id, TASK_FAILED_PROCESSING) task_log_stop(task.id)
def run(self): """Run information gathering. @return: information dict. """ self.key = "info" # Get git head. if os.path.exists(cwd(".cwd")): git_head = git_fetch_head = open(cwd(".cwd"), "rb").read() else: log.warning( "No .cwd file was found in the Cuckoo Working Directory. Did " "you correctly setup the CWD?") git_head = git_fetch_head = None # Monitor. monitor = cwd("monitor", self.task["options"].get("monitor", "latest")) if os.path.islink(monitor): monitor = os.readlink(monitor) elif os.path.isfile(monitor): monitor = open(monitor, "rb").read().strip() elif os.path.isdir(monitor): monitor = os.path.basename(monitor) else: monitor = None return dict(version=version, git={ "head": git_head, "fetch_head": git_fetch_head, }, monitor=monitor, added=self.task.get("added_on"), started=self.task["started_on"], ended=self.task.get("completed_on", "none"), duration=self.task.get("duration", -1), id=int(self.task["id"]), category=self.task["category"], custom=self.task["custom"], owner=self.task["owner"], package=self.task["package"], platform=self.task["platform"], machine=self.machine.get("name"), options=emit_options(self.task["options"]), route=self.task["route"], tasktype=self.task["type"])
def build_options(self): """Generate analysis options. @return: options dict. """ options = {} if self.task.category == "file": options["file_name"] = File(self.task.target).get_name() options["file_type"] = File(self.task.target).get_type() options["pe_exports"] = \ ",".join(File(self.task.target).get_exported_functions()) package, activity = File(self.task.target).get_apk_entry() self.task.options["apk_entry"] = "%s:%s" % (package, activity) elif self.task.category == "archive": options["file_name"] = File(self.task.target).get_name() options["id"] = self.task.id options["ip"] = self.machine.resultserver_ip options["port"] = self.machine.resultserver_port options["category"] = self.task.category options["target"] = self.task.target options["package"] = self.task.package options["options"] = emit_options(self.task.options) options["enforce_timeout"] = self.task.enforce_timeout options["clock"] = self.task.clock options["vnc"] = self.task.vnc options["terminate_processes"] = self.cfg.cuckoo.terminate_processes if not self.task.timeout: options["timeout"] = self.cfg.timeouts.default else: options["timeout"] = self.task.timeout # copy in other analyzer specific options, TEMPORARY (most likely) vm_options = getattr(machinery.options, self.machine.name) for k in vm_options: if k.startswith("analyzer_"): options[k] = vm_options[k] log.info(" [*] build_options() - options built:\n %s", str(options)) return options
def build_options(self): """Generate analysis options. @return: options dict. """ options = {} if self.task.category == "file": options["file_name"] = File(self.task.target).get_name() options["file_type"] = File(self.task.target).get_type() options["pe_exports"] = \ ",".join(File(self.task.target).get_exported_functions()) package, activity = File(self.task.target).get_apk_entry() self.task.options["apk_entry"] = "%s:%s" % (package, activity) elif self.task.category == "archive": options["file_name"] = File(self.task.target).get_name() options["id"] = self.task.id options["ip"] = self.machine.resultserver_ip options["port"] = self.machine.resultserver_port options["category"] = self.task.category options["target"] = self.task.target options["package"] = self.task.package options["options"] = emit_options(self.task.options) options["enforce_timeout"] = self.task.enforce_timeout options["clock"] = self.task.clock options["terminate_processes"] = self.cfg.cuckoo.terminate_processes if not self.task.timeout: options["timeout"] = self.cfg.timeouts.default else: options["timeout"] = self.task.timeout # copy in other analyzer specific options, TEMPORARY (most likely) vm_options = getattr(machinery.options, self.machine.name) for k in vm_options: if k.startswith("analyzer_"): options[k] = vm_options[k] return options
def launch_analysis(self): """Start analysis.""" succeeded = False if self.task.category == "file" or self.task.category == "archive": target = os.path.basename(self.task.target) else: target = self.task.target log.info("Starting analysis of %s \"%s\" (task #%d, options \"%s\")", self.task.category.upper(), target, self.task.id, emit_options(self.task.options), extra={ "action": "task.init", "status": "starting", "task_id": self.task.id, "target": target, "category": self.task.category, "package": self.task.package, "options": emit_options(self.task.options), "custom": self.task.custom, }) # Initialize the analysis. if not self.init(): logger("Failed to initialize", action="task.init", status="error") return False # Acquire analysis machine. try: self.acquire_machine() except CuckooOperationalError as e: machine_lock.release() log.error("Cannot acquire machine: %s", e, extra={ "action": "vm.acquire", "status": "error", }) return False self.rs_port = self.machine.resultserver_port or ResultServer().port # At this point we can tell the ResultServer about it. try: ResultServer().add_task(self.task, self.machine) except Exception as e: machinery.release(self.machine.label) self.errors.put(e) # Initialize the guest manager. self.guest_manager = GuestManager(self.machine.name, self.machine.ip, self.machine.platform, self.task.id, self) self.aux = RunAuxiliary(self.task, self.machine, self.guest_manager) self.aux.start() # Generate the analysis configuration file. options = self.build_options() # Check if the current task has remotecontrol # enabled before starting the machine. control_enabled = (config("cuckoo:remotecontrol:enabled") and "remotecontrol" in self.task.options) if control_enabled: try: machinery.enable_remote_control(self.machine.label) except NotImplementedError: log.error( "Remote control support has not been implemented for the " "configured machinery module: %s", config("cuckoo:cuckoo:machinery")) try: unlocked = False self.interface = None # Mark the selected analysis machine in the database as started. guest_log = self.db.guest_start(self.task.id, self.machine.name, self.machine.label, machinery.__class__.__name__) logger("Starting VM", action="vm.start", status="pending", vmname=self.machine.name) # Start the machine. machinery.start(self.machine.label, self.task) logger("Started VM", action="vm.start", status="success", vmname=self.machine.name) # retrieve the port used for remote control if control_enabled: try: params = machinery.get_remote_control_params( self.machine.label) self.db.set_machine_rcparams(self.machine.label, params) except NotImplementedError: log.error( "Remote control support has not been implemented for the " "configured machinery module: %s", config("cuckoo:cuckoo:machinery")) # Enable network routing. self.route_network() # By the time start returns it will have fully started the Virtual # Machine. We can now safely release the machine lock. machine_lock.release() unlocked = True # Run and manage the components inside the guest unless this # machine has the "noagent" option specified (please refer to the # wait_finish() function for more details on this function). if "noagent" not in self.machine.options: self.guest_manage(options) else: self.wait_finish() succeeded = True except CuckooMachineSnapshotError as e: log.error( "Unable to restore to the snapshot for this Virtual Machine! " "Does your VM have a proper Snapshot and can you revert to it " "manually? VM: %s, error: %s", self.machine.name, e, extra={ "action": "vm.resume", "status": "error", "vmname": self.machine.name, }) except CuckooMachineError as e: if not unlocked: machine_lock.release() log.error("Error starting Virtual Machine! VM: %s, error: %s", self.machine.name, e, extra={ "action": "vm.start", "status": "error", "vmname": self.machine.name, }) except CuckooGuestCriticalTimeout as e: if not unlocked: machine_lock.release() log.error( "Error from machine '%s': it appears that this Virtual " "Machine hasn't been configured properly as the Cuckoo Host " "wasn't able to connect to the Guest. There could be a few " "reasons for this, please refer to our documentation on the " "matter: %s", self.machine.name, faq("troubleshooting-vm-network-configuration"), extra={ "error_action": "vmrouting", "action": "guest.handle", "status": "error", "task_id": self.task.id, }) except CuckooGuestError as e: if not unlocked: machine_lock.release() log.error("Error from the Cuckoo Guest: %s", e, extra={ "action": "guest.handle", "status": "error", "task_id": self.task.id, }) finally: # Stop Auxiliary modules. if not self.stopped_aux: self.stopped_aux = True self.aux.stop() # Take a memory dump of the machine before shutting it off. if self.cfg.cuckoo.memory_dump or self.task.memory: logger("Taking full memory dump", action="vm.memdump", status="pending", vmname=self.machine.name) try: dump_path = os.path.join(self.storage, "memory.dmp") machinery.dump_memory(self.machine.label, dump_path) logger("Taken full memory dump", action="vm.memdump", status="success", vmname=self.machine.name) except NotImplementedError: log.error( "The memory dump functionality is not available for " "the current machine manager.", extra={ "action": "vm.memdump", "status": "error", "vmname": self.machine.name, }) except CuckooMachineError as e: log.error("Machinery error: %s", e, extra={ "action": "vm.memdump", "status": "error", }) logger("Stopping VM", action="vm.stop", status="pending", vmname=self.machine.name) try: # Stop the analysis machine. machinery.stop(self.machine.label) except CuckooMachineError as e: log.warning("Unable to stop machine %s: %s", self.machine.label, e, extra={ "action": "vm.stop", "status": "error", "vmname": self.machine.name, }) logger("Stopped VM", action="vm.stop", status="success", vmname=self.machine.name) # Disable remote control after stopping the machine # if it was enabled for the task. if control_enabled: try: machinery.disable_remote_control(self.machine.label) except NotImplementedError: log.error( "Remote control support has not been implemented for the " "configured machinery module: %s", config("cuckoo:cuckoo:machinery")) # Mark the machine in the database as stopped. Unless this machine # has been marked as dead, we just keep it as "started" in the # database so it'll not be used later on in this session. self.db.guest_stop(guest_log) # After all this, we can make the ResultServer forget about the # internal state for this analysis task. ResultServer().del_task(self.task, self.machine) # Drop the network routing rules if any. if not self.unrouted_network: self.unroute_network() try: # Release the analysis machine. But only if the machine has # not turned dead yet. machinery.release(self.machine.label) except CuckooMachineError as e: log.error( "Unable to release machine %s, reason %s. You might need " "to restore it manually.", self.machine.label, e, extra={ "action": "vm.release", "status": "error", "vmname": self.machine.name, }) return succeeded
def run(self): """Run information gathering. @return: information dict. """ self.key = "info" db = Database() dbtask = db.view_task(self.task["id"], details=True) # Fetch the task. if dbtask: task = dbtask.to_dict() else: # Task is gone from the database. if os.path.isfile(self.taskinfo_path): # We've got task.json, so grab info from there. task = json_decode(open(self.taskinfo_path).read()) else: # We don't have any info on the task :( emptytask = Task() emptytask.id = self.task["id"] task = emptytask.to_dict() # Get git head. if os.path.exists(cwd(".cwd")): git_head = git_fetch_head = open(cwd(".cwd"), "rb").read() else: log.warning( "No .cwd file was found in the Cuckoo Working Directory. Did " "you correctly setup the CWD?") git_head = git_fetch_head = None # Monitor. monitor = cwd("monitor", task["options"].get("monitor", "latest")) if os.path.islink(monitor): monitor = os.readlink(monitor) elif os.path.isfile(monitor): monitor = open(monitor, "rb").read().strip() elif os.path.isdir(monitor): monitor = os.path.basename(monitor) else: monitor = None return dict( version=version, git={ "head": git_head, "fetch_head": git_fetch_head, }, monitor=monitor, added=task.get("added_on"), started=task["started_on"], ended=task.get("completed_on", "none"), duration=task.get("duration", -1), id=int(task["id"]), category=task["category"], custom=task["custom"], owner=task["owner"], machine=task["guest"], package=task["package"], platform=task["platform"], options=emit_options(task["options"]), route=task["route"], )
def launch_analysis(self): """Start analysis.""" succeeded = False if self.task.category == "file" or self.task.category == "archive": target = os.path.basename(self.task.target) else: target = self.task.target log.info( "Starting analysis of %s \"%s\" (task #%d, options \"%s\")", self.task.category.upper(), target, self.task.id, emit_options(self.task.options), extra={ "action": "task.init", "status": "starting", "task_id": self.task.id, "target": target, "category": self.task.category, "package": self.task.package, "options": emit_options(self.task.options), "custom": self.task.custom, } ) # Initialize the analysis. if not self.init(): logger("Failed to initialize", action="task.init", status="error") return False # Acquire analysis machine. try: self.acquire_machine() except CuckooOperationalError as e: machine_lock.release() log.error("Cannot acquire machine: %s", e, extra={ "action": "vm.acquire", "status": "error", }) return False # At this point we can tell the ResultServer about it. try: ResultServer().add_task(self.task, self.machine) except Exception as e: machinery.release(self.machine.label) self.errors.put(e) # Initialize the guest manager. self.guest_manager = GuestManager( self.machine.name, self.machine.ip, self.machine.platform, self.task.id, self ) self.aux = RunAuxiliary(self.task, self.machine, self.guest_manager) self.aux.start() # Generate the analysis configuration file. options = self.build_options() # Check if the current task has remotecontrol # enabled before starting the machine. control_enabled = ( config("cuckoo:remotecontrol:enabled") and "remotecontrol" in self.task.options ) if control_enabled: try: machinery.enable_remote_control(self.machine.label) except NotImplementedError: raise CuckooMachineError( "Remote control support has not been implemented " "for this machinery." ) try: unlocked = False self.interface = None # Mark the selected analysis machine in the database as started. guest_log = self.db.guest_start(self.task.id, self.machine.name, self.machine.label, machinery.__class__.__name__) logger( "Starting VM", action="vm.start", status="pending", vmname=self.machine.name ) # Start the machine. machinery.start(self.machine.label, self.task) logger( "Started VM", action="vm.start", status="success", vmname=self.machine.name ) # retrieve the port used for remote control if control_enabled: try: params = machinery.get_remote_control_params( self.machine.label ) self.db.set_machine_rcparams(self.machine.label, params) except NotImplementedError: raise CuckooMachineError( "Remote control support has not been implemented " "for this machinery." ) # Enable network routing. self.route_network() # By the time start returns it will have fully started the Virtual # Machine. We can now safely release the machine lock. machine_lock.release() unlocked = True # Run and manage the components inside the guest unless this # machine has the "noagent" option specified (please refer to the # wait_finish() function for more details on this function). if "noagent" not in self.machine.options: self.guest_manage(options) else: self.wait_finish() succeeded = True except CuckooMachineSnapshotError as e: log.error( "Unable to restore to the snapshot for this Virtual Machine! " "Does your VM have a proper Snapshot and can you revert to it " "manually? VM: %s, error: %s", self.machine.name, e, extra={ "action": "vm.resume", "status": "error", "vmname": self.machine.name, } ) except CuckooMachineError as e: if not unlocked: machine_lock.release() log.error( "Error starting Virtual Machine! VM: %s, error: %s", self.machine.name, e, extra={ "action": "vm.start", "status": "error", "vmname": self.machine.name, } ) except CuckooGuestCriticalTimeout as e: if not unlocked: machine_lock.release() log.error( "Error from machine '%s': it appears that this Virtual " "Machine hasn't been configured properly as the Cuckoo Host " "wasn't able to connect to the Guest. There could be a few " "reasons for this, please refer to our documentation on the " "matter: %s", self.machine.name, faq("troubleshooting-vm-network-configuration"), extra={ "error_action": "vmrouting", "action": "guest.handle", "status": "error", "task_id": self.task.id, } ) except CuckooGuestError as e: if not unlocked: machine_lock.release() log.error("Error from the Cuckoo Guest: %s", e, extra={ "action": "guest.handle", "status": "error", "task_id": self.task.id, }) finally: # Stop Auxiliary modules. self.aux.stop() # Take a memory dump of the machine before shutting it off. if self.cfg.cuckoo.memory_dump or self.task.memory: logger( "Taking full memory dump", action="vm.memdump", status="pending", vmname=self.machine.name ) try: dump_path = os.path.join(self.storage, "memory.dmp") machinery.dump_memory(self.machine.label, dump_path) logger( "Taken full memory dump", action="vm.memdump", status="success", vmname=self.machine.name ) except NotImplementedError: log.error( "The memory dump functionality is not available for " "the current machine manager.", extra={ "action": "vm.memdump", "status": "error", "vmname": self.machine.name, } ) except CuckooMachineError as e: log.error("Machinery error: %s", e, extra={ "action": "vm.memdump", "status": "error", }) logger( "Stopping VM", action="vm.stop", status="pending", vmname=self.machine.name ) try: # Stop the analysis machine. machinery.stop(self.machine.label) except CuckooMachineError as e: log.warning( "Unable to stop machine %s: %s", self.machine.label, e, extra={ "action": "vm.stop", "status": "error", "vmname": self.machine.name, } ) logger( "Stopped VM", action="vm.stop", status="success", vmname=self.machine.name ) # Disable remote control after stopping the machine # if it was enabled for the task. if control_enabled: try: machinery.disable_remote_control(self.machine.label) except NotImplementedError: raise CuckooMachineError( "Remote control support has not been implemented " "for this machinery." ) # Mark the machine in the database as stopped. Unless this machine # has been marked as dead, we just keep it as "started" in the # database so it'll not be used later on in this session. self.db.guest_stop(guest_log) # After all this, we can make the ResultServer forget about the # internal state for this analysis task. ResultServer().del_task(self.task, self.machine) # Drop the network routing rules if any. self.unroute_network() try: # Release the analysis machine. But only if the machine has # not turned dead yet. machinery.release(self.machine.label) except CuckooMachineError as e: log.error( "Unable to release machine %s, reason %s. You might need " "to restore it manually.", self.machine.label, e, extra={ "action": "vm.release", "status": "error", "vmname": self.machine.name, } ) return succeeded
def run(self): """Run information gathering. @return: information dict. """ self.key = "info" db = Database() dbtask = db.view_task(self.task["id"], details=True) # Fetch the task. if dbtask: task = dbtask.to_dict() else: # Task is gone from the database. if os.path.isfile(self.taskinfo_path): # We've got task.json, so grab info from there. task = json_decode(open(self.taskinfo_path).read()) else: # We don't have any info on the task :( emptytask = Task() emptytask.id = self.task["id"] task = emptytask.to_dict() # Get git head. if os.path.exists(cwd(".cwd")): git_head = git_fetch_head = open(cwd(".cwd"), "rb").read() else: log.warning( "No .cwd file was found in the Cuckoo Working Directory. Did " "you correctly setup the CWD?" ) git_head = git_fetch_head = None # Monitor. monitor = cwd("monitor", task["options"].get("monitor", "latest")) if os.path.islink(monitor): monitor = os.readlink(monitor) elif os.path.isfile(monitor): monitor = open(monitor, "rb").read().strip() elif os.path.isdir(monitor): monitor = os.path.basename(monitor) else: monitor = None return dict( version=version, git={ "head": git_head, "fetch_head": git_fetch_head, }, monitor=monitor, added=task.get("added_on"), started=task["started_on"], ended=task.get("completed_on", "none"), duration=task.get("duration", -1), id=int(task["id"]), category=task["category"], custom=task["custom"], owner=task["owner"], machine=task["guest"], package=task["package"], platform=task["platform"], options=emit_options(task["options"]), route=task["route"], )