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)
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)
def run_memory_analysis(self): """ The main engine function for memory analysis """ # We copy the results because we add results that the user did not choose to run, # but will run as a dependency. log.info("Running memory analysis for dump: %s" % self.memfile) new_results = copy.deepcopy(self.results) mem_analysis = {} mem_analysis["diffs"] = {} dlls_dir = os.path.join(os.path.dirname(self.memfile), DLLS_DIR) drivers_dir = os.path.join(os.path.dirname(self.memfile), DRIVERS_DIR) malfinds_dir = os.path.join(os.path.dirname(self.memfile), MALFINDS_DIR) if not self.is_clean: utils.create_dir_safe(dlls_dir) utils.create_dir_safe(drivers_dir) utils.create_dir_safe(malfinds_dir) for mem_analysis_name, dependencies in MEMORY_ANALYSIS_DEPENDENCIES.iteritems(): attrs = getattr(self.voptions, mem_analysis_name) if attrs.enabled: attrs.pop("enabled") if attrs.has_key("filter"): attrs.pop("filter") if self.is_clean: self.results = self.run_dependencies(self.results, dependencies, **attrs) else: new_results = self.run_dependencies(new_results, dependencies, **attrs) if mem_analysis_name == "static_analyze_new_exe": mem_analysis[mem_analysis_name] = self.static_analyze_new_exe(self.clean_data, new_results, dependencies, **attrs) elif mem_analysis_name == "diff_heap_entropy": mem_analysis["diffs"]["diff_heap_entropy"] = {"desc" : "Calculates entropy of malware heap process", "new" : [], "deleted" : [], "star": "no"} proc = self.get_malware_proc(self.clean_data,new_results,dependencies) if proc is not None: pid = proc["process_id"] try: mem_analysis["diffs"]["diff_heap_entropy"]["value"] = self.vol.heapentropy(pid=str(pid))["data"][0]["entropy"] except: pass else: self.run_memory_plugin(mem_analysis, mem_analysis_name, self.clean_data, new_results, **attrs) if not self.is_clean: trigger = self.info["trigger"]["name"] args = self.info["trigger"]["args"] smart_analysis, plugins_to_run = self.parse_trigger_plugins(trigger) for plugin in plugins_to_run: if not mem_analysis["diffs"].has_key(plugin): self.run_memory_plugin(mem_analysis, plugin, self.clean_data, new_results) if smart_analysis: if TRIGGER_PLUGINS.has_key(trigger): for plugin in TRIGGER_PLUGINS[trigger]: if not mem_analysis["diffs"].has_key(plugin): self.run_memory_plugin(mem_analysis, plugin, self.clean_data, new_results) mem_analysis["diffs"][plugin]["star"] = "yes" if trigger == "ZwLoadDriver": patcher_res = self.vol.patcher(os.path.join(CUCKOO_ROOT, "data", "patchers", "patchpe.xml")) # Dump the new driver for p in patcher_res["patches"]: log.info("Dumping drive in offset: %d" % p) base = int(p) + 0x7fe00000 res = self.vol.moddump(dump_dir=drivers_dir, base=base) name = res["data"][0]["module_name"] mem_analysis["diffs"]["diff_moddump"]["new"].append({"module_name" : name,"module_base" : base}) elif trigger == "SetWindowsHookExA" or trigger == "SetWindowsHookExW": for mh in mem_analysis["diffs"]["diff_messagehooks"]["new"]: pid_pat = "\(.*?(\d+)\)" pids = re.findall(pid_pat, mh["thread"]) if len(pids) > 0: pid = pids[0] mod_name = mh["module"].split("\\")[-1] dlldump = self.vol.dlldump(dump_dir=dlls_dir, pid=pid, regex=mod_name) mem_analysis["diffs"]["injected_dll"] = { "new" : [], "deleted" : [], "star" : "yes", "desc" : "Shows the injected DLL", } if dlldump["data"] != []: mem_analysis["diffs"]["injected_dll"]["new"].append(dlldump["data"][0]) elif trigger == "VirtualProtectEx": verbal_protect = PAGE_PROTECTIONS[int(args["Protection"],16)] protect_vad_val = [key for key,val in vadinfo.PROTECT_FLAGS.iteritems() if val == verbal_protect][0] if "EXECUTE" in verbal_protect: key = "new" else: key = "deleted" mem_analysis["diffs"]["diff_malfind"][key].append( \ self.vol.malfind(dump_dir=malfinds_dir, pid=str(args["ProcessId"]), add_data=False, ignore_protect=True, address=int(args["Address"],16))["data"][0]) mem_analysis["diffs"]["diff_malfind"]["star"] = "yes" elif trigger == "WriteProcessMemory -> CreateRemoteThread -> LoadLibrary": resdir = os.path.join(os.path.dirname(self.memfile), "dlls") utils.create_dir_safe(resdir) pid = str(args["ProcessId"]) base = int(args["BaseAddress"], 16) new_dlldump = self.vol.dlldump(dump_dir=resdir, pid=pid, base=base) mem_analysis["diffs"]["injected_dll"] = \ { "new" : new_dlldump["data"], "deleted" : [], "star" : "yes", "desc" : "Shows the injected DLL", } elif trigger == "NtSetContextThread -> NtResumeThread": pid = str(args["ProcessId"]) tid = int(args["ThreadId"]) self.get_injected_thread(pid, tid, mem_analysis) elif trigger == "WriteProcessMemory -> CreateRemoteThread": start = int(args["StartRoutine"], 16) pid = str(args["ProcessId"]) tid = int(args["ThreadId"]) dis = None try: dis = disasm_from_memory(self.memfile, int(pid), start, 100) except: pass self.get_injected_thread(pid, tid, mem_analysis, dis) return mem_analysis, new_results