def _status(self, label): """Gets current status of a physical machine. @param label: physical machine name. @return: status string. """ # For physical machines, the agent can either be contacted or not. # However, there is some information to be garnered from potential # exceptions. log.debug("Getting status for machine: %s.", label) machine = self._get_machine(label) # The status is only used to determine whether the Guest is running # or whether it is in a stopped status, therefore the timeout can most # likely be fairly arbitrary. TODO This is a temporary fix as it is # not compatible with the new Cuckoo Agent, but it will have to do. url = "http://{0}:{1}".format(machine.ip, CUCKOO_GUEST_PORT) server = TimeoutServer(url, allow_none=True, timeout=60) try: status = server.get_status() except xmlrpclib.Fault as e: # Contacted Agent, but it threw an error. log.debug("Agent error: %s (%s) (Error: %s).", machine.id, machine.ip, e) return self.ERROR except socket.error as e: # Could not contact agent. log.debug("Agent unresponsive: %s (%s) (Error: %s).", machine.id, machine.ip, e) return self.STOPPED except Exception as e: # TODO Handle this better. log.debug("Received unknown exception: %s.", e) return self.ERROR # If the agent responded successfully, then the physical machine # is running if status: return self.RUNNING return self.ERROR
class OldGuestManager(object): """Old and deprecated Guest Manager. This class handles the communications with the old agent running in the virtual machine. """ def __init__(self, vm_id, ip, platform, task_id): """@param ip: guest's IP address. @param platform: guest's operating system type. """ self.id = vm_id self.ip = ip self.platform = platform self.task_id = task_id self.cfg = Config() # initialized in start_analysis so we can update the critical timeout # TODO, pull options parameter into __init__ so we can do this here self.timeout = None self.server = None def wait(self, status): """Waiting for status. @param status: status. @return: always True. """ log.debug("%s: waiting for status 0x%.04x", self.id, status) end = time.time() + self.timeout self.server._set_timeout(self.timeout) while db.guest_get_status(self.task_id) == "starting": # Check if we've passed the timeout. if time.time() > end: raise CuckooGuestError("{0}: the guest initialization hit the " "critical timeout, analysis " "aborted.".format(self.id)) try: # If the server returns the given status, break the loop # and return. if self.server.get_status() == status: log.debug("%s: status ready", self.id) break except: pass log.debug("%s: not ready yet", self.id) time.sleep(1) self.server._set_timeout(None) return True def upload_analyzer(self, monitor): """Upload analyzer to guest. @return: operation status. """ zip_data = analyzer_zipfile(self.platform, monitor) log.debug( "Uploading analyzer to guest (id=%s, ip=%s, monitor=%s, size=%d)", self.id, self.ip, monitor, len(zip_data)) # Send the zip containing the analyzer to the agent running inside # the guest. try: self.server.add_analyzer(xmlrpclib.Binary(zip_data)) except socket.timeout: raise CuckooGuestError("{0}: guest communication timeout: unable " "to upload agent, check networking or try " "to increase timeout".format(self.id)) def start_analysis(self, options, monitor): """Start analysis. @param options: options. @return: operation status. """ # TODO Deal with unicode URLs, should probably try URL encoding. # Unicode files are being taken care of. self.timeout = options["timeout"] + self.cfg.timeouts.critical url = "http://{0}:{1}".format(self.ip, CUCKOO_GUEST_PORT) self.server = TimeoutServer(url, allow_none=True, timeout=self.timeout) try: # Wait for the agent to respond. This is done to check the # availability of the agent and verify that it's ready to receive # data. self.wait(CUCKOO_GUEST_INIT) # Invoke the upload of the analyzer to the guest. self.upload_analyzer(monitor) # Give the analysis options to the guest, so it can generate the # analysis.conf inside the guest. try: self.server.add_config(options) except: raise CuckooGuestError("{0}: unable to upload config to " "analysis machine".format(self.id)) # If the target of the analysis is a file, upload it to the guest. if options["category"] == "file": try: file_data = open(options["target"], "rb").read() except (IOError, OSError) as e: raise CuckooGuestError("Unable to read {0}, error: " "{1}".format(options["target"], e)) data = xmlrpclib.Binary(file_data) try: self.server.add_malware(data, options["file_name"]) except Exception as e: raise CuckooGuestError("{0}: unable to upload malware to " "analysis machine: {1}".format( self.id, e)) # Launch the analyzer. pid = self.server.execute() log.debug("%s: analyzer started with PID %d", self.id, pid) # If something goes wrong when establishing the connection, raise an # exception and abort the analysis. except (socket.timeout, socket.error): raise CuckooGuestError("{0}: guest communication timeout, check " "networking or try to increase " "timeout".format(self.id)) def wait_for_completion(self): """Wait for analysis completion. @return: operation status. """ log.debug("%s: waiting for completion", self.id) end = time.time() + self.timeout self.server._set_timeout(self.timeout) while db.guest_get_status(self.task_id) == "running": time.sleep(1) # If the analysis hits the critical timeout, just return straight # away and try to recover the analysis results from the guest. if time.time() > end: raise CuckooGuestError( "The analysis hit the critical timeout, terminating.") try: status = self.server.get_status() except Exception as e: log.debug("%s: error retrieving status: %s", self.id, e) continue # React according to the returned status. if status == CUCKOO_GUEST_COMPLETED: log.info("%s: analysis completed successfully", self.id) break elif status == CUCKOO_GUEST_FAILED: error = self.server.get_error() if not error: error = "unknown error" raise CuckooGuestError("Analysis failed: {0}".format(error)) else: log.debug("%s: analysis not completed yet (status=%s)", self.id, status) self.server._set_timeout(None)
class GuestManager: """Guest Manager. This class handles the communications with the agents running in the machines. """ def __init__(self, vm_id, ip, platform="windows"): """@param ip: guest's IP address. @param platform: guest's operating system type. """ self.id = vm_id self.ip = ip self.platform = platform self._fm = None self.cfg = Config() self.timeout = self.cfg.timeouts.critical url = "http://{0}:{1}".format(ip, CUCKOO_GUEST_PORT) self.server = TimeoutServer(url, allow_none=True, timeout=self.timeout) def wait(self, status): """Waiting for status. @param status: status. @return: always True. """ log.debug("%s: waiting for status 0x%.04x", self.id, status) end = time.time() + self.timeout self.server._set_timeout(self.timeout) while True: # Check if we've passed the timeout. if time.time() > end: raise CuckooGuestError("{0}: the guest initialization hit the " "critical timeout, analysis " "aborted.".format(self.id)) try: # If the server returns the given status, break the loop # and return. if self.server.get_status() == status: log.debug("%s: status ready", self.id) break except: if self.id == "aosx" and self._fm and check_ping(self.ip): if not self._fm.process_on: run_cmd_with_timeout("killall adb", 4) run_cmd_with_timeout("adb root", 4) run_cmd_with_timeout("adb connect {}".format(self.ip), 4) run_cmd_with_timeout( "adb shell am start -n com.cuckoo.agent/com.cuckoo.agent.MainActivity -a android.intent.action.MAIN,android.intent.action.BOOT_COMPLETED -c android.intent.category.LAUNCHER", 4) time.sleep(2) self._fm.set_frida_server() time.sleep(2) run_cmd_with_timeout("adb shell am restart", 4) self._fm.process_on = True time.sleep(10) pass log.debug("%s: not ready yet", self.id) time.sleep(1) self.server._set_timeout(None) return True def upload_analyzer(self): """Upload analyzer to guest. @return: operation status. """ zip_data = StringIO() zip_file = ZipFile(zip_data, "w", ZIP_STORED) # Select the proper analyzer's folder according to the operating # system associated with the current machine. root = os.path.join(CUCKOO_ROOT, "analyzer", self.platform) root_len = len(os.path.abspath(root)) if not os.path.exists(root): log.error("No valid analyzer found at path: %s", root) return False # Walk through everything inside the analyzer's folder and write # them to the zip archive. for root, dirs, files in os.walk(root): archive_root = os.path.abspath(root)[root_len:] for name in files: path = os.path.join(root, name) archive_name = os.path.join(archive_root, name) zip_file.write(path, archive_name) zip_file.close() data = xmlrpclib.Binary(zip_data.getvalue()) zip_data.close() log.debug("Uploading analyzer to guest (id=%s, ip=%s)", self.id, self.ip) # Send the zip containing the analyzer to the agent running inside # the guest. try: self.server.add_analyzer(data) except socket.timeout: raise CuckooGuestError("{0}: guest communication timeout: unable " "to upload agent, check networking or try " "to increase timeout".format(self.id)) def start_analysis(self, options): """Start analysis. @param options: options. @return: operation status. """ log.info("Starting analysis on guest (id=%s, ip=%s)", self.id, self.ip) # TODO: deal with unicode URLs. if options["category"] == "file": options["file_name"] = sanitize_filename(options["file_name"]) # If the analysis timeout is higher than the critical timeout, # automatically increase the critical timeout by one minute. if options["timeout"] > self.timeout: log.debug("Automatically increased critical timeout to %s", self.timeout) self.timeout = options["timeout"] + 60 # Get and set dynamically generated resultserver port. options["port"] = str(ResultServer().port) try: # Wait for the agent to respond. This is done to check the # availability of the agent and verify that it's ready to receive # data. if self.platform == "android_device": self._fm = FridaManager(self.ip, options) self.wait(CUCKOO_GUEST_INIT) # Invoke the upload of the analyzer to the guest. self.upload_analyzer() # Give the analysis options to the guest, so it can generate the # analysis.conf inside the guest. try: self.server.add_config(options) except: raise CuckooGuestError("{0}: unable to upload config to " "analysis machine".format(self.id)) # If the target of the analysis is a file, upload it to the guest. if options["category"] == "file": try: file_data = open(options["target"], "rb").read() except (IOError, OSError) as e: raise CuckooGuestError("Unable to read {0}, error: " "{1}".format(options["target"], e)) data = xmlrpclib.Binary(file_data) try: self.server.add_malware(data, options["file_name"]) except Exception as e: raise CuckooGuestError("{0}: unable to upload malware to " "analysis machine: {1}".format( self.id, e)) # Launch the analyzer. if self._fm: self._fm.init() self._fm.start() self._fm.wait_for_startup() pid = self.server.execute() log.debug("%s: analyzer started with PID %d", self.id, pid) # If something goes wrong when establishing the connection, raise an # exception and abort the analysis. except (socket.timeout, socket.error): raise CuckooGuestError("{0}: guest communication timeout, check " "networking or try to increase " "timeout".format(self.id)) def wait_for_completion(self): """Wait for analysis completion. @return: operation status. """ log.debug("%s: waiting for completion", self.id) end = time.time() + self.timeout self.server._set_timeout(self.timeout) while True: time.sleep(1) # If the analysis hits the critical timeout, just return straight # away and try to recover the analysis results from the guest. if time.time() > end: self._fm.turn_off() raise CuckooGuestError( "The analysis hit the critical timeout, terminating.") try: status = self.server.get_status() except Exception as e: log.debug("%s: error retrieving status: %s", self.id, e) continue # React according to the returned status. if status == CUCKOO_GUEST_COMPLETED: self._fm.turn_off() log.info("%s: analysis completed successfully", self.id) break elif status == CUCKOO_GUEST_FAILED: error = self.server.get_error() if not error: error = "unknown error" self._fm.turn_off() raise CuckooGuestError("Analysis failed: {0}".format(error)) elif self._fm.check_frida_status(): raise CuckooGuestError( "Analysis failed: {0}".format("Frida is not working")) else: log.debug("%s: analysis not completed yet (status=%s)", self.id, status) self.server._set_timeout(None)
class OldGuestManager(object): """Old and deprecated Guest Manager. This class handles the communications with the old agent running in the virtual machine. """ def __init__(self, vm_id, ip, platform="windows"): """@param ip: guest's IP address. @param platform: guest's operating system type. """ self.id = vm_id self.ip = ip self.platform = platform self.cfg = Config() self.timeout = self.cfg.timeouts.critical url = "http://{0}:{1}".format(ip, CUCKOO_GUEST_PORT) self.server = TimeoutServer(url, allow_none=True, timeout=self.timeout) def wait(self, status): """Waiting for status. @param status: status. @return: always True. """ log.debug("%s: waiting for status 0x%.04x", self.id, status) end = time.time() + self.timeout self.server._set_timeout(self.timeout) while True: # Check if we've passed the timeout. if time.time() > end: raise CuckooGuestError("{0}: the guest initialization hit the " "critical timeout, analysis " "aborted.".format(self.id)) try: # If the server returns the given status, break the loop # and return. if self.server.get_status() == status: log.debug("%s: status ready", self.id) break except: pass log.debug("%s: not ready yet", self.id) time.sleep(1) self.server._set_timeout(None) return True def upload_analyzer(self, hashes_path): """Upload analyzer to guest. @return: operation status. """ zip_data = StringIO() zip_file = ZipFile(zip_data, "w", ZIP_STORED) # Select the proper analyzer's folder according to the operating # system associated with the current machine. root = os.path.join(CUCKOO_ROOT, "analyzer", self.platform) root_len = len(os.path.abspath(root)) if not os.path.exists(root): log.error("No valid analyzer found at path: %s", root) return False # Walk through everything inside the analyzer's folder and write # them to the zip archive. for root, dirs, files in os.walk(root): archive_root = os.path.abspath(root)[root_len:] for name in files: path = os.path.join(root, name) archive_name = os.path.join(archive_root, name) zip_file.write(path, archive_name) if hashes_path: zip_file.write(hashes_path, "hashes.bin") zip_file.close() data = xmlrpclib.Binary(zip_data.getvalue()) zip_data.close() log.debug("Uploading analyzer to guest (id=%s, ip=%s)", self.id, self.ip) # Send the zip containing the analyzer to the agent running inside # the guest. try: self.server.add_analyzer(data) except socket.timeout: raise CuckooGuestError("{0}: guest communication timeout: unable " "to upload agent, check networking or try " "to increase timeout".format(self.id)) def start_analysis(self, options): """Start analysis. @param options: options. @return: operation status. """ # TODO Deal with unicode URLs, should probably try URL encoding. # Unicode files are being taken care of. # If the analysis timeout is higher than the critical timeout, # automatically increase the critical timeout by one minute. if options["timeout"] > self.timeout: log.debug("Automatically increased critical timeout to %s", self.timeout) self.timeout = options["timeout"] + 60 opt = {} for row in options["options"].split(","): if "=" not in row: continue key, value = row.split("=", 1) opt[key.strip()] = value.strip() # Check whether the hashes file exists if it was provided. if "hashes-path" in opt: if not os.path.isfile(opt["hashes-path"]): raise CuckooGuestError("Non-existing hashing file provided!") try: # Wait for the agent to respond. This is done to check the # availability of the agent and verify that it's ready to receive # data. self.wait(CUCKOO_GUEST_INIT) # Invoke the upload of the analyzer to the guest. self.upload_analyzer(opt.get("hashes-path")) # Give the analysis options to the guest, so it can generate the # analysis.conf inside the guest. try: self.server.add_config(options) except: raise CuckooGuestError("{0}: unable to upload config to " "analysis machine".format(self.id)) # If the target of the analysis is a file, upload it to the guest. if options["category"] == "file": try: file_data = open(options["target"], "rb").read() except (IOError, OSError) as e: raise CuckooGuestError("Unable to read {0}, error: " "{1}".format(options["target"], e)) data = xmlrpclib.Binary(file_data) try: self.server.add_malware(data, options["file_name"]) except Exception as e: raise CuckooGuestError("{0}: unable to upload malware to " "analysis machine: {1}".format( self.id, e)) # Launch the analyzer. pid = self.server.execute() log.debug("%s: analyzer started with PID %d", self.id, pid) # If something goes wrong when establishing the connection, raise an # exception and abort the analysis. except (socket.timeout, socket.error): raise CuckooGuestError("{0}: guest communication timeout, check " "networking or try to increase " "timeout".format(self.id)) def wait_for_completion(self): """Wait for analysis completion. @return: operation status. """ log.debug("%s: waiting for completion", self.id) end = time.time() + self.timeout self.server._set_timeout(self.timeout) while True: time.sleep(1) # If the analysis hits the critical timeout, just return straight # away and try to recover the analysis results from the guest. if time.time() > end: raise CuckooGuestError( "The analysis hit the critical timeout, terminating.") try: status = self.server.get_status() except Exception as e: log.debug("%s: error retrieving status: %s", self.id, e) continue # React according to the returned status. if status == CUCKOO_GUEST_COMPLETED: log.info("%s: analysis completed successfully", self.id) break elif status == CUCKOO_GUEST_FAILED: error = self.server.get_error() if not error: error = "unknown error" raise CuckooGuestError("Analysis failed: {0}".format(error)) else: log.debug("%s: analysis not completed yet (status=%s)", self.id, status) self.server._set_timeout(None)
class OldGuestManager(object): """Old and deprecated Guest Manager. This class handles the communications with the old agent running in the virtual machine. """ def __init__(self, vm_id, ip, platform="windows"): """@param ip: guest's IP address. @param platform: guest's operating system type. """ self.id = vm_id self.ip = ip self.platform = platform self.cfg = Config() self.timeout = self.cfg.timeouts.critical url = "http://{0}:{1}".format(ip, CUCKOO_GUEST_PORT) self.server = TimeoutServer(url, allow_none=True, timeout=self.timeout) def wait(self, status): """Waiting for status. @param status: status. @return: always True. """ log.debug("%s: waiting for status 0x%.04x", self.id, status) end = time.time() + self.timeout self.server._set_timeout(self.timeout) while True: # Check if we've passed the timeout. if time.time() > end: raise CuckooGuestError("{0}: the guest initialization hit the " "critical timeout, analysis " "aborted.".format(self.id)) try: # If the server returns the given status, break the loop # and return. if self.server.get_status() == status: log.debug("%s: status ready", self.id) break except: pass log.debug("%s: not ready yet", self.id) time.sleep(1) self.server._set_timeout(None) return True def upload_analyzer(self, hashes_path): """Upload analyzer to guest. @return: operation status. """ zip_data = StringIO() zip_file = ZipFile(zip_data, "w", ZIP_STORED) # Select the proper analyzer's folder according to the operating # system associated with the current machine. root = os.path.join(CUCKOO_ROOT, "analyzer", self.platform) root_len = len(os.path.abspath(root)) if not os.path.exists(root): log.error("No valid analyzer found at path: %s", root) return False # Walk through everything inside the analyzer's folder and write # them to the zip archive. for root, dirs, files in os.walk(root): archive_root = os.path.abspath(root)[root_len:] for name in files: path = os.path.join(root, name) archive_name = os.path.join(archive_root, name) zip_file.write(path, archive_name) if hashes_path: zip_file.write(hashes_path, "hashes.bin") zip_file.close() data = xmlrpclib.Binary(zip_data.getvalue()) zip_data.close() log.debug("Uploading analyzer to guest (id=%s, ip=%s)", self.id, self.ip) # Send the zip containing the analyzer to the agent running inside # the guest. try: self.server.add_analyzer(data) except socket.timeout: raise CuckooGuestError("{0}: guest communication timeout: unable " "to upload agent, check networking or try " "to increase timeout".format(self.id)) def start_analysis(self, options): """Start analysis. @param options: options. @return: operation status. """ # TODO Deal with unicode URLs, should probably try URL encoding. # Unicode files are being taken care of. # If the analysis timeout is higher than the critical timeout, # automatically increase the critical timeout by one minute. if options["timeout"] > self.timeout: log.debug("Automatically increased critical timeout to %s", self.timeout) self.timeout = options["timeout"] + 60 opt = {} for row in options["options"].split(","): if "=" not in row: continue key, value = row.split("=", 1) opt[key.strip()] = value.strip() # Check whether the hashes file exists if it was provided. if "hashes-path" in opt: if not os.path.isfile(opt["hashes-path"]): raise CuckooGuestError("Non-existing hashing file provided!") try: # Wait for the agent to respond. This is done to check the # availability of the agent and verify that it's ready to receive # data. self.wait(CUCKOO_GUEST_INIT) # Invoke the upload of the analyzer to the guest. self.upload_analyzer(opt.get("hashes-path")) # Give the analysis options to the guest, so it can generate the # analysis.conf inside the guest. try: self.server.add_config(options) except: raise CuckooGuestError("{0}: unable to upload config to " "analysis machine".format(self.id)) # If the target of the analysis is a file, upload it to the guest. if options["category"] == "file": try: file_data = open(options["target"], "rb").read() except (IOError, OSError) as e: raise CuckooGuestError("Unable to read {0}, error: " "{1}".format(options["target"], e)) data = xmlrpclib.Binary(file_data) try: self.server.add_malware(data, options["file_name"]) except Exception as e: raise CuckooGuestError("{0}: unable to upload malware to " "analysis machine: {1}".format(self.id, e)) # Launch the analyzer. pid = self.server.execute() log.debug("%s: analyzer started with PID %d", self.id, pid) # If something goes wrong when establishing the connection, raise an # exception and abort the analysis. except (socket.timeout, socket.error): raise CuckooGuestError("{0}: guest communication timeout, check " "networking or try to increase " "timeout".format(self.id)) def wait_for_completion(self): """Wait for analysis completion. @return: operation status. """ log.debug("%s: waiting for completion", self.id) end = time.time() + self.timeout self.server._set_timeout(self.timeout) while True: time.sleep(1) # If the analysis hits the critical timeout, just return straight # away and try to recover the analysis results from the guest. if time.time() > end: raise CuckooGuestError("The analysis hit the critical timeout, terminating.") try: status = self.server.get_status() except Exception as e: log.debug("%s: error retrieving status: %s", self.id, e) continue # React according to the returned status. if status == CUCKOO_GUEST_COMPLETED: log.info("%s: analysis completed successfully", self.id) break elif status == CUCKOO_GUEST_FAILED: error = self.server.get_error() if not error: error = "unknown error" raise CuckooGuestError("Analysis failed: {0}".format(error)) else: log.debug("%s: analysis not completed yet (status=%s)", self.id, status) self.server._set_timeout(None)
class GuestManager: """Guest Mananager. This class handles the communications with the agents running in the machines. """ def __init__(self, vm_id, ip, platform="windows"): """@param ip: guest's IP address. @param platform: guest's operating system type. """ self.id = vm_id self.ip = ip self.platform = platform self.cfg = Config() self.timeout = self.cfg.timeouts.critical url = "http://{0}:{1}".format(ip, CUCKOO_GUEST_PORT) self.server = TimeoutServer(url, allow_none=True, timeout=self.timeout) def wait(self, status): """Waiting for status. @param status: status. @return: always True. """ log.debug("%s: waiting for status 0x%.04x", self.id, status) # Create an event that will invoke a function to stop the loop when # the critical timeout is h it. abort = Event() abort.clear() def die(): abort.set() # Initialize the timer. timer = Timer(self.timeout, die) timer.start() self.server._set_timeout(self.timeout) while True: # Check if the timer was hit and the abort event was set. if abort.is_set(): raise CuckooGuestError("{0}: the guest initialization hit the " "critical timeout, analysis " "aborted".format(self.id)) try: # If the server returns the given status, break the loop # and return. if self.server.get_status() == status: log.debug("%s: status ready", self.id) break except: pass log.debug("%s: not ready yet", self.id) time.sleep(1) self.server._set_timeout(None) return True def upload_analyzer(self): """Upload analyzer to guest. @return: operation status. """ zip_data = StringIO() zip_file = ZipFile(zip_data, "w", ZIP_STORED) # Select the proper analyzer's folder according to the operating # system associated with the current machine. root = os.path.join("analyzer", self.platform) root_len = len(os.path.abspath(root)) if not os.path.exists(root): log.error("No valid analyzer found at path: %s", root) return False # Walk through everything inside the analyzer's folder and write # them to the zip archive. for root, dirs, files in os.walk(root): archive_root = os.path.abspath(root)[root_len:] for name in files: path = os.path.join(root, name) archive_name = os.path.join(archive_root, name) zip_file.write(path, archive_name) zip_file.close() data = xmlrpclib.Binary(zip_data.getvalue()) zip_data.close() log.debug("Uploading analyzer to guest (id=%s, ip=%s)", self.id, self.ip) # Send the zip containing the analyzer to the agent running inside # the guest. try: self.server.add_analyzer(data) except socket.timeout: raise CuckooGuestError("{0}: guest communication timeout: unable " "to upload agent, check networking or try " "to increase timeout".format(self.id)) def start_analysis(self, options): """Start analysis. @param options: options. @return: operation status. """ log.info("Starting analysis on guest (id=%s, ip=%s)", self.id, self.ip) # TODO: deal with unicode URLs. if options["category"] == "file": options["file_name"] = sanitize_filename(options["file_name"]) try: # Wait for the agent to respond. This is done to check the # availability of the agent and verify that it's ready to receive # data. self.wait(CUCKOO_GUEST_INIT) # Invoke the upload of the analyzer to the guest. self.upload_analyzer() # Give the analysis options to the guest, so it can generate the # analysis.conf inside the guest. try: self.server.add_config(options) except: raise CuckooGuestError("{0}: unable to upload config to " "analysis machine".format(self.id)) # If the target of the analysis is a file, upload it to the guest. if options["category"] == "file": try: file_data = open(options["target"], "rb").read() except (IOError, OSError) as e: raise CuckooGuestError("Unable to read {0}, error: " "{1}".format(options["target"], e)) data = xmlrpclib.Binary(file_data) try: self.server.add_malware(data, options["file_name"]) except MemoryError as e: raise CuckooGuestError("{0}: unable to upload malware to " "analysis machine, not enough " "memory".format(self.id)) # Launch the analyzer. pid = self.server.execute() log.debug("%s: analyzer started with PID %d", self.id, pid) # If something goes wrong when establishing the connection, raise an # exception and abort the analysis. except (socket.timeout, socket.error): raise CuckooGuestError("{0}: guest communication timeout, check " "networking or try to increase " "timeout".format(self.id)) def wait_for_completion(self): """Wait for analysis completion. @return: operation status. """ log.debug("%s: waiting for completion", self.id) # Same procedure as in self.wait(). Just look at the comments there. abort = Event() abort.clear() def die(): abort.set() timer = Timer(self.timeout, die) timer.start() self.server._set_timeout(self.timeout) while True: time.sleep(1) # If the analysis hits the critical timeout, just return straight # straight away and try to recover the analysis results from the # guest. if abort.is_set(): raise CuckooGuestError("The analysis hit the critical timeout," " terminating") try: status = self.server.get_status() except Exception as e: log.debug("%s: error retrieving status: %s", self.id, e) continue # React according to the returned status. if status == CUCKOO_GUEST_COMPLETED: log.info("%s: analysis completed successfully", self.id) break elif status == CUCKOO_GUEST_FAILED: error = self.server.get_error() if not error: error = "unknown error" raise CuckooGuestError("Analysis failed: {0}".format(error)) else: log.debug("%s: analysis not completed yet (status=%s)", self.id, status) self.server._set_timeout(None)
class OldGuestManager(object): """Old and deprecated Guest Manager. This class handles the communications with the old agent running in the virtual machine. """ def __init__(self, vm_id, ip, platform, task_id): """@param ip: guest's IP address. @param platform: guest's operating system type. """ self.id = vm_id self.ip = ip self.platform = platform self.task_id = task_id self.cfg = Config() self.timeout = self.cfg.timeouts.critical self.burn_limit = self.cfg.burn.limit # custom self.hv = self.cfg.cuckoo.machinery # custom url = "http://{0}:{1}".format(ip, CUCKOO_GUEST_PORT) self.server = TimeoutServer(url, allow_none=True, timeout=self.timeout) def wait(self, status): """Waiting for status. @param status: status. @return: always True. """ log.debug("%s: waiting for status 0x%.04x", self.id, status) end = time.time() + self.timeout self.server._set_timeout(self.timeout) while db.guest_get_status(self.task_id) == "starting": # Check if we've passed the timeout. if time.time() > end: raise CuckooGuestError("{0}: the guest initialization hit the " "critical timeout, analysis " "aborted.".format(self.id)) try: # If the server returns the given status, break the loop # and return. if self.server.get_status() == status: log.debug("%s: status ready", self.id) break except: pass log.debug("%s: not ready yet", self.id) time.sleep(1) self.server._set_timeout(None) return True def upload_analyzer(self, monitor): """Upload analyzer to guest. @return: operation status. """ zip_data = StringIO() zip_file = ZipFile(zip_data, "w", ZIP_STORED) # Select the proper analyzer's folder according to the operating # system associated with the current machine. root = os.path.join(CUCKOO_ROOT, "analyzer", self.platform) root_len = len(os.path.abspath(root)) if not os.path.exists(root): log.error("No valid analyzer found at path: %s", root) return False # Walk through everything inside the analyzer's folder and write # them to the zip archive. for root, dirs, files in os.walk(root): archive_root = os.path.abspath(root)[root_len:] for name in files: path = os.path.join(root, name) archive_name = os.path.join(archive_root, name) zip_file.write(path, archive_name) # Include the chosen monitoring component. if self.platform == "windows": dirpath = os.path.join(CUCKOO_ROOT, "data", "monitor", monitor) for name in os.listdir(dirpath): path = os.path.join(dirpath, name) archive_name = os.path.join("/bin", name) zip_file.write(path, archive_name) zip_file.close() data = xmlrpclib.Binary(zip_data.getvalue()) zip_data.close() log.debug("Uploading analyzer to guest (id=%s, ip=%s)", self.id, self.ip) # Send the zip containing the analyzer to the agent running inside # the guest. try: self.server.add_analyzer(data) except socket.timeout: raise CuckooGuestError("{0}: guest communication timeout: unable " "to upload agent, check networking or try " "to increase timeout".format(self.id)) def start_analysis(self, options, monitor): """Start analysis. @param options: options. @return: operation status. """ # TODO Deal with unicode URLs, should probably try URL encoding. # Unicode files are being taken care of. # If the analysis timeout is higher than the critical timeout, # automatically increase the critical timeout by one minute. if options["timeout"] > self.timeout: # custom log.debug("Automatically increased critical timeout to %s", self.timeout) # custom self.timeout = options["timeout"] + 60 # custom try: # Wait for the agent to respond. This is done to check the # availability of the agent and verify that it's ready to receive # data. self.wait(CUCKOO_GUEST_INIT) # Invoke the upload of the analyzer to the guest. self.upload_analyzer(monitor) # Give the analysis options to the guest, so it can generate the # analysis.conf inside the guest. try: self.server.add_config(options) except: raise CuckooGuestError("{0}: unable to upload config to " "analysis machine".format(self.id)) # If the target of the analysis is a file, upload it to the guest. if options["category"] == "file": try: file_data = open(options["target"], "rb").read() except (IOError, OSError) as e: raise CuckooGuestError("Unable to read {0}, error: " "{1}".format(options["target"], e)) data = xmlrpclib.Binary(file_data) try: self.server.add_malware(data, options["file_name"]) except Exception as e: raise CuckooGuestError("{0}: unable to upload malware to " "analysis machine: {1}".format( self.id, e)) # Launch the analyzer. pid = self.server.execute() log.debug("%s: analyzer started with PID %d", self.id, pid) # If something goes wrong when establishing the connection, raise an # exception and abort the analysis. except (socket.timeout, socket.error): raise CuckooGuestError("{0}: guest communication timeout, check " "networking or try to increase " "timeout".format(self.id)) def wait_for_completion(self): """Wait for analysis completion. @return: operation status. """ log.debug("%s: waiting for completion", self.id) end = time.time() + self.timeout self.server._set_timeout(self.timeout) s = 0 burn = False # custom home = os.path.expanduser('~') child = os.popen('python %s/odoriba/child.py' % home) # custom print "A" * 100 log.debug('[MADE Child FLAG] python ~/odoriba/child.py') FIN_flag = False while db.guest_get_status(self.task_id) == "running": time.sleep(1) s += 1 # If the analysis hits the critical timeout, just return straight # away and try to recover the analysis results from the guest. if time.time() > end: raise CuckooGuestError( "The analysis hit the critical timeout, terminating.") try: status = self.server.get_status() except Exception as e: log.debug("%s: error retrieving status: %s", self.id, e) continue # React according to the returned status. if status == CUCKOO_GUEST_COMPLETED or FIN_flag == True: #custom log.info("%s: analysis completed successfully", self.id) status = CUCKOO_GUEST_COMPLETED #custom break elif status == CUCKOO_GUEST_FAILED: error = self.server.get_error() if not error: error = "unknown error" raise CuckooGuestError("Analysis failed: {0}".format(error)) else: log.debug( "%s: analysis not completed yet (status=%s) (spent=%ds/%s)", self.id, status, s, self.timeout) # custom ############### custom from here ################# if burn == False: s, FIN_flag = internet_control.check( self.hv, self.timeout, log, int(self.burn_limit)) if FIN_flag != True: burn = True log.debug( "[return odoriba] internet control out.. %ds" % s) if subprocess.check_output(['ps']).find('child.py') == -1: FIN_flag = True log.debug('[Analysis Finished] FIN_flag set in guest.py') ############### custom until here ################# self.server._set_timeout(None)
class GuestManager: """Guest Mananager. This class handles the communications with the agents running in the machines. """ def __init__(self, vm_id, ip, platform="windows"): """@param ip: guest's IP address. @param platform: guest's operating system type. """ self.id = vm_id self.ip = ip self.platform = platform self.cfg = Config() self.timeout = self.cfg.timeouts.critical self.server = TimeoutServer("http://{0}:{1}".format( ip, CUCKOO_GUEST_PORT), allow_none=True, timeout=self.timeout) def wait(self, status): """Waiting for status. @param status: status. @return: always True. """ log.debug("%s: waiting for status 0x%.04x", self.id, status) # Create an event that will invoke a function to stop the loop when # the critical timeout is h it. abort = Event() abort.clear() def die(): abort.set() # Initialize the timer. timer = Timer(self.timeout, die) timer.start() self.server._set_timeout(self.timeout) while True: # Check if the timer was hit and the abort event was set. if abort.is_set(): raise CuckooGuestError( "{0}: the guest initialization hit the " "critical timeout, analysis aborted".format(self.id)) try: # If the server returns the given status, break the loop # and return. if self.server.get_status() == status: log.debug("%s: status ready", self.id) break except: pass log.debug("%s: not ready yet", self.id) time.sleep(1) self.server._set_timeout(None) return True def upload_analyzer(self): """Upload analyzer to guest. @return: operation status. """ zip_data = StringIO() zip_file = ZipFile(zip_data, "w", ZIP_STORED) # Select the proper analyzer's folder according to the operating # system associated with the current machine. root = os.path.join("analyzer", self.platform) root_len = len(os.path.abspath(root)) if not os.path.exists(root): log.error("No valid analyzer found at path: %s", root) return False # Walk through everything inside the analyzer's folder and write # them to the zip archive. for root, dirs, files in os.walk(root): archive_root = os.path.abspath(root)[root_len:] for name in files: path = os.path.join(root, name) archive_name = os.path.join(archive_root, name) zip_file.write(path, archive_name) zip_file.close() data = xmlrpclib.Binary(zip_data.getvalue()) zip_data.close() log.debug("Uploading analyzer to guest (id=%s, ip=%s)", self.id, self.ip) # Send the zip containing the analyzer to the agent running inside # the guest. try: self.server.add_analyzer(data) except socket.timeout: raise CuckooGuestError("{0}: guest communication timeout: unable " "to upload agent, check networking or try " "to increase timeout".format(self.id)) def start_analysis(self, options): """Start analysis. @param options: options. @return: operation status. """ log.info("Starting analysis on guest (id=%s, ip=%s)", self.id, self.ip) try: # Wait for the agent to respond. This is done to check the # availability of the agent and verify that it's ready to receive # data. self.wait(CUCKOO_GUEST_INIT) # Invoke the upload of the analyzer to the guest. self.upload_analyzer() # Give the analysis options to the guest, so it can generate the # analysis.conf inside the guest. self.server.add_config(options) # If the target of the analysis is a file, upload it to the guest. if options["category"] == "file": try: file_data = open(options["target"], "rb").read() except (IOError, OSError) as e: raise CuckooGuestError( "Unable to read {0}, error: {1}".format( options["target"], e)) data = xmlrpclib.Binary(file_data) try: self.server.add_malware(data, options["file_name"]) except MemoryError as e: raise CuckooGuestError( "{0}: unable to upload malware to analysis machine, not enough memory" .format(self.id)) # Launch the analyzer. pid = self.server.execute() log.debug("%s: analyzer started with PID %d", self.id, pid) # If something goes wrong when establishing the connection, raise an # exception and abort the analysis. except (socket.timeout, socket.error): raise CuckooGuestError( "{0}: guest communication timeout, check " "networking or try to increase timeout".format(self.id)) def wait_for_completion(self): """Wait for analysis completion. @return: operation status. """ log.debug("%s: waiting for completion", self.id) # Same procedure as in self.wait(). Just look at the comments there. abort = Event() abort.clear() def die(): abort.set() timer = Timer(self.timeout, die) timer.start() self.server._set_timeout(self.timeout) while True: time.sleep(1) # If the analysis hits the critical timeout, just return straight # straight away and try to recover the analysis results from the # guest. if abort.is_set(): raise CuckooGuestError("The analysis hit the critical timeout," " terminating") try: status = self.server.get_status() except Exception as e: log.debug("%s: error retrieving status: %s", self.id, e) continue # React according to the returned status. if status == CUCKOO_GUEST_COMPLETED: log.info("%s: analysis completed successfully", self.id) break elif status == CUCKOO_GUEST_FAILED: error = self.server.get_error() if not error: error = "unknown error" raise CuckooGuestError("Analysis failed: {0}".format(error)) else: log.debug("%s: analysis not completed yet (status=%s)", self.id, status) self.server._set_timeout(None)
class GuestManager: """Guest Manager. This class handles the communications with the agents running in the machines. """ def __init__(self, vm_id, ip, platform="windows"): """@param ip: guest's IP address. @param platform: guest's operating system type. """ self.id = vm_id self.ip = ip self.platform = platform self.cfg = Config() self.timeout = self.cfg.timeouts.critical url = "http://{0}:{1}".format(ip, CUCKOO_GUEST_PORT) self.server = TimeoutServer(url, allow_none=True, timeout=self.timeout) def wait(self, status): """Waiting for status. @param status: status. @return: always True. """ log.debug("%s: waiting for status 0x%.04x", self.id, status) end = time.time() + self.timeout self.server._set_timeout(self.timeout) while True: # Check if we've passed the timeout. if time.time() > end: raise CuckooGuestError("{0}: the guest initialization hit the " "critical timeout, analysis " "aborted.".format(self.id)) try: # If the server returns the given status, break the loop # and return. if self.server.get_status() == status: log.debug("%s: status ready", self.id) break except: pass log.debug("%s: not ready yet", self.id) time.sleep(1) self.server._set_timeout(None) return True from random import randint def simulateUserInteraction(self): eventCodes = [ 57, 58, 59, 60, 61, 62, 66, 19, 20, 21, 22, 23, 82, 92, 93, 67 ] eventLen = len(eventCodes) runs = self.randint(5, 9) # If the app has started press enter first subprocess.call( [misc_config.ADB_PATH, "shell", "input", "keyevent", "66"]) # Randomly choose some input events for x in range(runs): rand = self.randint(0, eventLen - 1) subprocess.call([ misc_config.ADB_PATH, "shell", "input", "keyevent", str(eventCodes[rand]) ]) # Between each motion sleep a random amount of time between 1 and 6 seconds timeToSleep = self.randint(1, 6) time.sleep(timeToSleep) # Again, press enter at the end subprocess.call( [misc_config.ADB_PATH, "shell", "input", "keyevent", "66"]) def getProcessList(self): with open(settings.PLIST_FILE, "w+") as outFile: log.info("Get process list") processList = subprocess.check_output( [misc_config.ADB_PATH, "shell", "ps"]) outFile.write(processList) outFile.close() def getListeningPorts(self): with open(settings.NETSTAT_LIST, "w+") as outFile: log.info("Get listening ports") netstat = subprocess.check_output( [misc_config.ADB_PATH, "shell", "netstat -lntu"]) outFile.write(netstat) outFile.close() def generateFileList(self): log.info("Get file list") with open(settings.SBOX_FOLDER_LIST, "w+") as outFile: folderList = subprocess.check_output( [misc_config.ADB_PATH, "shell", "ls", "/data/data"]) outFile.write(folderList) outFile.close() def upload_analyzer(self): """Upload analyzer to guest. @return: operation status. """ zip_data = StringIO() zip_file = ZipFile(zip_data, "w", ZIP_STORED) # Select the proper analyzer's folder according to the operating # system associated with the current machine. root = os.path.join(CUCKOO_ROOT, "analyzer", self.platform) root_len = len(os.path.abspath(root)) if not os.path.exists(root): log.error("No valid analyzer found at path: %s", root) return False # Walk through everything inside the analyzer's folder and write # them to the zip archive. for root, dirs, files in os.walk(root): archive_root = os.path.abspath(root)[root_len:] for name in files: path = os.path.join(root, name) archive_name = os.path.join(archive_root, name) zip_file.write(path, archive_name) zip_file.close() data = xmlrpclib.Binary(zip_data.getvalue()) zip_data.close() log.debug("Uploading analyzer to guest (id=%s, ip=%s)", self.id, self.ip) # Send the zip containing the analyzer to the agent running inside # the guest. try: self.server.add_analyzer(data) except socket.timeout: raise CuckooGuestError("{0}: guest communication timeout: unable " "to upload agent, check networking or try " "to increase timeout".format(self.id)) def start_analysis(self, options): """Start analysis. @param options: options. @return: operation status. """ log.info("Starting analysis on guest (id=%s, ip=%s)", self.id, self.ip) if misc_config.ENABLE_CUCKOO_EXTRA_INFO: time.sleep(10) subprocess.call([misc_config.ADB_PATH, "connect", "192.168.56.10"]) log.info("Starting to collect information") # Custom: Get process information try: self.getProcessList() # Get listening Ports self.getListeningPorts() self.generateFileList() except: log.info("ADB Error occured! Try again...") try: subprocess.Popen([misc_config.ADB_PATH, "kill-server"]) subprocess.Popen(["killall adb"]) time.sleep(2) subprocess.call( [misc_config.ADB_PATH, "connect", "192.168.56.10"]) time.sleep(5) self.getProcessList() # Get listening Ports self.getListeningPorts() self.generateFileList() except: log.info("ADB Error for the second time!") # TODO: deal with unicode URLs. if options["category"] == "file": options["file_name"] = sanitize_filename(options["file_name"]) # If the analysis timeout is higher than the critical timeout, # automatically increase the critical timeout by one minute. if options["timeout"] > self.timeout: log.debug("Automatically increased critical timeout to %s", self.timeout) self.timeout = options["timeout"] + 60 # Get and set dynamically generated resultserver port. options["port"] = str(ResultServer().port) try: # Wait for the agent to respond. This is done to check the # availability of the agent and verify that it's ready to receive # data. self.wait(CUCKOO_GUEST_INIT) # Invoke the upload of the analyzer to the guest. self.upload_analyzer() # Give the analysis options to the guest, so it can generate the # analysis.conf inside the guest. try: self.server.add_config(options) except: raise CuckooGuestError("{0}: unable to upload config to " "analysis machine".format(self.id)) # If the target of the analysis is a file, upload it to the guest. if options["category"] == "file": try: file_data = open(options["target"], "rb").read() except (IOError, OSError) as e: raise CuckooGuestError("Unable to read {0}, error: " "{1}".format(options["target"], e)) data = xmlrpclib.Binary(file_data) try: self.server.add_malware(data, options["file_name"]) except Exception as e: raise CuckooGuestError("{0}: unable to upload malware to " "analysis machine: {1}".format( self.id, e)) # Launch the analyzer. pid = self.server.execute() log.debug("%s: analyzer started with PID %d", self.id, pid) # If something goes wrong when establishing the connection, raise an # exception and abort the analysis. except (socket.timeout, socket.error): raise CuckooGuestError("{0}: guest communication timeout, check " "networking or try to increase " "timeout".format(self.id)) # Custom # Give the app some time to start up log.debug("Starting to simulate user interaction") time.sleep(10) self.simulateUserInteraction() def wait_for_completion(self): """Wait for analysis completion. @return: operation status. """ log.debug("%s: waiting for completion", self.id) end = time.time() + self.timeout self.server._set_timeout(self.timeout) while True: time.sleep(1) # If the analysis hits the critical timeout, just return straight # away and try to recover the analysis results from the guest. if time.time() > end: raise CuckooGuestError( "The analysis hit the critical timeout, terminating.") try: status = self.server.get_status() except Exception as e: log.debug("%s: error retrieving status: %s", self.id, e) continue # React according to the returned status. if status == CUCKOO_GUEST_COMPLETED: log.info("%s: analysis completed successfully", self.id) break elif status == CUCKOO_GUEST_FAILED: error = self.server.get_error() if not error: error = "unknown error" raise CuckooGuestError("Analysis failed: {0}".format(error)) else: log.debug("%s: analysis not completed yet (status=%s)", self.id, status) self.server._set_timeout(None)
class GuestManager: """Guest Mananager. This class handles the communications with the agents running in the machines. """ def __init__(self, vm_id, ip, platform="windows"): """@param ip: guest's IP address. @param platform: guest's operating system type. """ self.id = vm_id self.ip = ip self.platform = platform self.cfg = Config() self.timeout = self.cfg.timeouts.critical url = "http://{0}:{1}".format(ip, CUCKOO_GUEST_PORT) self.server = TimeoutServer(url, allow_none=True, timeout=self.timeout) def wait(self, status): """Waiting for status. @param status: status. @return: always True. """ log.debug("%s: waiting for status 0x%.04x", self.id, status) # Create an event that will invoke a function to stop the loop when # the critical timeout is h it. abort = Event() abort.clear() def die(): abort.set() # Initialize the timer. timer = Timer(self.timeout, die) timer.start() self.server._set_timeout(self.timeout) while True: # Check if the timer was hit and the abort event was set. if abort.is_set(): raise CuckooGuestError("{0}: the guest initialization hit the " "critical timeout, analysis " "aborted".format(self.id)) try: # If the server returns the given status, break the loop # and return. if self.server.get_status() == status: log.debug("%s: status ready", self.id) break except: pass log.debug("%s: not ready yet", self.id) time.sleep(1) self.server._set_timeout(None) return True def upload_analyzer(self): """Upload analyzer to guest. @return: operation status. """ zip_data = StringIO() zip_file = ZipFile(zip_data, "w", ZIP_STORED) # Select the proper analyzer's folder according to the operating # system associated with the current machine. root = os.path.join("analyzer", self.platform) root_len = len(os.path.abspath(root)) if not os.path.exists(root): log.error("No valid analyzer found at path: %s", root) return False # Walk through everything inside the analyzer's folder and write # them to the zip archive. for root, dirs, files in os.walk(root): archive_root = os.path.abspath(root)[root_len:] for name in files: path = os.path.join(root, name) archive_name = os.path.join(archive_root, name) zip_file.write(path, archive_name) zip_file.close() data = xmlrpclib.Binary(zip_data.getvalue()) zip_data.close() log.debug("Uploading analyzer to guest (id=%s, ip=%s)", self.id, self.ip) # Send the zip containing the analyzer to the agent running inside # the guest. try: self.server.add_analyzer(data) except socket.timeout: raise CuckooGuestError("{0}: guest communication timeout: unable " "to upload agent, check networking or try " "to increase timeout".format(self.id)) def start_analysis(self, options): """Start analysis. @param options: options. @return: operation status. """ log.info("Starting analysis on guest (id=%s, ip=%s)", self.id, self.ip) # TODO: deal with unicode URLs. if options["category"] == "file": options["file_name"] = sanitize_filename(options["file_name"]) try: # Wait for the agent to respond. This is done to check the # availability of the agent and verify that it's ready to receive # data. self.wait(CUCKOO_GUEST_INIT) # Invoke the upload of the analyzer to the guest. self.upload_analyzer() # Give the analysis options to the guest, so it can generate the # analysis.conf inside the guest. try: self.server.add_config(options) except: raise CuckooGuestError("{0}: unable to upload config to " "analysis machine".format(self.id)) # If the target of the analysis is a file, upload it to the guest. if options["category"] == "file": try: file_data = open(options["target"], "rb").read() except (IOError, OSError) as e: raise CuckooGuestError("Unable to read {0}, error: " "{1}".format(options["target"], e)) data = xmlrpclib.Binary(file_data) try: self.server.add_malware(data, options["file_name"]) except MemoryError as e: raise CuckooGuestError("{0}: unable to upload malware to " "analysis machine, not enough " "memory".format(self.id)) pid = self.server.execute() log.debug("%s: analyzer started with PID %d", self.id, pid) # If something goes wrong when establishing the connection, raise an # exception and abort the analysis. except (socket.timeout, socket.error): raise CuckooGuestError("{0}: guest communication timeout, check " "networking or try to increase " "timeout".format(self.id)) def take_mem_dump(self, dumps_dir, machine, machinery, json_obj): """ Takes memory dump and dumps json info file. """ listdir = sorted(os.listdir(dumps_dir), key=int, reverse=True) if listdir == []: i = 1 else: i = int(listdir[0]) + 1 dump_dir = os.path.join(dumps_dir, str(i)) os.mkdir(dump_dir) machinery.dump_memory(machine.label, os.path.join(dump_dir, "memory.dmp")) json.dump(json_obj, file(os.path.join(dump_dir, "info.json"), "wb"), sort_keys=False, indent=4) def wait_for_completion(self, machine, storage, machinery): """Wait for analysis completion. @return: operation status. """ log.debug("%s: waiting for completion", self.id) # Same procedure as in self.wait(). Just look at the comments there. abort = Event() abort.clear() def die(): abort.set() # CHANGED: Added time-based dumps here. resumableTimer = ResumableTimer(self.timeout, die) resumableTimer.start() sec_counter = 0 mem_analysis_conf = Config( os.path.join(CUCKOO_ROOT, "conf", "memoryanalysis.conf")) time_to_sleep = int( mem_analysis_conf.time_based.time_to_sleep_before_dump_in_seconds) number_of_dumps = int(mem_analysis_conf.basic.max_number_of_dumps) memory_results_dir = os.path.join(storage, "memory") dumps_dir = os.path.join(memory_results_dir, "dumps") create_dir_safe(memory_results_dir) create_dir_safe(dumps_dir) while True: if abort.is_set(): info_dict = { "trigger": { "name": "End", "args": {} }, "time": str(sec_counter) } log.info("Taking dump before termination...") self.take_mem_dump(dumps_dir, machine, machinery, info_dict) raise CuckooGuestError( "The analysis hit the critical timeout, terminating") while Event(STOP_EVENT).is_set(): time.sleep(0.005) time.sleep(1) sec_counter += 1 if mem_analysis_conf.basic.time_based and sec_counter % time_to_sleep == 0: resumableTimer.stop() info_dict = { "trigger": { "name": "Time", "args": { "interval": time_to_sleep } } } self.take_mem_dump(dumps_dir, machine, machinery, info_dict) resumableTimer.resume() try: status = self.server.get_status() except Exception as e: log.debug("%s: error retrieving status: %s", self.id, e) continue # React according to the returned status. if status == CUCKOO_GUEST_COMPLETED: log.info("%s: analysis completed successfully", self.id) break elif status == CUCKOO_GUEST_FAILED: error = self.server.get_error() if not error: error = "unknown error" info_dict = {"trigger": {"name": "End", "args": {}}} log.info("Taking dump before termination...") self.take_mem_dump(dumps_dir, machine, machinery, info_dict) raise CuckooGuestError("Analysis failed: {0}".format(error)) # TODO: suspend machine and take dump else: log.debug("%s: analysis not completed yet (status=%s)", self.id, status) self.server._set_timeout(None) log.info("Taking dump before termination...") info_dict = {"trigger": {"name": "End", "args": {}}} self.take_mem_dump(dumps_dir, machine, machinery, info_dict)
class GuestManager: """Guest Mananager. This class handles the communications with the agents running in the machines. """ def __init__(self, vm_id, ip, platform="windows"): """@param ip: guest's IP address. @param platform: guest's operating system type. """ self.id = vm_id self.ip = ip self.platform = platform self.cfg = Config() self.timeout = self.cfg.timeouts.critical url = "http://{0}:{1}".format(ip, CUCKOO_GUEST_PORT) self.server = TimeoutServer(url, allow_none=True, timeout=self.timeout) def wait(self, status): """Waiting for status. @param status: status. @return: always True. """ log.debug("%s: waiting for status 0x%.04x", self.id, status) # Create an event that will invoke a function to stop the loop when # the critical timeout is h it. abort = Event() abort.clear() def die(): abort.set() # Initialize the timer. timer = Timer(self.timeout, die) timer.start() self.server._set_timeout(self.timeout) while True: # Check if the timer was hit and the abort event was set. if abort.is_set(): raise CuckooGuestError( "{0}: the guest initialization hit the " "critical timeout, analysis " "aborted".format(self.id) ) try: # If the server returns the given status, break the loop # and return. if self.server.get_status() == status: log.debug("%s: status ready", self.id) break except: pass log.debug("%s: not ready yet", self.id) time.sleep(1) self.server._set_timeout(None) return True def upload_analyzer(self): """Upload analyzer to guest. @return: operation status. """ zip_data = StringIO() zip_file = ZipFile(zip_data, "w", ZIP_STORED) # Select the proper analyzer's folder according to the operating # system associated with the current machine. root = os.path.join("analyzer", self.platform) root_len = len(os.path.abspath(root)) if not os.path.exists(root): log.error("No valid analyzer found at path: %s", root) return False # Walk through everything inside the analyzer's folder and write # them to the zip archive. for root, dirs, files in os.walk(root): archive_root = os.path.abspath(root)[root_len:] for name in files: path = os.path.join(root, name) archive_name = os.path.join(archive_root, name) zip_file.write(path, archive_name) zip_file.close() data = xmlrpclib.Binary(zip_data.getvalue()) zip_data.close() log.debug("Uploading analyzer to guest (id=%s, ip=%s)", self.id, self.ip) # Send the zip containing the analyzer to the agent running inside # the guest. try: self.server.add_analyzer(data) except socket.timeout: raise CuckooGuestError( "{0}: guest communication timeout: unable " "to upload agent, check networking or try " "to increase timeout".format(self.id) ) def start_analysis(self, options): """Start analysis. @param options: options. @return: operation status. """ log.info("Starting analysis on guest (id=%s, ip=%s)", self.id, self.ip) # TODO: deal with unicode URLs. if options["category"] == "file": options["file_name"] = sanitize_filename(options["file_name"]) try: # Wait for the agent to respond. This is done to check the # availability of the agent and verify that it's ready to receive # data. self.wait(CUCKOO_GUEST_INIT) # Invoke the upload of the analyzer to the guest. self.upload_analyzer() # Give the analysis options to the guest, so it can generate the # analysis.conf inside the guest. try: self.server.add_config(options) except: raise CuckooGuestError("{0}: unable to upload config to " "analysis machine".format(self.id)) # If the target of the analysis is a file, upload it to the guest. if options["category"] == "file": try: file_data = open(options["target"], "rb").read() except (IOError, OSError) as e: raise CuckooGuestError("Unable to read {0}, error: " "{1}".format(options["target"], e)) data = xmlrpclib.Binary(file_data) try: self.server.add_malware(data, options["file_name"]) except MemoryError as e: raise CuckooGuestError( "{0}: unable to upload malware to " "analysis machine, not enough " "memory".format(self.id) ) pid = self.server.execute() log.debug("%s: analyzer started with PID %d", self.id, pid) # If something goes wrong when establishing the connection, raise an # exception and abort the analysis. except (socket.timeout, socket.error): raise CuckooGuestError( "{0}: guest communication timeout, check " "networking or try to increase " "timeout".format(self.id) ) def take_mem_dump(self, dumps_dir, machine, machinery, json_obj): """ Takes memory dump and dumps json info file. """ listdir = sorted(os.listdir(dumps_dir), key=int, reverse=True) if listdir == []: i = 1 else: i = int(listdir[0]) + 1 dump_dir = os.path.join(dumps_dir, str(i)) os.mkdir(dump_dir) machinery.dump_memory(machine.label, os.path.join(dump_dir, "memory.dmp")) json.dump(json_obj, file(os.path.join(dump_dir, "info.json"), "wb"), sort_keys=False, indent=4) def wait_for_completion(self, machine, storage, machinery): """Wait for analysis completion. @return: operation status. """ log.debug("%s: waiting for completion", self.id) # Same procedure as in self.wait(). Just look at the comments there. abort = Event() abort.clear() def die(): abort.set() # CHANGED: Added time-based dumps here. resumableTimer = ResumableTimer(self.timeout, die) resumableTimer.start() sec_counter = 0 mem_analysis_conf = Config(os.path.join(CUCKOO_ROOT, "conf", "memoryanalysis.conf")) time_to_sleep = int(mem_analysis_conf.time_based.time_to_sleep_before_dump_in_seconds) number_of_dumps = int(mem_analysis_conf.basic.max_number_of_dumps) memory_results_dir = os.path.join(storage, "memory") dumps_dir = os.path.join(memory_results_dir, "dumps") create_dir_safe(memory_results_dir) create_dir_safe(dumps_dir) while True: if abort.is_set(): info_dict = {"trigger": {"name": "End", "args": {}}, "time": str(sec_counter)} log.info("Taking dump before termination...") self.take_mem_dump(dumps_dir, machine, machinery, info_dict) raise CuckooGuestError("The analysis hit the critical timeout, terminating") while Event(STOP_EVENT).is_set(): time.sleep(0.005) time.sleep(1) sec_counter += 1 if mem_analysis_conf.basic.time_based and sec_counter % time_to_sleep == 0: resumableTimer.stop() info_dict = {"trigger": {"name": "Time", "args": {"interval": time_to_sleep}}} self.take_mem_dump(dumps_dir, machine, machinery, info_dict) resumableTimer.resume() try: status = self.server.get_status() except Exception as e: log.debug("%s: error retrieving status: %s", self.id, e) continue # React according to the returned status. if status == CUCKOO_GUEST_COMPLETED: log.info("%s: analysis completed successfully", self.id) break elif status == CUCKOO_GUEST_FAILED: error = self.server.get_error() if not error: error = "unknown error" info_dict = {"trigger": {"name": "End", "args": {}}} log.info("Taking dump before termination...") self.take_mem_dump(dumps_dir, machine, machinery, info_dict) raise CuckooGuestError("Analysis failed: {0}".format(error)) # TODO: suspend machine and take dump else: log.debug("%s: analysis not completed yet (status=%s)", self.id, status) self.server._set_timeout(None) log.info("Taking dump before termination...") info_dict = {"trigger": {"name": "End", "args": {}}} self.take_mem_dump(dumps_dir, machine, machinery, info_dict)
class GuestManager: """Guest Manager. This class handles the communications with the agents running in the machines. """ def __init__(self, vm_id, ip, platform="windows"): """@param ip: guest's IP address. @param platform: guest's operating system type. """ self.id = vm_id self.ip = ip self.platform = platform self.cfg = Config() # initialized in start_analysis so we can update the critical timeout # TODO, pull options parameter into __init__ so we can do this here self.timeout = None self.server = None def wait(self, status): """Waiting for status. @param status: status. @return: always True. """ log.debug("%s: waiting for status 0x%.04x", self.id, status) end = time.time() + self.timeout self.server._set_timeout(self.timeout) while True: # Check if we've passed the timeout. if time.time() > end: raise CuckooGuestError("{0}: the guest initialization hit the " "critical timeout, analysis " "aborted.".format(self.id)) try: # If the server returns the given status, break the loop # and return. if self.server.get_status() == status: log.debug("%s: status ready", self.id) break except: pass log.debug("%s: not ready yet", self.id) time.sleep(1) self.server._set_timeout(None) return True def upload_analyzer(self): """Upload analyzer to guest. @return: operation status. """ zip_data = StringIO() zip_file = ZipFile(zip_data, "w", ZIP_STORED) # Select the proper analyzer's folder according to the operating # system associated with the current machine. root = os.path.join(CUCKOO_ROOT, "analyzer", self.platform) root_len = len(os.path.abspath(root)) if not os.path.exists(root): log.error("No valid analyzer found at path: %s", root) return False # Walk through everything inside the analyzer's folder and write # them to the zip archive. for root, dirs, files in os.walk(root): archive_root = os.path.abspath(root)[root_len:] for name in files: path = os.path.join(root, name) archive_name = os.path.join(archive_root, name) zip_file.write(path, archive_name) zip_file.close() data = xmlrpclib.Binary(zip_data.getvalue()) zip_data.close() log.debug("Uploading analyzer to guest (id=%s, ip=%s)", self.id, self.ip) # Send the zip containing the analyzer to the agent running inside # the guest. try: self.server.add_analyzer(data) except socket.timeout: raise CuckooGuestError("{0}: guest communication timeout: unable " "to upload agent, check networking or try " "to increase timeout".format(self.id)) def upload_support_files(self, options): """ Upload supporting files from zip temp directory if they exist :param options: options :return: """ log.info("Uploading support files to guest (id=%s, ip=%s)", self.id, self.ip) basedir = os.path.dirname(options["target"]) for dirpath, _, files in os.walk(basedir): for xf in files: target = os.path.join(dirpath, xf) # Copy all files except for the original target if not target == options["target"]: try: file_data = open(target, "rb").read() except (IOError, OSError) as e: raise CuckooGuestError( "Unable to read {}, error: {}".format(target, e)) data = xmlrpclib.Binary(file_data) try: self.server.add_malware(data, xf) except Exception as e: raise CuckooGuestError( "{}: unable to upload support file to " "analysis machine: {}".format(self.id, e)) return def start_analysis(self, options): """Start analysis. @param options: options. @return: operation status. """ log.info("Starting analysis on guest (id=%s, ip=%s)", self.id, self.ip) # TODO: deal with unicode URLs. if options["category"] == "file": options["file_name"] = "'" + sanitize_filename( options["file_name"]) + "'" self.timeout = options["timeout"] + self.cfg.timeouts.critical # Get and set dynamically generated resultserver port. options["port"] = str(ResultServer().port) url = "http://{0}:{1}".format(self.ip, CUCKOO_GUEST_PORT) self.server = TimeoutServer(url, allow_none=True, timeout=self.timeout) try: # Wait for the agent to respond. This is done to check the # availability of the agent and verify that it's ready to receive # data. self.wait(CUCKOO_GUEST_INIT) # Invoke the upload of the analyzer to the guest. self.upload_analyzer() # Give the analysis options to the guest, so it can generate the # analysis.conf inside the guest. try: self.server.add_config(options) except: raise CuckooGuestError("{0}: unable to upload config to " "analysis machine".format(self.id)) # If the target of the analysis is a file, upload it to the guest. if options["category"] == "file": try: file_data = open(options["target"], "rb").read() except (IOError, OSError) as e: raise CuckooGuestError("Unable to read {0}, error: " "{1}".format(options["target"], e)) data = xmlrpclib.Binary(file_data) try: # strip off the added surrounding quotes self.server.add_malware(data, options["file_name"][1:-1]) except Exception as e: raise CuckooGuestError("{0}: unable to upload malware to " "analysis machine: {1}".format( self.id, e)) # check for support files and upload them to guest. self.upload_support_files(options) # Debug analyzer.py in vm if "CUCKOO_DBG" in os.environ: while True: pass # Launch the analyzer. pid = self.server.execute() log.debug("%s: analyzer started with PID %d", self.id, pid) # If something goes wrong when establishing the connection, raise an # exception and abort the analysis. except (socket.timeout, socket.error): raise CuckooGuestError("{0}: guest communication timeout, check " "networking or try to increase " "timeout".format(self.id)) def wait_for_completion(self): """Wait for analysis completion. @return: operation status. """ log.debug("%s: waiting for completion", self.id) end = time.time() + self.timeout self.server._set_timeout(self.timeout) while True: time.sleep(0.2) # If the analysis hits the critical timeout, just return straight # away and try to recover the analysis results from the guest. if time.time() > end: raise CuckooGuestError( "The analysis hit the critical timeout, terminating.") try: status = self.server.get_status() except Exception as e: log.debug("%s: error retrieving status: %s", self.id, e) continue # React according to the returned status. if status == CUCKOO_GUEST_COMPLETED: log.info("%s: analysis completed successfully", self.id) break elif status == CUCKOO_GUEST_FAILED: error = self.server.get_error() if not error: error = "unknown error" raise CuckooGuestError("Analysis failed: {0}".format(error)) elif status == CUCKOO_GUEST_INIT: # means the system must have bluescreened or restarted and now we're getting the initial agent.py request again raise CuckooGuestError( "Analysis failed: system restarted unexpectedly") else: log.debug("%s: analysis not completed yet (status=%s)", self.id, status) self.server._set_timeout(None)