def start(self, path): control = self.get_path() if not control: raise CuckooPackageError("Unable to find any control.exe " "executable available") dll = self.options.get("dll", None) free = self.options.get("free", False) gw = self.options.get("setgw",None) u = Utils() if gw: u.set_default_gw(gw) suspended = True if free: suspended = False p = Process() if not p.execute(path=control, args="\"%s\"" % path, suspended=suspended): raise CuckooPackageError("Unable to execute initial Control " "process, analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def start(self, path): p = Process() dll = self.options.get("dll") p.execute(path="bin/execsc.exe", args=path, suspended=True) p.inject(dll) p.resume() return p.pid
def _inject_process(self, process_id, thread_id, mode): """Helper function for injecting the monitor into a process.""" # We acquire the process lock in order to prevent the analyzer to # terminate the analysis while we are operating on the new process. self.analyzer.process_lock.acquire() # Set the current DLL to the default one provided at submission. dll = self.analyzer.default_dll if process_id in (self.analyzer.pid, self.analyzer.ppid): if process_id not in self.ignore_list["pid"]: log.warning("Received request to inject Cuckoo processes, " "skipping it.") self.ignore_list["pid"].append(process_id) self.analyzer.process_lock.release() return # We inject the process only if it's not being monitored already, # otherwise we would generated polluted logs (if it wouldn't crash # horribly to start with). if self.analyzer.process_list.has_pid(process_id): # This pid is already on the notrack list, move it to the # list of tracked pids. if not self.analyzer.process_list.has_pid(process_id, notrack=False): log.debug( "Received request to inject pid=%d. It was already " "on our notrack list, moving it to the track list." ) self.analyzer.process_list.remove_pid(process_id) self.analyzer.process_list.add_pid(process_id) self.ignore_list["pid"].append(process_id) # Spit out an error once and just ignore it further on. elif process_id not in self.ignore_list["pid"]: log.debug("Received request to inject pid=%d, but we are " "already injected there.", process_id) self.ignore_list["pid"].append(process_id) # We're done operating on the processes list, release the lock. self.analyzer.process_lock.release() return # Open the process and inject the DLL. Hope it enjoys it. proc = Process(pid=process_id, tid=thread_id) filename = os.path.basename(proc.get_filepath()) if not self.analyzer.files.is_protected_filename(filename): # Add the new process ID to the list of monitored processes. self.analyzer.process_list.add_pid(process_id) # We're done operating on the processes list, # release the lock. Let the injection do its thing. self.analyzer.process_lock.release() # If we have both pid and tid, then we can use APC to inject. if process_id and thread_id: proc.inject(dll, apc=True, mode="%s" % mode) else: proc.inject(dll, apc=False, mode="%s" % mode) log.info("Injected into process with pid %s and name %r", proc.pid, filename)
def execute(self, path, args, interest): """Starts an executable for analysis. @param path: executable path @param args: executable arguments @param interest: file of interest, passed to the cuckoomon config @return: process pid """ dll = self.options.get("dll") free = self.options.get("free") suspended = True if free: suspended = False kernel_analysis = self.options.get("kernel_analysis", False) if kernel_analysis != False: kernel_analysis = True p = Process() if not p.execute(path=path, args=args, suspended=suspended, kernel_analysis=kernel_analysis): raise CuckooPackageError("Unable to execute the initial process, " "analysis aborted.") if free: return None if not kernel_analysis: p.inject(dll, interest) p.resume() p.close() return p.pid
def start(self, path): free = self.options.get("free", False) function = self.options.get("function", "DllMain") arguments = self.options.get("arguments", None) suspended = True if free: suspended = False if not path.endswith('.cpl'): args = "{0},{1}".format(path, function) if arguments: args += " {0}".format(arguments) exe_path = "C:\\WINDOWS\\system32\\rundll32.exe" else: args = "{0}".format(path) if arguments: args += " {0}".format(arguments) exe_path = "C:\\WINDOWS\\system32\\control.exe" log.info("starting DLL with: %s" % (args)) p = Process() #if not p.execute(path="C:\\WINDOWS\\system32\\rundll32.exe", args=args, suspended=suspended): if not p.execute(path=exe_path, args=args, suspended=suspended): raise CuckooPackageError("Unable to execute rundll32, analysis aborted") if not free and suspended: p.inject() p.resume() return p.pid else: return None
def start(self, path): free = self.options.get("free", False) args = self.options.get("arguments", None) dll = self.options.get("dll", None) gw = self.options.get("setgw",None) u = Utils() if gw: u.set_default_gw(gw) suspended = True if free: suspended = False p = Process() if not p.execute(path=path, args=args, suspended=suspended): raise CuckooPackageError("Unable to execute initial process, " "analysis aborted") if not free and suspended: p.inject(dll) p.resume() p.close() return p.pid else: return None
def start(self, url): free = self.options.get("free", False) dll = self.options.get("dll", None) gw = self.options.get("setgw",None) u = Utils() if gw: u.set_default_gw(gw) suspended = True if free: suspended = False iexplore = os.path.join(os.getenv("ProgramFiles"), "Internet Explorer", "iexplore.exe") p = Process() if not p.execute(path=iexplore, args="\"%s\"" % url, suspended=suspended): raise CuckooPackageError("Unable to execute initial Internet " "Explorer process, analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def start(self, path): excel = self.get_path() if not excel: raise CuckooPackageError("Unable to find any Microsoft " "Office Excel executable available") dll = self.options.get("dll", None) free = self.options.get("free", False) gw = self.options.get("setgw", None) u = Utils() if gw: u.set_default_gw(gw) suspended = True if free: suspended = False p = Process() if not p.execute(path=excel, args='"%s"' % path, suspended=suspended): raise CuckooPackageError("Unable to execute initial Microsoft " "Office Excel process, analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def start(self, path): java = self.get_path() if not java: raise CuckooPackageError("Unable to find any Java executable available") free = self.options.get("free", False) class_path = self.options.get("class", None) suspended = True if free: suspended = False if class_path: args = '-cp "%s" %s' % (path, class_path) else: args = '-jar "%s"' % path p = Process() if not p.execute(path=java, args=args, suspended=suspended): raise CuckooPackageError("Unable to execute initial Java process, analysis aborted") if not free and suspended: p.inject() p.resume() return p.pid else: return None
def start(self, path): free = self.options.get("free", False) function = self.options.get("function", "DllMain") arguments = self.options.get("arguments", None) dll = self.options.get("dll", None) gw = self.options.get("setgw", None) u = Utils() if gw: u.set_default_gw(gw) suspended = True if free: suspended = False args = "{0},{1}".format(path, function) if arguments: args += " {0}".format(arguments) p = Process() if not p.execute(path="C:\\WINDOWS\\system32\\rundll32.exe", args=args, suspended=suspended): raise CuckooPackageError("Unable to execute rundll32, " "analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def start(self, path): java = self.get_path() if not java: raise CuckooPackageError("Unable to find any Java " "executable available") dll = self.options.get("dll", None) free = self.options.get("free", False) class_path = self.options.get("class", None) gw = self.options.get("setgw",None) u = Utils() if gw: u.set_default_gw(gw) suspended = True if free: suspended = False if class_path: args = "-cp \"%s\" %s" % (path, class_path) else: args = "-jar \"%s\"" % path p = Process() if not p.execute(path=java, args=args, suspended=suspended): raise CuckooPackageError("Unable to execute initial Java " "process, analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def start(self, path): free = self.options.get("free", False) dll = self.options.get("dll", None) gw = self.options.get("setgw",None) u = Utils() if gw: u.set_default_gw(gw) suspended = True if free: suspended = False cmd_path = os.path.join(os.getenv("SystemRoot"), "system32", "cmd.exe") cmd_args = "/c start \"{0}\"".format(path) p = Process() if not p.execute(path=cmd_path, args=cmd_args, suspended=suspended): raise CuckooPackageError("Unable to execute initial process, " "analysis aborted") if not free and suspended: p.inject(dll) p.resume() p.close() return p.pid else: return None
def start(self, path): wscript = self.get_path() if not wscript: raise CuckooPackageError("Unable to find any WScript " "executable available") dll = self.options.get("dll", None) free = self.options.get("free", False) gw = self.options.get("setgw",None) u = Utils() if gw: u.set_default_gw(gw) suspended = True if free: suspended = False p = Process() if not p.execute(path=wscript, args="\"{0}\"".format(path), suspended=suspended): raise CuckooPackageError("Unable to execute initial WScript " "process, analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def start(self, path): powershell = self.get_path() if not powershell: raise CuckooPackageError("Unable to find any PowerShell executable available") dll = self.options.get("dll", None) free = self.options.get("free", False) gw = self.options.get("setgw",None) u = Utils() if gw: u.set_default_gw(gw) suspended = True if free: suspended = False args = "-NoProfile -ExecutionPolicy unrestricted -File \"{0}\"".format(path) p = Process() if not p.execute(path=powershell, args=args, suspended=suspended): raise CuckooPackageError("Unable to execute initial PowerShell process, analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def start(self, path): browser = self.get_path() if not browser: raise CuckooPackageError("Unable to find any browser " "executable available") dll = self.options.get("dll", None) free = self.options.get("free", False) class_name = self.options.get("class", None) suspended = True if free: suspended = False html_path = self.make_html(path, class_name) p = Process() if not p.execute(path=browser, args="\"%s\"" % html_path, suspended=suspended): raise CuckooPackageError("Unable to execute initial Internet " "Explorer process, analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def start(self, path): root = os.environ["TEMP"] with ZipFile(path, "r") as archive: try: archive.extractall(root) except BadZipfile as e: raise CuckooPackageError("Invalid Zip file") except RuntimeError: try: archive.extractall(path=root, pwd="infected") except RuntimeError as e: raise CuckooPackageError("Unable to extract Zip file, unknown password?") file_path = os.path.join(root, self.options.get("file", "sample.exe")) free = self.options.get("free", False) args = self.options.get("arguments", None) suspended = True if free: suspended = False p = Process() if not p.execute(path=file_path, args=args, suspended=suspended): raise CuckooPackageError("Unable to execute initial process, analysis aborted") if not free and suspended: p.inject() p.resume() return p.pid else: return None
def start(self, path): free = self.options.get("free", False) dll = self.options.get("dll", None) suspended = True if free: suspended = False if os.getenv("ProgramFiles(x86)"): iex86 = os.path.join(os.getenv("ProgramFiles(x86)"), "Internet Explorer", "iexplore.exe") else: iex86 = os.path.join(os.getenv("ProgramFiles"), "Internet Explorer", "iexplore.exe") ie32 = os.path.join(os.getenv("ProgramFiles"), "Internet Explorer", "iexplore.exe") if os.path.exists(iex86): iexplore = iex86 else: iexplore = ie32 p = Process() if not p.execute(path=iexplore, args="\"%s\"" % path, suspended=suspended): raise CuckooPackageError("Unable to execute initial Internet " "Explorer process, analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def start(self, path): p = Process() p.execute(path="bin/execsc.exe", args=path, suspended=True) p.inject() p.resume() return p.pid
def start(self, path): arg = "\"%s\"" % path p = Process() p.execute(path="C:\\Program Files\\Microsoft Office\\Office12\\WINWORD.EXE", args=arg, suspended=True) p.inject() p.resume() return p.pid
def start(self, path): arg = "\"%s\"" % path p = Process() p.execute(path="C:\\Program Files\\Internet Explorer\\iexplore.exe", args=arg, suspended=True) p.inject() p.resume() return p.pid
def start(self, path): arg = "\"%s\"" % path p = Process() p.execute(path="C:\\Program Files\\Adobe\\Reader 9.0\\Reader\\AcroRd32.exe", args=arg, suspended=True) p.inject() p.resume() return p.pid
def start(self, path): arg = "\"%s\"" % path p = Process() p.execute(path="C:\\WINDOWS\\system32\\cmd.exe", args=arg, suspended=True) p.inject() p.resume() return p.pid
def start(self, path): p = Process() execsc = "extra/execsc.exe" p.execute(path=execsc, args=path, suspended=True) p.inject() p.resume() return p.pid
def start(self, path): arg = "\"%s\"" % path p = Process() # p.execute(path="C:\\Program Files\\Internet Explorer\\iexplore.exe", args=arg, suspended=True) url = self.options["url"] url = url + "=" * (-len(url)%4) url = base64.b64decode(url) p.execute(path="C:\\Program Files\\Internet Explorer\\iexplore.exe", args=url, suspended=True) p.inject() p.resume() return p.pid
def run(self): """Run handler. @return: operation status. """ data = "" while True: bytes_read = c_int(0) buf = create_string_buffer(BUFSIZE) success = KERNEL32.ReadFile(self.h_pipe, buf, sizeof(buf), byref(bytes_read), None) data += buf.value if not success and KERNEL32.GetLastError() == ERROR_MORE_DATA: continue #elif not success or bytes_read.value == 0: # if KERNEL32.GetLastError() == ERROR_BROKEN_PIPE: # pass break if data: command = data.strip() #log.debug("Connection received (data=%s)" % command) if command.startswith("PID:"): PROCESS_LOCK.acquire() pid = command[4:] if pid.isdigit(): pid = int(pid) if pid not in PROCESS_LIST: add_pids(pid) proc = Process(pid=pid) proc.inject() KERNEL32.WriteFile(self.h_pipe, create_string_buffer("OK"), 2, byref(bytes_read), None) PROCESS_LOCK.release() elif command.startswith("FILE:"): file_path = command[5:] add_file(file_path) KERNEL32.CloseHandle(self.h_pipe) return True
def start(self, path): p = Process() if "arguments" in self.options: p.execute(path=path, args=self.options["arguments"], suspended=True) else: p.execute(path=path, suspended=True) if self.options.get("free", "no") != "yes": p.inject() p.resume() return p.pid
def start(self, path): gw = self.options.get("setgw",None) u = Utils() if gw: u.set_default_gw(gw) p = Process() dll = self.options.get("dll") p.execute(path="bin/execsc.exe", args=path, suspended=True) p.inject(dll) p.resume() return p.pid
def start(self, path): root = os.environ["TEMP"] password = self.options.get("password", None) default_file_name = "sample.exe" with ZipFile(path, "r") as archive: zipinfos = archive.infolist() try: archive.extractall(path=root, pwd=password) except BadZipfile as e: raise CuckooPackageError("Invalid Zip file") except RuntimeError: try: password = self.options.get("password", "infected") archive.extractall(path=root, pwd=password) except RuntimeError as e: raise CuckooPackageError("Unable to extract Zip file: " "{0}".format(e)) file_name = self.options.get("file", default_file_name) if file_name == default_file_name: # no name provided try to find a better name if len(zipinfos) > 0: # take the first one file_name = zipinfos[0].filename file_path = os.path.join(root, file_name) dll = self.options.get("dll", None) free = self.options.get("free", False) args = self.options.get("arguments", None) gw = self.options.get("setgw", None) u = Utils() if gw: u.set_default_gw(gw) suspended = True if free: suspended = False p = Process() if not p.execute(path=file_path, args=args, suspended=suspended): raise CuckooPackageError("Unable to execute initial process, " "analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def start(self, url): free = self.options.get("free", False) suspended = True if free: suspended = False p = Process() if not p.execute(path=os.path.join(os.getenv("ProgramFiles"), "Internet Explorer", "iexplore.exe"), args="\"%s\"" % url, suspended=suspended): raise CuckooPackageError("Unable to execute initial Internet Explorer process, analysis aborted") if not free and suspended: p.inject() p.resume() return p.pid else: return None
def start(self, path): p = Process() rundll32 = "C:\\WINDOWS\\system32\\rundll32.exe" if "function" in self.options: p.execute(path=rundll32, args="%s,%s" % (path, self.options["function"]), suspended=True) else: p.execute(path=rundll32, args="%s,DllMain" % path, suspended=True) if self.options.get("free", "no") != "yes": p.inject() p.resume() return p.pid
def start(self, path): free = self.options.get("free", False) args = self.options.get("arguments", None) suspended = True if free: suspended = False p = Process() if not p.execute(path=path, args=args, suspended=suspended): raise CuckooPackageError("Unable to execute initial process, analysis aborted") if not free and suspended: p.inject() p.resume() return p.pid else: return None
def start(self, path): root = os.environ["TEMP"] password = self.options.get("password", None) default_file_name = "sample.exe" with ZipFile(path, "r") as archive: zipinfos = archive.infolist() try: archive.extractall(path=root, pwd=password) except BadZipfile as e: raise CuckooPackageError("Invalid Zip file") except RuntimeError: try: password = self.options.get("password", "infected") archive.extractall(path=root, pwd=password) except RuntimeError as e: raise CuckooPackageError("Unable to extract Zip file: " "{0}".format(e)) file_name = self.options.get("file", default_file_name) if file_name == default_file_name: #no name provided try to find a better name if len(zipinfos) > 0: #take the first one file_name = zipinfos[0].filename file_path = os.path.join(root, file_name) dll = self.options.get("dll", None) free = self.options.get("free", False) args = self.options.get("arguments", None) suspended = True if free: suspended = False p = Process() if not p.execute(path=file_path, args=args, suspended=suspended): raise CuckooPackageError("Unable to execute initial process, " "analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def execute(self, path, args, interest): """Starts an executable for analysis. @param path: executable path @param args: executable arguments @param interest: file of interest, passed to the cuckoomon config @return: process pid """ dll = self.options.get("dll") dll_64 = self.options.get("dll_64") free = self.options.get("free") gw = self.options.get("setgw", None) u = Utils() if gw: u.set_default_gw(gw) suspended = True if free: suspended = False kernel_analysis = self.options.get("kernel_analysis", False) if kernel_analysis != False: kernel_analysis = True p = Process() if not p.execute(path=path, args=args, suspended=suspended, kernel_analysis=kernel_analysis): raise CuckooPackageError("Unable to execute the initial process, " "analysis aborted.") if free: return None is_64bit = p.is_64bit() if not kernel_analysis: if is_64bit: p.inject(dll_64, INJECT_QUEUEUSERAPC, interest) else: p.inject(dll, INJECT_QUEUEUSERAPC, interest) p.resume() p.close() return p.pid
def execute(self, path, args): dll = self.options.get("dll") free = self.options.get("free") suspended = True if free: suspended = False p = Process() if not p.execute(path=path, args=args, suspended=suspended): raise CuckooPackageError("Unable to execute the initial process, " "analysis aborted.") if not free and suspended: p.inject(dll) p.resume() p.close() return p.pid
def start(self, path): free = self.options.get("free", False) dll = self.options.get("dll", None) suspended = True if free: suspended = False if os.getenv("ProgramFiles(x86)"): iex86 = os.path.join(os.getenv("ProgramFiles(x86)"), "Internet Explorer", "iexplore.exe") else: iex86 = os.path.join(os.getenv("ProgramFiles"), "Internet Explorer", "iexplore.exe") ie32 = os.path.join(os.getenv("ProgramFiles"), "Internet Explorer", "iexplore.exe") if os.path.exists(iex86): iexplore = iex86 else: iexplore = ie32 # Travelling inside malware universe you should bring a towel with you. # If a file detected as HTML is submitted without a proper extension, # or without an extension at all (are you used to name samples with hash?), # IE is going to open it as a text file, so you precious sample will not # be executed. # We help you sample to execute renaming it with a proper extension. if not path.endswith(".html") or not path.endswith(".htm"): shutil.copy(path, path + ".html") path = path + ".html" log.info("Submitted file is missing extension, adding .html") p = Process() if not p.execute( path=iexplore, args="\"%s\"" % path, suspended=suspended): raise CuckooPackageError("Unable to execute initial Internet " "Explorer process, analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def run(self): """Run handler. @return: operation status. """ data = "" while True: bytes_read = c_int(0) buf = create_string_buffer(BUFSIZE) success = KERNEL32.ReadFile(self.h_pipe, buf, sizeof(buf), byref(bytes_read), None) data += buf.value if not success and KERNEL32.GetLastError() == ERROR_MORE_DATA: continue #elif not success or bytes_read.value == 0: # if KERNEL32.GetLastError() == ERROR_BROKEN_PIPE: # pass break if data: command = data.strip() #log.debug("Connection received (data=%s)" % command) if command.startswith("PID:"): pid = command[4:] if pid.isdigit(): pid = int(pid) if pid not in PROCESS_LIST: add_pids(pid) proc = Process(pid=pid) proc.inject() KERNEL32.WriteFile(self.h_pipe, create_string_buffer("OK"), 2, byref(bytes_read), None) elif command.startswith("FILE:"): file_path = command[5:] add_file(file_path) return True
def start(self, path): free = self.options.get("free", False) arguments = self.options.get("arguments", "") dll = self.options.get("dll") suspended = True if free: suspended = False p = Process() if not p.execute(path="C:\\Python27\\python.exe", args="%s %s" % (path, arguments), suspended=suspended): raise CuckooPackageError("Unable to execute python, " "analysis aborted.") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def start(self, path): p = Process() if "arguments" in self.options: p.execute(path=path, args=self.options["arguments"], suspended=True) else: p.execute(path=path, suspended=True) inject = True if "free" in self.options: if self.options["free"] == "yes": inject = False if inject: p.inject() p.resume() return p.pid
def execute(self, path, args): """Starts an executable for analysis. @param path: executable path @param args: executable arguments @return: process pid """ dll = self.options.get("dll") free = self.options.get("free") suspended = True if free: suspended = False p = Process() if not p.execute(path=path, args=args, suspended=suspended): raise CuckooPackageError("Unable to execute the initial process, " "analysis aborted.") if not free and suspended: p.inject(dll) p.resume() p.close() return p.pid
def start(self, url): free = self.options.get("free", False) dll = self.options.get("dll", None) suspended = True if free: suspended = False iexplore = os.path.join(os.getenv("ProgramFiles"), "Internet Explorer", "iexplore.exe") p = Process() if not p.execute( path=iexplore, args="\"%s\"" % url, suspended=suspended): raise CuckooPackageError("Unable to execute initial Internet " "Explorer process, analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def start(self, path): free = self.options.get("free", False) dll = self.options.get("dll", None) suspended = True if free: suspended = False cmd_path = os.path.join(os.getenv("SystemRoot"), "system32", "cmd.exe") cmd_args = "/c start \"{0}\"".format(path) p = Process() if not p.execute(path=cmd_path, args=cmd_args, suspended=suspended): raise CuckooPackageError("Unable to execute initial process, " "analysis aborted") if not free and suspended: p.inject(dll) p.resume() p.close() return p.pid else: return None
def start(self, path): free = self.options.get("free", False) function = self.options.get("function", "DllMain") arguments = self.options.get("arguments", None) suspended = True if free: suspended = False args = "{0},{1}".format(path, function) if arguments: args += " {0}".format(arguments) p = Process() if not p.execute(path="C:\\WINDOWS\\system32\\rundll32.exe", args=args, suspended=suspended): raise CuckooPackageError("Unable to execute rundll32, analysis aborted") if not free and suspended: p.inject() p.resume() return p.pid else: return None
def start(self, path): word = self.get_path() if not word: raise CuckooPackageError("Unable to find any Microsoft " "Office Word executable available") dll = self.options.get("dll", None) free = self.options.get("free", False) suspended = True if free: suspended = False p = Process() if not p.execute(path=word, args="\"%s\"" % path, suspended=suspended): raise CuckooPackageError("Unable to execute initial Microsoft " "Office Word process, analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def start(self, path): wscript = self.get_path() if not wscript: raise CuckooPackageError("Unable to find any WScript " "executable available") dll = self.options.get("dll", None) free = self.options.get("free", False) suspended = True if free: suspended = False p = Process() if not p.execute(path=wscript, args="\"{0}\"".format(path), suspended=suspended): raise CuckooPackageError("Unable to execute initial WScript " "process, analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def start(self, path): powershell = self.get_path() if not powershell: raise CuckooPackageError("Unable to find any PowerShell executable available") dll = self.options.get("dll", None) free = self.options.get("free", False) suspended = True if free: suspended = False args = "-NoProfile -ExecutionPolicy unrestricted -File \"{0}\"".format(path) p = Process() if not p.execute(path=powershell, args=args, suspended=suspended): raise CuckooPackageError("Unable to execute initial PowerShell process, analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def start(self, path): reader = self.get_path() if not reader: raise CuckooPackageError("Unable to find any Adobe Reader " "executable available") dll = self.options.get("dll", None) free = self.options.get("free", False) suspended = True if free: suspended = False p = Process() if not p.execute(path=reader, args="\"%s\"" % path, suspended=suspended): raise CuckooPackageError("Unable to execute initial Adobe Reader " "process, analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def start(self, path): p = Process() rundll32 = "C:\\WINDOWS\\system32\\rundll32.exe" if "function" in self.options: p.execute(path=rundll32, args="%s,%s" % (path, self.options["function"]), suspended=True) else: p.execute(path=rundll32, args="%s,DllMain" % path, suspended=True) inject = True if "free" in self.options: if self.options["free"] == "yes": inject = False if inject: p.inject() p.resume() return p.pid
def start(self, path): pin = "C:\\pin\\pin.exe" pindll = os.path.join(os.getcwd(), "dll", "PinVMShield.dll") if not pindll: raise CuckooPackageError("Unable to find any DBA available") free = self.options.get("free", False) args = self.options.get("arguments", None) dbi = self.options.get("dbi", None) if dbi == "true": isdbi = True else: isdbi = False suspended = True if free: suspended = False p = Process() if not isdbi: if not p.execute(path=path, args=args, suspended=suspended): raise CuckooPackageError( "Unable to execute initial process, analysis aborted") else: if not p.execute(path=pin, args="-t \"%s\" -- \"%s\" %s" % (pindll, path, args), suspended=suspended): raise CuckooPackageError( "Unable to execute initial process, analysis aborted") if not free and suspended: p.inject() p.resume() p.close() return p.pid else: return None
def run(self): """Run handler. @return: operation status. """ data = "" response = "OK" wait = False proc = None # Read the data submitted to the Pipe Server. while True: bytes_read = c_int(0) buf = create_string_buffer(BUFSIZE) success = KERNEL32.ReadFile(self.h_pipe, buf, sizeof(buf), byref(bytes_read), None) data += buf.value if not success and KERNEL32.GetLastError() == ERROR_MORE_DATA: continue #elif not success or bytes_read.value == 0: # if KERNEL32.GetLastError() == ERROR_BROKEN_PIPE: # pass break if data: command = data.strip() # Parse the prefix for the received notification. # In case of GETPIDS we're gonna return the current process ID # and the process ID of our parent process (agent.py). if command == "GETPIDS": response = struct.pack("II", PID, PPID) # When analyzing we don't want to hook all functions, as we're # having some stability issues with regards to webbrowsers. elif command == "HOOKDLLS": is_url = Config(cfg="analysis.conf").category != "file" url_dlls = "ntdll", "kernel32" def hookdll_encode(names): # We have to encode each dll name as unicode string # with length 16. names = [ name + "\x00" * (16 - len(name)) for name in names ] f = lambda s: "".join(ch + "\x00" for ch in s) return "".join(f(name) for name in names) # If this sample is not a URL, then we don't want to limit # any API hooks (at least for now), so we write a null-byte # which indicates that all DLLs should be hooked. if not is_url: response = "\x00" else: response = hookdll_encode(url_dlls) # In case of PID, the client is trying to notify the creation of # a new process to be injected and monitored. elif command.startswith("PROCESS:"): # We acquire the process lock in order to prevent the analyzer # to terminate the analysis while we are operating on the new # process. PROCESS_LOCK.acquire() # Set the current DLL to the default one provided # at submission. dll = DEFAULT_DLL # We parse the process ID. data = command[8:] process_id = thread_id = None if not "," in data: if data.isdigit(): process_id = int(data) elif len(data.split(",")) == 2: process_id, param = data.split(",") thread_id = None if process_id.isdigit(): process_id = int(process_id) else: process_id = None if param.isdigit(): thread_id = int(param) else: # XXX: Expect a new DLL as a message parameter? if isinstance(param, str): dll = param if process_id: if process_id not in (PID, PPID): # We inject the process only if it's not being # monitored already, otherwise we would generated # polluted logs. if process_id not in PROCESS_LIST: # Open the process and inject the DLL. # Hope it enjoys it. proc = Process(pid=process_id, thread_id=thread_id) filepath = proc.get_filepath() filename = os.path.basename(filepath) log.info("Announced process name: %s", filename) if not protected_filename(filename): # Add the new process ID to the list of # monitored processes. add_pids(process_id) # If we have both pid and tid, then we can use # apc to inject if process_id and thread_id: proc.inject(dll, apc=True) else: # we inject using CreateRemoteThread, this # needs the waiting in order to make sure # no race conditions occur proc.inject(dll) wait = True log.info( "Successfully injected process with " "pid %s", proc.pid) else: log.warning("Received request to inject Cuckoo " "processes, skip") # Once we're done operating on the processes list, we release # the lock. PROCESS_LOCK.release() # In case of FILE_NEW, the client is trying to notify the creation # of a new file. elif command.startswith("FILE_NEW:"): # We extract the file path. file_path = command[9:].decode("utf-8") # We add the file to the list. add_file(file_path) # In case of FILE_DEL, the client is trying to notify an ongoing # deletion of an existing file, therefore we need to dump it # straight away. elif command.startswith("FILE_DEL:"): # Extract the file path. file_path = command[9:].decode("utf-8") # Dump the file straight away. del_file(file_path) elif command.startswith("FILE_MOVE:"): # syntax = FILE_MOVE:old_file_path::new_file_path if "::" in command[10:]: old_fname, new_fname = command[10:].split("::", 1) move_file(old_fname.decode("utf-8"), new_fname.decode("utf-8")) KERNEL32.WriteFile(self.h_pipe, create_string_buffer(response), len(response), byref(bytes_read), None) KERNEL32.CloseHandle(self.h_pipe) # We wait until cuckoomon reports back. if wait: proc.wait() if proc: proc.close() return True
def run(self): """Run handler. @return: operation status. """ global MONITORED_SERVICES global LASTINJECT_TIME data = "" response = "OK" # Read the data submitted to the Pipe Server. while True: bytes_read = c_int(0) buf = create_string_buffer(BUFSIZE) success = KERNEL32.ReadFile(self.h_pipe, buf, sizeof(buf), byref(bytes_read), None) data += buf.value if not success and KERNEL32.GetLastError() == ERROR_MORE_DATA: continue # elif not success or bytes_read.value == 0: # if KERNEL32.GetLastError() == ERROR_BROKEN_PIPE: # pass break if data: command = data.strip() # Debug, Regular, Warning, or Critical information from CuckooMon. if command.startswith("DEBUG:"): log.debug(command[6:]) elif command.startswith("INFO:"): log.info(command[5:]) elif command.startswith("WARNING:"): log.warning(command[8:]) elif command.startswith("CRITICAL:"): log.critical(command[9:]) # Parse the prefix for the received notification. # In case of GETPIDS we're gonna return the current process ID # and the process ID of our parent process (agent.py). elif command == "GETPIDS": response = struct.pack("II", PID, PPID) # When analyzing we don't want to hook all functions, as we're # having some stability issues with regards to webbrowsers. elif command == "HOOKDLLS": is_url = Config(cfg="analysis.conf").category != "file" url_dlls = "ntdll", "kernel32" def hookdll_encode(names): # We have to encode each dll name as unicode string # with length 16. names = [name + "\x00" * (16-len(name)) for name in names] f = lambda s: "".join(ch + "\x00" for ch in s) return "".join(f(name) for name in names) # If this sample is not a URL, then we don't want to limit # any API hooks (at least for now), so we write a null-byte # which indicates that all DLLs should be hooked. if not is_url: response = "\x00" else: response = hookdll_encode(url_dlls) # remove pid from process list because we received a notification # from kernel land elif command.startswith("KTERMINATE:"): data = command[11:] process_id = int(data) if process_id: if process_id in PROCESS_LIST: remove_pid(process_id) # same than below but we don't want to inject any DLLs because # it's a kernel analysis elif command.startswith("KPROCESS:"): PROCESS_LOCK.acquire() data = command[9:] process_id = int(data) thread_id = None if process_id: if process_id not in (PID, PPID): if process_id not in PROCESS_LIST: proc = Process(pid=process_id,thread_id=thread_id) filepath = proc.get_filepath() filename = os.path.basename(filepath) if not protected_filename(filename): add_pid(process_id) log.info("Announce process name : %s", filename) PROCESS_LOCK.release() elif command.startswith("KERROR:"): error_msg = command[7:] log.error("Error : %s", str(error_msg)) # if a new driver has been loaded, we stop the analysis elif command == "KSUBVERT": for pid in PROCESS_LIST: log.info("Process with pid %s has terminated", pid) PROCESS_LIST.remove(pid) # Handle case of a service being started by a monitored process # Switch the service type to own process behind its back so we # can monitor the service more easily with less noise elif command.startswith("SERVICE:"): servname = command[8:] si = subprocess.STARTUPINFO() si.dwFlags = subprocess.STARTF_USESHOWWINDOW si.wShowWindow = subprocess.SW_HIDE subprocess.call("sc config " + servname + " type= own", startupinfo=si) log.info("Announced starting service \"%s\"", servname) if not MONITORED_SERVICES: # Inject into services.exe so we can monitor service creation # if tasklist previously failed to get the services.exe PID we'll be # unable to inject if SERVICES_PID: servproc = Process(pid=SERVICES_PID,suspended=False) filepath = servproc.get_filepath() servproc.inject(dll=DEFAULT_DLL, interest=filepath, nosleepskip=True) LASTINJECT_TIME = datetime.now() servproc.close() KERNEL32.Sleep(1000) MONITORED_SERVICES = True else: log.error('Unable to monitor service %s' % (servname)) # For now all we care about is bumping up our LASTINJECT_TIME to account for long delays between # injection and actual resume time where the DLL would have a chance to load in the new process # and report back to have its pid added to the list of monitored processes elif command.startswith("RESUME:"): LASTINJECT_TIME = datetime.now() # Handle case of malware terminating a process -- notify the target # ahead of time so that it can flush its log buffer elif command.startswith("KILL:"): PROCESS_LOCK.acquire() process_id = int(command[5:]) if process_id not in (PID, PPID) and process_id in PROCESS_LIST: # only notify processes we've hooked event_name = TERMINATE_EVENT + str(process_id) event_handle = KERNEL32.OpenEventA(EVENT_MODIFY_STATE, False, event_name) if not event_handle: log.warning("Unable to open termination event for pid %u.", process_id) else: # make sure process is aware of the termination KERNEL32.SetEvent(event_handle) KERNEL32.CloseHandle(event_handle) PROCESS_LOCK.release() # Handle notification of cuckoomon loading in a process elif command.startswith("LOADED:"): PROCESS_LOCK.acquire() process_id = int(command[7:]) if process_id not in PROCESS_LIST: add_pids(process_id) PROCESS_LOCK.release() log.info("Cuckoomon successfully loaded in process with pid %u.", process_id) # In case of PID, the client is trying to notify the creation of # a new process to be injected and monitored. elif command.startswith("PROCESS:"): # We acquire the process lock in order to prevent the analyzer # to terminate the analysis while we are operating on the new # process. PROCESS_LOCK.acquire() # Set the current DLL to the default one provided # at submission. dll = DEFAULT_DLL suspended = False # We parse the process ID. data = command[8:] if len(data) > 2 and data[1] == ':': if data[0] == '1': suspended = True data = command[10:] process_id = thread_id = None if "," not in data: if data.isdigit(): process_id = int(data) elif data.count(",") == 1: process_id, param = data.split(",") thread_id = None if process_id.isdigit(): process_id = int(process_id) else: process_id = None if param.isdigit(): thread_id = int(param) if process_id: if process_id not in (PID, PPID): # We inject the process only if it's not being # monitored already, otherwise we would generate # polluted logs. if process_id not in PROCESS_LIST: # Open the process and inject the DLL. # Hope it enjoys it. proc = Process(pid=process_id, thread_id=thread_id, suspended=suspended) filepath = proc.get_filepath() is_64bit = proc.is_64bit() filename = os.path.basename(filepath) log.info("Announced %s process name: %s pid: %d", "64-bit" if is_64bit else "32-bit", filename, process_id) if not in_protected_path(filename): res = proc.inject(dll, filepath) LASTINJECT_TIME = datetime.now() proc.close() else: log.warning("Received request to inject Cuckoo " "process with pid %d, skip", process_id) # Once we're done operating on the processes list, we release # the lock. PROCESS_LOCK.release() # In case of FILE_NEW, the client is trying to notify the creation # of a new file. elif command.startswith("FILE_NEW:"): # We extract the file path. file_path = unicode(command[9:].decode("utf-8")) # We add the file to the list. add_file(file_path) # In case of FILE_DEL, the client is trying to notify an ongoing # deletion of an existing file, therefore we need to dump it # straight away. elif command.startswith("FILE_DEL:"): # Extract the file path. file_path = unicode(command[9:].decode("utf-8")) # Dump the file straight away. del_file(file_path) elif command.startswith("FILE_MOVE:"): # Syntax = "FILE_MOVE:old_file_path::new_file_path". if "::" in command[10:]: old_fname, new_fname = command[10:].split("::", 1) move_file(unicode(old_fname.decode("utf-8")), unicode(new_fname.decode("utf-8"))) else: log.warning("Received unknown command from cuckoomon: %s", command) KERNEL32.WriteFile(self.h_pipe, create_string_buffer(response), len(response), byref(bytes_read), None) KERNEL32.CloseHandle(self.h_pipe) return True
def _inject_process(self, process_id, thread_id, mode): """Helper function for injecting the monitor into a process.""" # We acquire the process lock in order to prevent the analyzer to # terminate the analysis while we are operating on the new process. self.analyzer.process_lock.acquire() # Set the current DLL to the default one provided at submission. dll = self.analyzer.default_dll if process_id in (self.analyzer.pid, self.analyzer.ppid): if process_id not in self.ignore_list["pid"]: log.warning("Received request to inject Cuckoo processes, " "skipping it.") self.ignore_list["pid"].append(process_id) self.analyzer.process_lock.release() return # We inject the process only if it's not being monitored already, # otherwise we would generated polluted logs (if it wouldn't crash # horribly to start with). if self.analyzer.process_list.has_pid(process_id): # This pid is already on the notrack list, move it to the # list of tracked pids. if not self.analyzer.process_list.has_pid(process_id, notrack=False): log.debug("Received request to inject pid=%d. It was already " "on our notrack list, moving it to the track list.") self.analyzer.process_list.remove_pid(process_id) self.analyzer.process_list.add_pid(process_id) self.ignore_list["pid"].append(process_id) # Spit out an error once and just ignore it further on. elif process_id not in self.ignore_list["pid"]: log.debug( "Received request to inject pid=%d, but we are " "already injected there.", process_id) self.ignore_list["pid"].append(process_id) # We're done operating on the processes list, release the lock. self.analyzer.process_lock.release() return # Open the process and inject the DLL. Hope it enjoys it. proc = Process(pid=process_id, tid=thread_id) filename = os.path.basename(proc.get_filepath()) if not self.analyzer.files.is_protected_filename(filename): # Add the new process ID to the list of monitored processes. self.analyzer.process_list.add_pid(process_id) # We're done operating on the processes list, # release the lock. Let the injection do its thing. self.analyzer.process_lock.release() # If we have both pid and tid, then we can use APC to inject. if process_id and thread_id: proc.inject(dll, apc=True, mode="%s" % mode) else: proc.inject(dll, apc=False, mode="%s" % mode) log.info("Injected into process with pid %s and name %r", proc.pid, filename)
def run(self): """Run handler. @return: operation status. """ global MONITORED_SERVICES global MONITORED_WMI global MONITORED_DCOM global MONITORED_TASKSCHED global MONITORED_BITS global LASTINJECT_TIME global NUM_INJECTED try: data = "" response = "OK" # Read the data submitted to the Pipe Server. while True: bytes_read = c_int(0) buf = create_string_buffer(BUFSIZE) success = KERNEL32.ReadFile(self.h_pipe, buf, sizeof(buf), byref(bytes_read), None) data += buf.value if not success and KERNEL32.GetLastError() == ERROR_MORE_DATA: continue # elif not success or bytes_read.value == 0: # if KERNEL32.GetLastError() == ERROR_BROKEN_PIPE: # pass break if data: command = data.strip() # Debug, Regular, Warning, or Critical information from CuckooMon. if command.startswith("DEBUG:"): log.debug(command[6:]) elif command.startswith("INFO:"): log.info(command[5:]) elif command.startswith("WARNING:"): log.warning(command[8:]) elif command.startswith("CRITICAL:"): log.critical(command[9:]) # Parse the prefix for the received notification. # In case of GETPIDS we're gonna return the current process ID # and the process ID of our parent process (agent.py). elif command == "GETPIDS": hidepids = set() hidepids.update(HIDE_PIDS) hidepids.update([PID, PPID]) response = struct.pack("%dI" % len(hidepids), *hidepids) # remove pid from process list because we received a notification # from kernel land elif command.startswith("KTERMINATE:"): data = command[11:] process_id = int(data) if process_id: if process_id in PROCESS_LIST: remove_pid(process_id) # same than below but we don't want to inject any DLLs because # it's a kernel analysis elif command.startswith("KPROCESS:"): PROCESS_LOCK.acquire() data = command[9:] process_id = int(data) thread_id = None if process_id: if process_id not in (PID, PPID): if process_id not in PROCESS_LIST: proc = Process(pid=process_id,thread_id=thread_id) filepath = proc.get_filepath() filename = os.path.basename(filepath) if not in_protected_path(filename): add_pid(process_id) log.info("Announce process name : %s", filename) PROCESS_LOCK.release() elif command.startswith("KERROR:"): error_msg = command[7:] log.error("Error : %s", str(error_msg)) # if a new driver has been loaded, we stop the analysis elif command == "KSUBVERT": for pid in PROCESS_LIST: log.info("Process with pid %s has terminated", pid) PROCESS_LIST.remove(pid) elif command.startswith("INTEROP:"): if not MONITORED_DCOM: MONITORED_DCOM = True dcom_pid = pid_from_service_name("DcomLaunch") if dcom_pid: servproc = Process(pid=dcom_pid,suspended=False) servproc.set_critical() filepath = servproc.get_filepath() servproc.inject(dll=DEFAULT_DLL, interest=filepath, nosleepskip=True) LASTINJECT_TIME = datetime.now() servproc.close() KERNEL32.Sleep(2000) elif command.startswith("WMI:"): if not MONITORED_WMI: MONITORED_WMI = True si = subprocess.STARTUPINFO() # STARTF_USESHOWWINDOW si.dwFlags = 1 # SW_HIDE si.wShowWindow = 0 log.info("Stopping WMI Service") subprocess.call(['net', 'stop', 'winmgmt', '/y'], startupinfo=si) log.info("Stopped WMI Service") subprocess.call("sc config winmgmt type= own", startupinfo=si) if not MONITORED_DCOM: MONITORED_DCOM = True dcom_pid = pid_from_service_name("DcomLaunch") if dcom_pid: servproc = Process(pid=dcom_pid,suspended=False) servproc.set_critical() filepath = servproc.get_filepath() servproc.inject(dll=DEFAULT_DLL, interest=filepath, nosleepskip=True) LASTINJECT_TIME = datetime.now() servproc.close() KERNEL32.Sleep(2000) log.info("Starting WMI Service") subprocess.call("net start winmgmt", startupinfo=si) log.info("Started WMI Service") wmi_pid = pid_from_service_name("winmgmt") if wmi_pid: servproc = Process(pid=wmi_pid,suspended=False) servproc.set_critical() filepath = servproc.get_filepath() servproc.inject(dll=DEFAULT_DLL, interest=filepath, nosleepskip=True) LASTINJECT_TIME = datetime.now() servproc.close() KERNEL32.Sleep(2000) elif command.startswith("TASKSCHED:"): if not MONITORED_TASKSCHED: MONITORED_TASKSCHED = True si = subprocess.STARTUPINFO() # STARTF_USESHOWWINDOW si.dwFlags = 1 # SW_HIDE si.wShowWindow = 0 log.info("Stopping Task Scheduler Service") subprocess.call(['net', 'stop', 'schedule', '/y'], startupinfo=si) log.info("Stopped Task Scheduler Service") subprocess.call("sc config schedule type= own", startupinfo=si) log.info("Starting Task Scheduler Service") subprocess.call("net start schedule", startupinfo=si) log.info("Started Task Scheduler Service") sched_pid = pid_from_service_name("schedule") if sched_pid: servproc = Process(pid=sched_pid,suspended=False) servproc.set_critical() filepath = servproc.get_filepath() servproc.inject(dll=DEFAULT_DLL, interest=filepath, nosleepskip=True) LASTINJECT_TIME = datetime.now() servproc.close() KERNEL32.Sleep(2000) elif command.startswith("BITS:"): if not MONITORED_BITS: MONITORED_BITS = True si = subprocess.STARTUPINFO() # STARTF_USESHOWWINDOW si.dwFlags = 1 # SW_HIDE si.wShowWindow = 0 log.info("Stopping BITS Service") subprocess.call(['net', 'stop', 'BITS', '/y'], startupinfo=si) log.info("Stopped BITS Service") subprocess.call("sc config BITS type= own", startupinfo=si) if not MONITORED_DCOM: MONITORED_DCOM = True dcom_pid = pid_from_service_name("DcomLaunch") if dcom_pid: servproc = Process(pid=dcom_pid,suspended=False) servproc.set_critical() filepath = servproc.get_filepath() servproc.inject(dll=DEFAULT_DLL, interest=filepath, nosleepskip=True) LASTINJECT_TIME = datetime.now() servproc.close() KERNEL32.Sleep(2000) log.info("Starting BITS Service") subprocess.call("net start BITS", startupinfo=si) log.info("Started BITS Service") bits_pid = pid_from_service_name("BITS") if bits_pid: servproc = Process(pid=bits_pid,suspended=False) servproc.set_critical() filepath = servproc.get_filepath() servproc.inject(dll=DEFAULT_DLL, interest=filepath, nosleepskip=True) LASTINJECT_TIME = datetime.now() servproc.close() KERNEL32.Sleep(2000) # Handle case of a service being started by a monitored process # Switch the service type to own process behind its back so we # can monitor the service more easily with less noise elif command.startswith("SERVICE:"): servname = command[8:] si = subprocess.STARTUPINFO() # STARTF_USESHOWWINDOW si.dwFlags = 1 # SW_HIDE si.wShowWindow = 0 subprocess.call("sc config " + servname + " type= own", startupinfo=si) log.info("Announced starting service \"%s\"", servname) if not MONITORED_SERVICES: # Inject into services.exe so we can monitor service creation # if tasklist previously failed to get the services.exe PID we'll be # unable to inject if SERVICES_PID: servproc = Process(pid=SERVICES_PID,suspended=False) servproc.set_critical() filepath = servproc.get_filepath() servproc.inject(dll=DEFAULT_DLL, interest=filepath, nosleepskip=True) LASTINJECT_TIME = datetime.now() servproc.close() KERNEL32.Sleep(1000) MONITORED_SERVICES = True else: log.error('Unable to monitor service %s' % (servname)) # For now all we care about is bumping up our LASTINJECT_TIME to account for long delays between # injection and actual resume time where the DLL would have a chance to load in the new process # and report back to have its pid added to the list of monitored processes elif command.startswith("RESUME:"): LASTINJECT_TIME = datetime.now() # Handle attempted shutdowns/restarts -- flush logs for all monitored processes # additional handling can be added later elif command.startswith("SHUTDOWN:"): log.info("Received shutdown request") PROCESS_LOCK.acquire() for process_id in PROCESS_LIST: event_name = TERMINATE_EVENT + str(process_id) event_handle = KERNEL32.OpenEventA(EVENT_MODIFY_STATE, False, event_name) if event_handle: KERNEL32.SetEvent(event_handle) KERNEL32.CloseHandle(event_handle) if self.options.get("procmemdump"): p = Process(pid=process_id) p.dump_memory() dump_files() PROCESS_LOCK.release() # Handle case of malware terminating a process -- notify the target # ahead of time so that it can flush its log buffer elif command.startswith("KILL:"): PROCESS_LOCK.acquire() process_id = int(command[5:]) if process_id not in (PID, PPID) and process_id in PROCESS_LIST: # only notify processes we've hooked event_name = TERMINATE_EVENT + str(process_id) event_handle = KERNEL32.OpenEventA(EVENT_MODIFY_STATE, False, event_name) if not event_handle: log.warning("Unable to open termination event for pid %u.", process_id) else: log.info("Notified of termination of process with pid %u.", process_id) # dump the memory of exiting processes if self.options.get("procmemdump"): p = Process(pid=process_id) p.dump_memory() # make sure process is aware of the termination KERNEL32.SetEvent(event_handle) KERNEL32.CloseHandle(event_handle) PROCESS_LOCK.release() # Handle notification of cuckoomon loading in a process elif command.startswith("LOADED:"): PROCESS_LOCK.acquire() process_id = int(command[7:]) if process_id not in PROCESS_LIST: add_pids(process_id) PROCESS_LOCK.release() NUM_INJECTED += 1 log.info("Cuckoomon successfully loaded in process with pid %u.", process_id) # In case of PID, the client is trying to notify the creation of # a new process to be injected and monitored. elif command.startswith("PROCESS:"): # We acquire the process lock in order to prevent the analyzer # to terminate the analysis while we are operating on the new # process. PROCESS_LOCK.acquire() # Set the current DLL to the default one provided # at submission. dll = DEFAULT_DLL suspended = False # We parse the process ID. data = command[8:] if len(data) > 2 and data[1] == ':': if data[0] == '1': suspended = True data = command[10:] process_id = thread_id = None if "," not in data: if data.isdigit(): process_id = int(data) elif data.count(",") == 1: process_id, param = data.split(",") thread_id = None if process_id.isdigit(): process_id = int(process_id) else: process_id = None if param.isdigit(): thread_id = int(param) if process_id: if process_id not in (PID, PPID): # We inject the process only if it's not being # monitored already, otherwise we would generate # polluted logs. if process_id not in PROCESS_LIST: # Open the process and inject the DLL. # Hope it enjoys it. proc = Process(pid=process_id, thread_id=thread_id, suspended=suspended) filepath = proc.get_filepath() # if it's a URL analysis, provide the URL to all processes as # the "interest" -- this will allow cuckoomon to see in the # child browser process that a URL analysis is occurring if self.config.category == "file" or NUM_INJECTED > 1: interest = filepath else: interest = self.config.target is_64bit = proc.is_64bit() filename = os.path.basename(filepath) if not in_protected_path(filename) and proc.check_inject(): log.info("Announced %s process name: %s pid: %d", "64-bit" if is_64bit else "32-bit", filename, process_id) proc.inject(dll, interest) LASTINJECT_TIME = datetime.now() proc.close() else: log.warning("Received request to inject Cuckoo " "process with pid %d, skip", process_id) # Once we're done operating on the processes list, we release # the lock. PROCESS_LOCK.release() # In case of FILE_NEW, the client is trying to notify the creation # of a new file. elif command.startswith("FILE_NEW:"): FILES_LIST_LOCK.acquire() # We extract the file path. file_path = unicode(command[9:].decode("utf-8")) # We add the file to the list. add_file(file_path) FILES_LIST_LOCK.release() # In case of FILE_DEL, the client is trying to notify an ongoing # deletion of an existing file, therefore we need to dump it # straight away. elif command.startswith("FILE_DEL:"): FILES_LIST_LOCK.acquire() # Extract the file path. file_path = unicode(command[9:].decode("utf-8")) # Dump the file straight away. del_file(file_path) FILES_LIST_LOCK.release() elif command.startswith("FILE_MOVE:"): FILES_LIST_LOCK.acquire() # Syntax = "FILE_MOVE:old_file_path::new_file_path". if "::" in command[10:]: old_fname, new_fname = command[10:].split("::", 1) move_file(unicode(old_fname.decode("utf-8")), unicode(new_fname.decode("utf-8"))) FILES_LIST_LOCK.release() else: log.warning("Received unknown command from cuckoomon: %s", command) KERNEL32.WriteFile(self.h_pipe, create_string_buffer(response), len(response), byref(bytes_read), None) KERNEL32.CloseHandle(self.h_pipe) return True except Exception as e: error_exc = traceback.format_exc() log.exception(error_exc) return True
def start(self, path): try: sc = self.get_path("sc.exe") servicename = self.options.get("servicename", "CAPEService") servicedesc = self.options.get("servicedesc", "CAPE Service") arguments = self.options.get("arguments") if "." not in os.path.basename(path): new_path = path + ".exe" os.rename(path, new_path) path = new_path binPath = '"{0}"'.format(path) if arguments: binPath += " {0}".format(arguments) scm_handle = ADVAPI32.OpenSCManagerA(None, None, SC_MANAGER_ALL_ACCESS) if scm_handle == 0: log.info("Failed to open SCManager") log.info(ctypes.FormatError()) return service_handle = ADVAPI32.CreateServiceA( scm_handle, servicename, servicedesc, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, binPath, None, None, None, None, None, ) if service_handle == 0: log.info("Failed to create service") log.info(ctypes.FormatError()) return log.info("Created service (handle: 0x%x)", service_handle) servproc = Process(options=self.options, config=self.config, pid=self.config.services_pid, suspended=False) filepath = servproc.get_filepath() is_64bit = servproc.is_64bit() if is_64bit: servproc.inject(injectmode=INJECT_QUEUEUSERAPC, interest=filepath, nosleepskip=True) else: servproc.inject(injectmode=INJECT_QUEUEUSERAPC, interest=filepath, nosleepskip=True) servproc.close() KERNEL32.Sleep(500) service_launched = ADVAPI32.StartServiceA(service_handle, 0, None) if service_launched == True: log.info("Successfully started service") else: log.info(ctypes.FormatError()) log.info("Failed to start service") ADVAPI32.CloseServiceHandle(service_handle) ADVAPI32.CloseServiceHandle(scm_handle) return except Exception as e: log.info(sys.exc_info()[0]) log.info(e) log.info(e.__dict__) log.info(e.__class__) log.exception(e)
def run(self): """Run handler. @return: operation status. """ data = "" response = "OK" wait = False proc = None # Read the data submitted to the Pipe Server. while True: bytes_read = c_int(0) buf = create_string_buffer(BUFSIZE) success = KERNEL32.ReadFile(self.h_pipe, buf, sizeof(buf), byref(bytes_read), None) data += buf.value if not success and KERNEL32.GetLastError() == ERROR_MORE_DATA: continue #elif not success or bytes_read.value == 0: # if KERNEL32.GetLastError() == ERROR_BROKEN_PIPE: # pass break if data: command = data.strip() # Parse the prefix for the received notification. # In case of GETPIDS we're gonna return the current process ID # and the process ID of our parent process (agent.py). if command == "GETPIDS": response = struct.pack("II", PID, PPID) # In case of PID, the client is trying to notify the creation of # a new process to be injected and monitored. elif command.startswith("PROCESS:"): # We acquire the process lock in order to prevent the analyzer # to terminate the analysis while we are operating on the new # process. PROCESS_LOCK.acquire() # We parse the process ID. data = command[8:] process_id = thread_id = None if not "," in data: if data.isdigit(): process_id = int(data) elif len(data.split(",")) == 2: process_id, thread_id = data.split(",") if process_id.isdigit(): process_id = int(process_id) else: process_id = None if thread_id.isdigit(): thread_id = int(thread_id) else: thread_id = None if process_id: if process_id not in (PID, PPID): # We inject the process only if it's not being monitored # already, otherwise we would generated polluted logs. if process_id not in PROCESS_LIST: # Open the process and inject the DLL. # Hope it enjoys it. proc = Process(pid=process_id, thread_id=thread_id) filepath = proc.get_filepath() filename = os.path.basename(filepath) log.info("Announced process name: %s", filename) if not protected_filename(filename): # Add the new process ID to the list of monitored # processes. add_pids(process_id) # If we have both pid and tid, then we can use # apc to inject if process_id and thread_id: proc.inject(apc=True) else: # we inject using CreateRemoteThread, this # needs the waiting in order to make sure no # race conditions occur proc.inject() wait = True log.info("Successfully injected process with pid %s", proc.pid) else: log.warning("Received request to inject Cuckoo processes, skip") # Once we're done operating on the processes list, we release # the lock. PROCESS_LOCK.release() # In case of FILE_NEW, the client is trying to notify the creation # of a new file. elif command.startswith("FILE_NEW:"): # We extract the file path. file_path = command[9:].decode("utf-8") # We add the file to the list. add_file(file_path) # In case of FILE_DEL, the client is trying to notify an ongoing # deletion of an existing file, therefore we need to dump it # straight away. elif command.startswith("FILE_DEL:"): # Extract the file path. file_path = command[9:].decode("utf-8") # Dump the file straight away. del_file(file_path) elif command.startswith("FILE_MOVE:"): # syntax = FILE_MOVE:old_file_path::new_file_path if "::" in command[10:]: old_fname, new_fname = command[10:].split("::", 1) move_file(old_fname.decode("utf-8"), new_fname.decode("utf-8")) KERNEL32.WriteFile(self.h_pipe, create_string_buffer(response), len(response), byref(bytes_read), None) KERNEL32.CloseHandle(self.h_pipe) # We wait until cuckoomon reports back. if wait: proc.wait() if proc: proc.close() return True