def run(self): """Run analysis. @return: operation status. """ start = KERNEL32.GetTickCount() self.prepare() self.path = os.getcwd() log.debug("Starting analyzer from: %s", self.path) log.debug("Pipe server name: %s", self.config.pipe) log.debug("Log pipe server name: %s", self.config.logpipe) # If no analysis package was specified at submission, we try to select # one automatically. if not self.config.package: log.debug("No analysis package specified, trying to detect " "it automagically.") # If the analysis target is a file, we choose the package according # to the file format. if self.config.category == "file": package = choose_package(self.config.file_type, self.config.file_name, self.config.pe_exports.split(",")) # If it's an URL, we'll just use the default Internet Explorer # package. else: package = "ie" # If we weren't able to automatically determine the proper package, # we need to abort the analysis. if not package: raise CuckooError("No valid package available for file " "type: {0}".format(self.config.file_type)) log.info("Automatically selected analysis package \"%s\"", package) # Otherwise just select the specified package. else: package = self.config.package # Generate the package path. package_name = "modules.packages.%s" % package # Try to import the analysis package. try: package_module = __import__(package_name, globals(), locals(), ["dummy"], -1) # If it fails, we need to abort the analysis. except ImportError: raise CuckooError("Unable to import package \"{0}\", does " "not exist.".format(package_name)) # Initialize the package parent abstract. Package() # Find the package class, the file name does not always equal the class name (eg doc.py -> Class _DOC_) class_name = next((attr for attr in dir(package_module) if attr.lower() == package.lower()), None) if not class_name: raise CuckooError("Unable to select package class " "(package={0})".format(package_name)) package_class = getattr(package_module, class_name) # Initialize the analysis package. log.debug("arguments options: [%s]", str(self.config.options)) self.package = package_class(self.config.options, analyzer=self) # Move the sample to the current working directory as provided by the # task - one is able to override the starting path of the sample. # E.g., for some samples it might be useful to run from %APPDATA% # instead of %TEMP%. if self.config.category == "file": self.target = self.package.move_curdir(self.target) # Initialize Auxiliary modules aux_start = KERNEL32.GetTickCount() Auxiliary() prefix = auxiliary.__name__ + "." for loader, name, ispkg in pkgutil.iter_modules( auxiliary.__path__, prefix): if ispkg: continue # Import the auxiliary module. try: __import__(name, globals(), locals(), ["dummy"], -1) except ImportError as e: log.warning( "Unable to import the auxiliary module " "\"%s\": %s", name, e) # Walk through the available auxiliary modules. aux_enabled, aux_avail = [], [] for module in Auxiliary.__subclasses__(): # Try to start the auxiliary module. try: self.config.options[ 'timeout'] = self.config.timeout # pass timeout to aux modules aux = module(options=self.config.options, analyzer=self) aux_avail.append(aux) aux.init() aux.start() except (NotImplementedError, AttributeError): log.exception("Auxiliary module %s was not implemented", module.__name__) except CuckooDisableModule: continue except Exception as e: log.exception("Cannot execute auxiliary module %s: %s", module.__name__, e) else: log.debug("Started auxiliary module %s", module.__name__) aux_enabled.append(aux) aux_end = KERNEL32.GetTickCount() log.debug("Loaded auxiliary modules in {}s".format( str((aux_end - aux_start) / 1000))) # Forward the command pipe and logpipe names on to zer0m0n. zer0m0n.cmdpipe(self.config.pipe) zer0m0n.channel(self.config.logpipe) # Initialize zer0m0n with our compiled Yara rules. zer0m0n.yarald("bin/rules.yarac") # Start analysis package. If for any reason, the execution of the # analysis package fails, we have to abort the analysis. process_monitoring_start = KERNEL32.GetTickCount() pids = self.package.start(self.target) process_monitoring_end = KERNEL32.GetTickCount() log.debug("Monitored first process in {}s".format( str((process_monitoring_end - process_monitoring_start) / 1000))) # If the analysis package returned a list of process identifiers, we # add them to the list of monitored processes and enable the process monitor. if pids: self.process_list.add_pids(pids) pid_check = True # If the package didn't return any process ID (for example in the case # where the package isn't enabling any behavioral analysis), we don't # enable the process monitor. else: log.info("No process IDs returned by the package, running " "for the full timeout.") pid_check = False # Check in the options if the user toggled the timeout enforce. If so, # we need to override pid_check and disable process monitor. if self.config.enforce_timeout: log.info("Enabled timeout enforce, running for the full timeout.") pid_check = False end = KERNEL32.GetTickCount() log.info("Initialized VM in {}s".format(str((end - start) / 1000))) end = KERNEL32.GetTickCount() + int(self.config.timeout) * 1000 while self.do_run: now = KERNEL32.GetTickCount() # log.debug("Time passed: {}, terminating at {}".format((end-now)/1000, str(self.config.timeout))) if now >= end: log.info("Analysis timeout hit, terminating analysis.") break # If the process lock is locked, it means that something is # operating on the list of monitored processes. Therefore we # cannot proceed with the checks until the lock is released. if self.process_lock.locked(): KERNEL32.Sleep(1000) continue try: # If the process monitor is enabled we start checking whether # the monitored processes are still alive. if pid_check: for pid in self.process_list.pids: if not Process(pid=pid).is_alive(): log.info("Process with pid %s has terminated", pid) self.process_list.remove_pid(pid) # If none of the monitored processes are still alive, we # can terminate the analysis. if not self.process_list.pids: log.info("Process list is empty, " "terminating analysis.") break # Update the list of monitored processes available to the # analysis package. It could be used for internal # operations within the module. self.package.set_pids(self.process_list.pids) try: # The analysis packages are provided with a function that # is executed at every loop's iteration. If such function # returns False, it means that it requested the analysis # to be terminate. if not self.package.check(): log.info("The analysis package requested the " "termination of the analysis.") break # If the check() function of the package raised some exception # we don't care, we can still proceed with the analysis but we # throw a warning. except Exception as e: log.warning( "The package \"%s\" check function raised " "an exception: %s", package_name, e) finally: # Zzz. KERNEL32.Sleep(1000) if not self.do_run: log.debug("The analyzer has been stopped on request by an " "auxiliary module.") # Create the shutdown mutex. KERNEL32.CreateMutexA(None, False, SHUTDOWN_MUTEX) try: # Before shutting down the analysis, the package can perform some # final operations through the finish() function. self.package.finish() except Exception as e: log.warning( "The package \"%s\" finish function raised an " "exception: %s", package_name, e) try: # Upload files the package created to package_files in the # results folder. for path, name in self.package.package_files() or []: upload_to_host(path, os.path.join("package_files", name)) except Exception as e: log.warning( "The package \"%s\" package_files function raised an " "exception: %s", package_name, e) # Terminate the Auxiliary modules. for aux in aux_enabled: try: aux.stop() except (NotImplementedError, AttributeError): continue except Exception as e: log.warning("Cannot terminate auxiliary module %s: %s", aux.__class__.__name__, e) if self.config.terminate_processes: # Try to terminate remaining active processes. log.info("Terminating remaining processes before shutdown.") for pid in self.process_list.pids: proc = Process(pid=pid) if proc.is_alive(): try: proc.terminate() except: continue # Run the finish callback of every available Auxiliary module. for aux in aux_avail: try: aux.finish() except (NotImplementedError, AttributeError): continue except Exception as e: log.warning( "Exception running finish callback of auxiliary " "module %s: %s", aux.__class__.__name__, e) # Let's invoke the completion procedure. self.complete() return True
def run(self): # human starts before the sample invocation, wait for 3s to start minimal_timeout = KERNEL32.GetTickCount() + 3000 # set office close timeout after 2/3 of analysis (in milliseconds) office_close_sec = int(self.options.get("timeout") * (3. / 4) * 1000) office_close_timeout = KERNEL32.GetTickCount() + office_close_sec is_office_close = False is_full_screen = False pdf_clicks_ctr = 10 # adaptive sleep timer sleep = 50 if self.is_ultrafast else 750 while self.do_run: KERNEL32.Sleep( sleep) # we wait for minimal timeout anyway so no loss here if KERNEL32.GetTickCount() < minimal_timeout: continue if not is_office_close and KERNEL32.GetTickCount( ) > office_close_timeout: USER32.EnumWindows(EnumWindowsProc(get_office_window), 0) is_office_close = True if self.do_click_mouse and self.do_move_mouse: # extract foregroud window name fg_window_name = "" hwnd = USER32.GetForegroundWindow() try: fg_window_name = get_window_text(hwnd).lower() except: log.exception("failed to extract window name") pass # make the office window on front if fg_window_name in ["", "program manager"]: x, y = self.coordinates.center() move_mouse(x, y) click_mouse(x, y) continue else: log.info("fg_window_name: %s", fg_window_name) if "word" in fg_window_name or "excel" in fg_window_name: if not is_full_screen: set_full_screen(hwnd) is_full_screen = True x, y = self.coordinates.next() move_mouse(x, y) double_click(x, y) elif "powerpoint" in fg_window_name: if not is_full_screen: set_full_screen(hwnd) is_full_screen = True x, y = self.coordinates.center() move_mouse(x, y) click_mouse(x, y) elif "acrobat reader" in fg_window_name: if not is_full_screen: set_full_screen(hwnd) is_full_screen = True # place cursor on top left x, y = 120, 200 move_mouse(x, y) click_mouse(x, y) if pdf_clicks_ctr > 0: # press tab click_button(win32con.VK_TAB) # press enter click_button(win32con.VK_RETURN) pdf_clicks_ctr = pdf_clicks_ctr - 1 # wait for result KERNEL32.Sleep(1000) else: # make random move x, y = self.coordinates.random() move_mouse(x, y) if self.do_click_buttons: USER32.EnumWindows(EnumWindowsProc(foreach_window), 0)