def run(self): while self.do_run: flags = FILE_FLAG_WRITE_THROUGH if self.message: pipe_handle = KERNEL32.CreateNamedPipeA( self.pipe_name, PIPE_ACCESS_DUPLEX | flags, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, BUFSIZE, BUFSIZE, 0, None) else: pipe_handle = KERNEL32.CreateNamedPipeA( self.pipe_name, PIPE_ACCESS_INBOUND | flags, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 0, BUFSIZE, 0, None) if pipe_handle == INVALID_HANDLE_VALUE: log.warning("Error opening logging pipe server.") continue if KERNEL32.ConnectNamedPipe(pipe_handle, None) or \ KERNEL32.GetLastError() == ERROR_PIPE_CONNECTED: handler = self.pipe_handler(pipe_handle, **self.kwargs) handler.daemon = True handler.start() self.handlers.add(handler) else: KERNEL32.CloseHandle(pipe_handle)
def spCreateProcessW(application_name, command_line, process_attributes, thread_attributes, inherit_handles, creation_flags, environment, current_directory, startup_info): class STARTUPINFO(Structure): _fields_ = [ ("cb", c_uint), ("reserved1", c_void_p), ("desktop", c_void_p), ("title", c_void_p), ("unused1", c_uint * 7), ("flags", c_uint), ("show_window", c_uint16), ("reserved2", c_uint16), ("reserved3", c_void_p), ("std_input", c_void_p), ("std_output", c_void_p), ("std_error", c_void_p), ] class PROCESS_INFORMATION(Structure): _fields_ = [ ("process_handle", c_void_p), ("thread_handle", c_void_p), ("process_identifier", c_uint), ("thread_identifier", c_uint), ] class Handle(int): def Close(self): KERNEL32.CloseHandle(self) if environment: environment = "\x00".join("%s=%s" % (k, v) for k, v in environment.items()) + "\x00\x00" si = STARTUPINFO() si.cb = sizeof(STARTUPINFO) if startup_info: si.flags = startup_info.dwFlags si.show_window = startup_info.wShowWindow if si.flags & STARTF_USESTDHANDLES: si.std_input = cast(int(startup_info.hStdInput), c_void_p) si.std_output = cast(int(startup_info.hStdOutput), c_void_p) si.std_error = cast(int(startup_info.hStdError), c_void_p) pi = PROCESS_INFORMATION() result = KERNEL32.CreateProcessW(application_name, command_line, None, None, inherit_handles, creation_flags, environment, current_directory, byref(si), byref(pi)) if not result: # TODO We'll just assume this is correct for now. raise WindowsError(KERNEL32.GetLastError()) return (Handle(pi.process_handle), Handle(pi.thread_handle), pi.process_identifier, pi.thread_identifier)
def exit_code(self): """Get process exit code. @return: exit code value. """ process_handle = self.open_process() exit_code = c_ulong(0) KERNEL32.GetExitCodeProcess(process_handle, byref(exit_code)) KERNEL32.CloseHandle(process_handle) return exit_code.value
def terminate(self): """Terminate process. @return: operation status. """ process_handle = self.open_process() ret = KERNEL32.TerminateProcess(process_handle, 1) KERNEL32.CloseHandle(process_handle) if ret: log.info("Successfully terminated process with pid %d.", self.pid) return True else: log.error("Failed to terminate process with pid %d.", self.pid) return False
def _read_message(self, buf): """Reads a message.""" bytes_read = c_uint() ret = "" while True: success = KERNEL32.ReadFile(self.pipe_handle, byref(buf), sizeof(buf), byref(bytes_read), None) if KERNEL32.GetLastError() == ERROR_MORE_DATA: ret += buf.raw[:bytes_read.value] elif success: return ret + buf.raw[:bytes_read.value] else: return
def get_parent_pid(self): """Get the Parent Process ID.""" class PROCESS_BASIC_INFORMATION(Structure): _fields_ = [ ("ExitStatus", c_void_p), ("PebBaseAddress", c_void_p), ("AffinityMask", c_void_p), ("BasePriority", c_void_p), ("UniqueProcessId", c_void_p), ("InheritedFromUniqueProcessId", c_void_p), ] NT_SUCCESS = lambda val: val >= 0 pbi = PROCESS_BASIC_INFORMATION() size = c_int() # Set return value to signed 32bit integer. NTDLL.NtQueryInformationProcess.restype = c_int process_handle = self.open_process() ret = NTDLL.NtQueryInformationProcess(process_handle, 0, byref(pbi), sizeof(pbi), byref(size)) KERNEL32.CloseHandle(process_handle) if NT_SUCCESS(ret) and size.value == sizeof(pbi): return pbi.InheritedFromUniqueProcessId
def run(self): """Run the pipe dispatcher.""" buf = create_string_buffer(BUFSIZE) bytes_written = c_uint() while self.do_run: message = self._read_message(buf) if not message: break response = self.dispatcher.dispatch(message) or "OK" KERNEL32.WriteFile(self.pipe_handle, response, len(response), byref(bytes_written), None) KERNEL32.CloseHandle(self.pipe_handle)
def get_filepath(self): """Get process image file path. @return: decoded file path. """ process_handle = self.open_process() NT_SUCCESS = lambda val: val >= 0 pbi = create_string_buffer(200) size = c_int() # Set return value to signed 32bit integer. NTDLL.NtQueryInformationProcess.restype = c_int ret = NTDLL.NtQueryInformationProcess(process_handle, 27, byref(pbi), sizeof(pbi), byref(size)) KERNEL32.CloseHandle(process_handle) if NT_SUCCESS(ret) and size.value > 8: try: fbuf = pbi.raw[8:] fbuf = fbuf[:fbuf.find("\x00\x00") + 1] return fbuf.decode("utf16", errors="ignore") except: return "" return ""
def click_mouse(): # Move mouse to top-middle position. USER32.SetCursorPos(RESOLUTION["x"] / 2, 0) # Mouse down. USER32.mouse_event(2, 0, 0, 0, None) KERNEL32.Sleep(50) # Mouse up. USER32.mouse_event(4, 0, 0, 0, None)
def set_clock(clock): st = SYSTEMTIME() st.wYear = clock.year st.wMonth = clock.month st.wDay = clock.day st.wHour = clock.hour st.wMinute = clock.minute st.wSecond = clock.second st.wMilliseconds = 0 KERNEL32.SetLocalTime(ctypes.byref(st))
def grant_privilege(privilege): """Grant debug privileges. @param pid: PID. @return: operation status. """ ADVAPI32.OpenProcessToken.argtypes = (wintypes.HANDLE, wintypes.DWORD, POINTER(wintypes.HANDLE)) ADVAPI32.LookupPrivilegeValueW.argtypes = (wintypes.LPWSTR, wintypes.LPWSTR, POINTER(LUID)) ADVAPI32.AdjustTokenPrivileges.argtypes = (wintypes.HANDLE, wintypes.BOOL, POINTER(TOKEN_PRIVILEGES), wintypes.DWORD, POINTER(TOKEN_PRIVILEGES), POINTER(wintypes.DWORD)) h_process = KERNEL32.GetCurrentProcess() h_current_token = wintypes.HANDLE() if not ADVAPI32.OpenProcessToken(h_process, TOKEN_ALL_ACCESS, h_current_token): return False se_original_luid = LUID() if not ADVAPI32.LookupPrivilegeValueW(None, privilege, se_original_luid): return False luid_attributes = LUID_AND_ATTRIBUTES() luid_attributes.Luid = se_original_luid luid_attributes.Attributes = SE_PRIVILEGE_ENABLED token_privs = TOKEN_PRIVILEGES() token_privs.PrivilegeCount = 1 token_privs.Privileges = luid_attributes if not ADVAPI32.AdjustTokenPrivileges(h_current_token, False, token_privs, 0, None, None): return False KERNEL32.CloseHandle(h_current_token) KERNEL32.CloseHandle(h_process) return True
def invoke(self, ctlcode, value, outlength=0x1000): device_handle = KERNEL32.CreateFileA( "\\\\.\\%s" % self.pipepath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, None, OPEN_EXISTING, 0, None) % 2**32 if device_handle == 0xffffffff: # Only report an error if the error is not "name not found", # indicating that no kernel analysis is currently taking place. if KERNEL32.GetLastError() != 2: log.warning("Error opening handle to driver (%s): %d!", driver_name, KERNEL32.GetLastError()) return False out = ctypes.create_string_buffer(outlength) length = ctypes.c_uint() ret = KERNEL32.DeviceIoControl(device_handle, ctlcode, value, len(value), out, ctypes.sizeof(out), ctypes.byref(length), None) KERNEL32.CloseHandle(device_handle) if not ret: log.warning("Error performing ioctl (0x%08x): %d!", ctlcode, KERNEL32.GetLastError()) return False return out.raw[:length.value]
def run(self): seconds = 0 # Global disable flag. if "human" in self.options: self.do_move_mouse = int(self.options["human"]) self.do_click_mouse = int(self.options["human"]) self.do_click_buttons = int(self.options["human"]) else: self.do_move_mouse = True self.do_click_mouse = True self.do_click_buttons = True # Per-feature enable or disable flag. if "human.move_mouse" in self.options: self.do_move_mouse = int(self.options["human.move_mouse"]) if "human.click_mouse" in self.options: self.do_click_mouse = int(self.options["human.click_mouse"]) if "human.click_buttons" in self.options: self.do_click_buttons = int(self.options["human.click_buttons"]) while self.do_run: if seconds and not seconds % 60: USER32.EnumWindows(EnumWindowsProc(get_office_window), 0) if self.do_click_mouse: click_mouse() if self.do_move_mouse: move_mouse() if self.do_click_buttons: USER32.EnumWindows(EnumWindowsProc(foreach_window), 0) KERNEL32.Sleep(1000) seconds += 1
def run(self): """Run analysis. @return: operation status. """ 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: __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() # Enumerate the abstract subclasses. try: package_class = Package.__subclasses__()[0] except IndexError as e: raise CuckooError("Unable to select package class " "(package={0}): {1}".format(package_name, e)) # Initialize the analysis package. 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 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: 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) # Inform zer0m0n of the ResultServer address. zer0m0n.resultserver(self.config.ip, self.config.port) # Forward the command pipe and logpipe names on to zer0m0n. zer0m0n.cmdpipe(self.config.pipe) zer0m0n.channel(self.config.logpipe) # Hide the Cuckoo Analyzer & Cuckoo Agent. zer0m0n.hidepid(self.pid) zer0m0n.hidepid(self.ppid) # Initialize zer0m0n with our compiled Yara rules. zer0m0n.yarald("bin/rules.yarac") # Propagate the requested dump interval, if set. zer0m0n.dumpint(int(self.config.options.get("dumpint", "0"))) # Start analysis package. If for any reason, the execution of the # analysis package fails, we have to abort the analysis. pids = self.package.start(self.target) # 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 while self.do_run: self.time_counter += 1 if self.time_counter == int(self.config.timeout): 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: # We also track the PIDs provided by zer0m0n. self.process_list.add_pids(zer0m0n.getpids()) 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) # Dump all the notified files. self.files.dump_files() # Hell yeah. log.info("Analysis completed.") return True
def run(self): buf = create_string_buffer(BUFSIZE) bytes_read = c_uint() pid = c_uint() # The first four bytes indicate the process identifier. In case the # pipe handle is closed in an unknown way, reopening one and # specifying the same process identifier will reuse the same socket, # thus making it look like as if it was never closed in the first # place. success = KERNEL32.ReadFile(self.pipe_handle, byref(pid), sizeof(pid), byref(bytes_read), None) if not success or bytes_read.value != sizeof(pid): log.warning("Unable to read the process identifier of this " "log pipe instance.") KERNEL32.CloseHandle(self.pipe_handle) return if self.active.get(pid.value): log.warning("A second log pipe handler for an active process is " "being requested, denying request.") KERNEL32.CloseHandle(self.pipe_handle) return if pid.value: sock = self.sockets.get(pid.value) if not sock: sock = socket.create_connection(self.destination) self.sockets[pid.value] = sock self.active[pid.value] = True else: sock = socket.create_connection(self.destination) open_handles.add(sock) while self.do_run: success = KERNEL32.ReadFile(self.pipe_handle, byref(buf), sizeof(buf), byref(bytes_read), None) if success or KERNEL32.GetLastError() == ERROR_MORE_DATA: try: sock.sendall(buf.raw[:bytes_read.value]) except socket.error as e: if e.errno != errno.EBADF: log.warning("Failed socket operation: %s", e) break # If we get the broken pipe error then this pipe connection has # been terminated for one reason or another. So break from the # loop and make the socket "inactive", that is, another pipe # connection can in theory pick it up. (This will only happen in # cases where malware for some reason broke our pipe connection). elif KERNEL32.GetLastError() == ERROR_BROKEN_PIPE: break else: log.warning("The log pipe handler has failed, last error %d.", KERNEL32.GetLastError()) break if pid.value: self.active[pid.value] = False
def click(hwnd): USER32.SetForegroundWindow(hwnd) KERNEL32.Sleep(1000) USER32.SendMessageW(hwnd, BM_CLICK, 0, 0)
def Close(self): KERNEL32.CloseHandle(self)
def get_system_info(self): """Get system information.""" self.system_info = SYSTEM_INFO() KERNEL32.GetSystemInfo(byref(self.system_info))
def open_process(self): """Open a process handle.""" return KERNEL32.OpenProcess(PROCESS_ALL_ACCESS, False, self.pid)
def open_thread(self): """Open a thread handle.""" return KERNEL32.OpenThread(THREAD_ALL_ACCESS, False, self.tid)