def enumerate_threads_setup_owners(self): # Enumerating threads is a special operation concerning the owner process. # We may not be able to retrieve the name of the owning process by normal way # (as we need to get a handle on the process) # So, this implementation of enumerate_thread also setup the owner with the result of enumerate_processes dbgprint("Enumerating threads with CreateToolhelp32Snapshot and setup owner", "SLOW") # One snap for both enum to be prevent race snap = winproxy.CreateToolhelp32Snapshot(gdef.TH32CS_SNAPTHREAD | gdef.TH32CS_SNAPPROCESS, 0) process_entry = gdef.PROCESSENTRY32() process_entry.dwSize = ctypes.sizeof(process_entry) winproxy.Process32First(snap, process_entry) processes = [] processes.append(process.WinProcess._from_PROCESSENTRY32(process_entry)) while winproxy.Process32Next(snap, process_entry): processes.append(process.WinProcess._from_PROCESSENTRY32(process_entry)) # Forge a dict pid -> process proc_dict = {proc.pid: proc for proc in processes} thread_entry = gdef.THREADENTRY32() thread_entry.dwSize = ctypes.sizeof(thread_entry) threads = [] winproxy.Thread32First(snap, thread_entry) parent = proc_dict[thread_entry.th32OwnerProcessID] threads.append(process.WinThread._from_THREADENTRY32(thread_entry, owner=parent)) while winproxy.Thread32Next(snap, thread_entry): parent = proc_dict[thread_entry.th32OwnerProcessID] threads.append(process.WinThread._from_THREADENTRY32(thread_entry, owner=parent)) winproxy.CloseHandle(snap) return threads
def _open_key(self, handle, name, sam): result = WinRegistryKey() winproxy.RegOpenKeyExW(handle, name, 0, sam, result) dbgprint( u"Opening registry key <{0}> (handle={1:#x})".format( name, result.value), "REGISTRY") return result
def __del__(self): if sys is None or sys.path is None: # Late shutdown (not sur winproxy is still up) return if self: # Not NULL handle ? dbgprint(u"Closing registry key handle {0:#x}".format(self.value), 'REGISTRY') self._close_function(self)
def find_alpc_endpoint_and_connect(targetiid, version=(1, 0), sid=gdef.WinLocalSystemSid): """Ask the EPMapper for ALPC endpoints of ``targetiid:version`` and connect to one of them. :param str targetiid: The IID of the requested interface :param (int,int) version: The version requested interface :param WELL_KNOWN_SID_TYPE sid: The SID used to request the EPMapper :returns: A connected :class:`~windows.rpc.RPCClient` """ dbgprint("Finding ALPC endpoints for <{0}>".format(targetiid), "RPC") alpctowers = find_alpc_endpoints(targetiid, version, nb_response=50, sid=sid) dbgprint("ALPC endpoints list: <{0}>".format(alpctowers), "RPC") for tower in alpctowers: dbgprint("Trying to connect to endpoint <{0}>".format(tower.endpoint), "RPC") alpc_port = r"\RPC Control\{0}".format(tower.endpoint.decode()) try: client = windows.rpc.RPCClient(alpc_port) except Exception as e: dbgprint( "Could not connect to endpoint <{0}>: {1}".format( tower.endpoint, e), "RPC") continue break else: raise ValueError( "Could not find a valid endpoint for target <{0}> version <{1}>". format(targetiid, version)) dbgprint('Connected to ALPC port "{0}"'.format(alpc_port), "RPC") return client
def _resolve(self, addr, target): dbgprint("Resolving <{0}> in <{1}>".format(addr, target), "DBG") if not isinstance(addr, basestring): return addr dll, api = addr.split("!") dll = dll.lower() modules = self._module_by_process[target.pid] mod = None if dll in modules: mod = [modules[dll]] if not mod: return None # TODO: optim exports are the same for whole system (32 vs 64 bits) # I don't have to reparse the exports each time.. # Try to interpret api as an int try: api_int = int(api, 0) return mod[0].baseaddr + api_int except ValueError: pass exports = mod[0].exports if api not in exports: dbgprint("Error resolving <{0}> in <{1}>".format(addr, target), "DBG") raise ValueError("Unknown API <{0}> in DLL {1}".format(api, dll)) return exports[api]
def __init__(self, *initial_args): dbgprint("Assembling {0}{1}".format(type(self).__name__, initial_args), "X64") for type_encoding in self.encoding: args = list(initial_args) res = [] prefix = [] full_rex = self.default_rex #if hasattr(self, "default_32_bits") and self.default_32_bits: # full_rex = BitArray.from_int(8, 0x48) for element in type_encoding: arg_consum, value, rex = element.accept_arg(args, instr_state(res, prefix, type(self))) if arg_consum is None: break res.append(value) del args[:arg_consum] if rex is not None: full_rex = full_rex | rex else: # if no break if args: # if still args: fail continue dbgprint("Valid encoding found: REX={0:#x}".format(ord(full_rex.dump())), "X64") self.prefix = prefix self.value = sum(res, BitArray(0, "")) if str(full_rex.dump()) != "\x40": self.value = full_rex + self.value return raise ValueError("Cannot encode <{0} {1}>:(".format(type(self).__name__, initial_args))
def __call__(self, f): try: return self.subdecorator(f) except ExportNotFound as e: dbgprint( "Export <{e.func_name}> not found in <{e.api_name}>".format( e=e), "EXPORTNOTFOUND") return None
def read_memory(self, addr, size): """Read ``size`` from ``addr`` :return: The data read :rtype: :class:`str` """ dbgprint('Read CurrentProcess Memory', 'READMEM') buffer = (c_char * size).from_address(addr) return buffer[:]
def _create_key(self, parent, name, sam): result = WinRegistryKey() flags = 0 winproxy.RegCreateKeyExW(parent, name, 0, None, flags, sam, None, result, None) dbgprint( u"Creating registry key <{0}> (handle={1:#x})".format( name, result.value), "REGISTRY") return result
def __del__(self): # sys.path is not None -> check if python shutdown if hasattr(sys, "path") and sys.path is not None and hasattr( self, "_handle") and self._handle: # Prevent some bug where dbgprint might be None when __del__ is called in a closing process dbgprint( "Closing Handle {0} for {1}".format(hex(self._handle), self), "HANDLE") if dbgprint is not None else None self._close_function(self._handle)
def _handle_create_process(self, debug_event): """Handle CREATE_PROCESS_DEBUG_EVENT""" create_process = debug_event.u.CreateProcessInfo # Duplicate handle, so garbage collection of the process/thread does not # break the debug API invariant (those x_event handle are close by the debug API itself) proc_handle = HANDLE() thread_handle = HANDLE() cp_handle = windows.current_process.handle winproxy.DuplicateHandle(cp_handle, create_process.hProcess, cp_handle, ctypes.byref(proc_handle), dwOptions=DUPLICATE_SAME_ACCESS) winproxy.DuplicateHandle(cp_handle, create_process.hThread, cp_handle, ctypes.byref(thread_handle), dwOptions=DUPLICATE_SAME_ACCESS) dbgprint( " Got PROC handle {0:#x}".format(create_process.hProcess, self), "HANDLE") dbgprint(" PROC handle duplicated: {0:#x}".format(proc_handle.value), "HANDLE") dbgprint( " Got THREAD handle {0:#x}".format(create_process.hThread, self), "HANDLE") dbgprint( " THREAD handle duplicated: {0:#x}".format(thread_handle.value), "HANDLE") self.current_process = WinProcess._from_handle(proc_handle.value) self.current_thread = WinThread._from_handle(thread_handle.value) dbgprint("New process: {0}".format(self.current_process), "DBG") self.threads[self.current_thread.tid] = self.current_thread self._explicit_single_step[self.current_thread.tid] = False self._hardware_breakpoint[self.current_thread.tid] = {} self._breakpoint_to_reput[self.current_thread.tid] = [] self.processes[self.current_process.pid] = self.current_process self._watched_pages[self.current_process.pid] = {} #defaultdict(list) self.breakpoints[self.current_process.pid] = {} self._memory_save[self.current_process.pid] = {} self._module_by_process[self.current_process.pid] = {} self._update_debugger_state(debug_event) self._add_exe_to_module_list(create_process) self._setup_pending_breakpoints_new_process(self.current_process) self._setup_pending_breakpoints_new_thread(self.current_thread) with self.DisabledMemoryBreakpoint(): try: return self.on_create_process(create_process) finally: if create_process.hFile: winproxy.CloseHandle(create_process.hFile)
def enumerate_processes(): dbgprint("Enumerating processes with CreateToolhelp32Snapshot", "SLOW") process_entry = gdef.PROCESSENTRY32() process_entry.dwSize = ctypes.sizeof(process_entry) snap = winproxy.CreateToolhelp32Snapshot(gdef.TH32CS_SNAPPROCESS, 0) winproxy.Process32First(snap, process_entry) res = [] res.append(process.WinProcess._from_PROCESSENTRY32(process_entry)) while winproxy.Process32Next(snap, process_entry): res.append(process.WinProcess._from_PROCESSENTRY32(process_entry)) winproxy.CloseHandle(snap) return res
def _from_handle(handle): tid = WinThread._get_thread_id(handle) try: # Really useful ? thread = [t for t in windows.winobject.system.System().threads if t.tid == tid][0] # set AutoHandle _handle thread._handle = handle dbgprint("Thread {0} from handle {1}".format(thread, hex(handle)), "HANDLE") return thread except IndexError: dbgprint("DeadThread from handle {0}".format(hex(handle)), "HANDLE") return DeadThread(handle, tid)
def handle(self): """An handle on the object :type: HANDLE .. note:: The handle is automaticaly closed when the object is destroyed """ if hasattr(self, "_handle"): return self._handle self._handle = self._get_handle() dbgprint("Open handle {0} for {1}".format(hex(self._handle), self), "HANDLE") return self._handle
def handle(self): """An handle on the object :type: HANDLE .. note:: The handle is automaticaly closed when the object is destroyed """ if hasattr(self, "_handle"): return self._handle self._handle = self._get_handle() dbgprint("Open handle {0} for {1}".format(hex(self._handle), self), "HANDLE") #if "DEAD" in str(self): # print("OPEN FOR THE DEADS") # import pdb;pdb.set_trace() return self._handle
def on_exception(self, exception): """Called on exception event other that known breakpoint or requested single step. ``exception`` is one of the following type: * :class:`windows.winobject.exception.EEXCEPTION_DEBUG_INFO32` * :class:`windows.winobject.exception.EEXCEPTION_DEBUG_INFO64` The default behaviour is to return ``DBG_CONTINUE`` for the known exception code and ``DBG_EXCEPTION_NOT_HANDLED`` else """ dbgprint( "Exception: {0} at ".format( exception.ExceptionRecord.ExceptionCode, exception.ExceptionRecord.ExceptionAddress), "DBG") if not exception.ExceptionRecord.ExceptionCode in winexception.exception_name_by_value: return DBG_EXCEPTION_NOT_HANDLED return DBG_CONTINUE
def create_process(path, args=None, dwCreationFlags=0, show_windows=True): """A convenient wrapper arround :func:`windows.winproxy.CreateProcessA`""" proc_info = PROCESS_INFORMATION() lpStartupInfo = None if show_windows: StartupInfo = STARTUPINFOA() StartupInfo.cb = ctypes.sizeof(StartupInfo) StartupInfo.dwFlags = 0 lpStartupInfo = ctypes.byref(StartupInfo) lpCommandLine = None if args: lpCommandLine = (" ".join([str(a) for a in args])) windows.winproxy.CreateProcessA( path, lpCommandLine=lpCommandLine, dwCreationFlags=dwCreationFlags, lpProcessInformation=ctypes.byref(proc_info), lpStartupInfo=lpStartupInfo) dbgprint( "CreateProcessA new process handle {:#x}".format(proc_info.hProcess), "HANDLE") dbgprint( "CreateProcessA new thread handle {:#x}".format(proc_info.hThread), "HANDLE") dbgprint( "Automatic close of thread handle {:#x}".format(proc_info.hThread), "HANDLE") windows.winproxy.CloseHandle( proc_info.hThread ) # Give access to a WinThread in addition of the WinProcess ? return windows.winobject.process.WinProcess(pid=proc_info.dwProcessId, handle=proc_info.hProcess)
def load_dll_in_remote_process(target, dll_name): rpeb = target.peb if rpeb.Ldr: # LDR est parcourable, ca va etre deja plus simple.. modules = rpeb.modules if any(mod.name == dll_name for mod in modules): # DLL already loaded dbgprint("DLL already present in target", "DLLINJECT") return True k32 = [mod for mod in modules if mod.name.lower() == "kernel32.dll"] if k32: # We have kernel32 \o/ k32 = k32[0] try: load_libraryA = k32.pe.exports["LoadLibraryA"] except KeyError: raise ValueError("Kernel32 have no export <LoadLibraryA> (wtf)") with target.allocated_memory(0x1000) as addr: target.write_memory(addr, dll_name + "\x00") t = target.create_thread(load_libraryA, addr) t.wait() dbgprint("DLL Injected via LoadLibray", "DLLINJECT") return True # Hardcore mode # We don't have k32 or PEB->Ldr # Go inject a GetProcAddress(LoadLib) + LoadLib shellcode :D dbgprint("DLL Via manual getproc / loadlib", "DLLINJECT") if target.bitness == 32: return perform_manual_getproc_loadlib_32(target, dll_name) return perform_manual_getproc_loadlib_64(target, dll_name)
def load_dll_in_remote_process(target, dll_name): rpeb = target.peb if rpeb.Ldr: # LDR est parcourable, ca va etre deja plus simple.. modules = rpeb.modules if any(mod.name == dll_name for mod in modules): # DLL already loaded dbgprint("DLL already present in target", "DLLINJECT") return True k32 = [mod for mod in modules if mod.name.lower() == "kernel32.dll"] if k32: # We have kernel32 \o/ k32 = k32[0] try: load_libraryA = k32.pe.exports["LoadLibraryA"] except KeyError: raise ValueError( "Kernel32 have no export <LoadLibraryA> (wtf)") with target.allocated_memory(0x1000) as addr: target.write_memory(addr, dll_name + "\x00") t = target.create_thread(load_libraryA, addr) t.wait() dbgprint("DLL Injected via LoadLibray", "DLLINJECT") return True # Hardcore mode # We don't have k32 or PEB->Ldr # Go inject a GetProcAddress(LoadLib) + LoadLib shellcode :D dbgprint("DLL Via manual getproc / loadlib", "DLLINJECT") if target.bitness == 32: return perform_manual_getproc_loadlib_32(target, dll_name) return perform_manual_getproc_loadlib_64(target, dll_name)
def load_dll_in_remote_process(target, dll_name): # if target.bitness == 64: # import pdb;pdb.set_trace() rpeb = target.peb if rpeb.Ldr: # LDR est parcourable, ca va etre deja plus simple.. modules = rpeb.modules if any(mod.name == dll_name for mod in modules): # DLL already loaded dbgprint("DLL already present in target", "DLLINJECT") return False k32 = [mod for mod in modules if mod.name.lower() == "kernel32.dll"] if k32: # We have kernel32 \o/ k32 = k32[0] try: load_libraryW = k32.pe.exports["LoadLibraryW"] except KeyError: raise ValueError( "Kernel32 have no export <LoadLibraryA> (wtf)") with target.allocated_memory(0x1000) as addr: if target.bitness == 32: target.write_memory(addr, (dll_name + "\x00").encode('utf-16le')) t = target.create_thread(load_libraryW, addr) t.wait() module_baseaddr = t.exit_code else: # For 64b target we need a special stub as the return value of # load_libraryW does not fit in t.exit_code (DWORD) retval_addr = addr target.write_ptr(retval_addr, 0) addr += ctypes.sizeof(ctypes.c_ulonglong) full_dll_name = (dll_name + "\x00").encode('utf-16le') target.write_memory(addr, full_dll_name) param_addr = addr addr += len(full_dll_name) shellcode_addr = addr shellcode = generate_simple_LoadLibraryW_64( load_libraryW, retval_addr) target.write_memory(shellcode_addr, shellcode) t = target.create_thread(shellcode_addr, param_addr) t.wait() module_baseaddr = target.read_ptr(retval_addr) if not module_baseaddr: raise InjectionFailedError( u"Injection of <{0}> failed".format(dll_name)) dbgprint("DLL Injected via LoadLibray", "DLLINJECT") # Cannot return the full return value of load_libraryW in 64b target.. (exit_code is a DWORD) return module_baseaddr # Hardcore mode # We don't have k32 or PEB->Ldr # Go inject a GetProcAddress(LoadLib) + LoadLib shellcode :D dbgprint("DLL Via manual getproc / loadlib", "DLLINJECT") if target.bitness == 32: return perform_manual_getproc_loadlib_32(target, dll_name) return perform_manual_getproc_loadlib_64(target, dll_name)
def enumerate_threads_generator(): # Ptet dangereux, parce que on yield la meme THREADENTRY32 a chaque fois dbgprint("Enumerating threads with CreateToolhelp32Snapshot <generator>", "SLOW") thread_entry = gdef.THREADENTRY32() thread_entry.dwSize = ctypes.sizeof(thread_entry) snap = winproxy.CreateToolhelp32Snapshot(gdef.TH32CS_SNAPTHREAD, 0) dbgprint("New handle CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD) <generator> | {0:#x}".format(snap), "HANDLE") try: winproxy.Thread32First(snap, thread_entry) yield thread_entry while winproxy.Thread32Next(snap, thread_entry): yield thread_entry finally: winproxy.CloseHandle(snap) dbgprint("CLOSE CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD) <generator> | {0:#x}".format(snap), "HANDLE")
def create_process(path, args=None, dwCreationFlags=0, show_windows=True): """A convenient wrapper arround :func:`windows.winproxy.CreateProcessA`""" proc_info = PROCESS_INFORMATION() lpStartupInfo = None if show_windows: StartupInfo = STARTUPINFOW() StartupInfo.cb = ctypes.sizeof(StartupInfo) StartupInfo.dwFlags = 0 lpStartupInfo = ctypes.byref(StartupInfo) lpCommandLine = None if isinstance(path, bytes): path = path.decode() if args: unicode_args = [] for arg in args: if isinstance(arg, bytes): arg = arg.decode() unicode_args.append(arg) lpCommandLine = (" ".join(unicode_args)) windows.winproxy.CreateProcessW( path, lpCommandLine=lpCommandLine, dwCreationFlags=dwCreationFlags, lpProcessInformation=ctypes.byref(proc_info), lpStartupInfo=lpStartupInfo) dbgprint( "CreateProcessW new process handle {:#x}".format(proc_info.hProcess), "HANDLE") dbgprint( "CreateProcessW new thread handle {:#x}".format(proc_info.hThread), "HANDLE") dbgprint( "Automatic close of thread handle {:#x}".format(proc_info.hThread), "HANDLE") windows.winproxy.CloseHandle( proc_info.hThread ) # Give access to a WinThread in addition of the WinProcess ? return windows.winobject.process.WinProcess(pid=proc_info.dwProcessId, handle=proc_info.hProcess)
def load_dll_in_remote_process(target, dll_name): rpeb = target.peb if rpeb.Ldr: # LDR est parcourable, ca va etre deja plus simple.. modules = rpeb.modules if any(mod.name == dll_name for mod in modules): # DLL already loaded dbgprint("DLL already present in target", "DLLINJECT") return False k32 = [mod for mod in modules if mod.name.lower() == "kernel32.dll"] if k32: # We have kernel32 \o/ k32 = k32[0] try: load_libraryW = k32.pe.exports["LoadLibraryW"] except KeyError: raise ValueError( "Kernel32 have no export <LoadLibraryA> (wtf)") with target.allocated_memory(0x1000) as addr: target.write_memory(addr, (dll_name + "\x00").encode('utf-16le')) t = target.create_thread(load_libraryW, addr) t.wait() if not t.exit_code: raise InjectionFailedError( u"Injection of <{0}> failed".format(dll_name)) dbgprint("DLL Injected via LoadLibray", "DLLINJECT") # Cannot return the full return value of load_libraryW in 64b target.. (exit_code is a DWORD) return t.exit_code # Hardcore mode # We don't have k32 or PEB->Ldr # Go inject a GetProcAddress(LoadLib) + LoadLib shellcode :D dbgprint("DLL Via manual getproc / loadlib", "DLLINJECT") if target.bitness == 32: return perform_manual_getproc_loadlib_32(target, dll_name) return perform_manual_getproc_loadlib_64(target, dll_name)
def read_memory(self, addr, size): """Read size from adddr""" dbgprint('Read CurrentProcess Memory', 'READMEM') buffer = (c_char * size).from_address(addr) return buffer[:]
def __del__(self): if hasattr(self, "_handle") and self._handle: dbgprint("Closing Handle {0} for {1}".format(hex(self._handle), self), "HANDLE") self._close_function(self._handle)
def __call__(self, f): try: return self.subdecorator(f) except ExportNotFound as e: dbgprint("Export <{e.func_name}> not found in <{e.api_name}>".format(e=e), "EXPORTNOTFOUND") return None