import os from os.path import realpath, dirname import struct import itertools import functools import ctypes from ctypes import byref, WINFUNCTYPE, HRESULT, WinError from simple_com import COMInterface, IDebugOutputCallbacksVtable import resource_emulation import driver_upgrade from driver_upgrade import DU_MEMALLOC_IOCTL, DU_KCALL_IOCTL, DU_OUT_IOCTL, DU_IN_IOCTL import windows import windows.hooks import windows.winproxy as winproxy from windows.generated_def.winstructs import * from dbgdef import * from dbgtype import DbgEngType # Based on the trick used in PRAW # http://stackoverflow.com/a/22023805 IS_SPHINX_BUILD = bool(os.environ.get('SPHINX_BUILD', '0')) # The COM Interfaces we need for the LocalKernelDebugger class IDebugClient(COMInterface): _functions_ = { "QueryInterface": ctypes.WINFUNCTYPE(HRESULT, PVOID, PVOID)(0, "QueryInterface"), "AddRef": ctypes.WINFUNCTYPE(HRESULT)(1, "AddRef"), "Release": ctypes.WINFUNCTYPE(HRESULT)(2, "Release"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff538145%28v=vs.85%29.aspx "AttachKernel": ctypes.WINFUNCTYPE(HRESULT, ULONG, c_char_p)(3, "AttachKernel"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff541851%28v=vs.85%29.aspx "DetachProcesses": WINFUNCTYPE(HRESULT)(25, "DetachProcesses"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff543004%28v=vs.85%29.aspx "EndSession": WINFUNCTYPE(HRESULT, c_ulong)(26, "EndSession"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff556751%28v=vs.85%29.aspx "SetOutputCallbacks": ctypes.WINFUNCTYPE(HRESULT, c_void_p)(34, "SetOutputCallbacks"), } class IDebugDataSpaces(COMInterface): _functions_ = { "QueryInterface": ctypes.WINFUNCTYPE(HRESULT, PVOID, PVOID)(0, "QueryInterface"), "AddRef": ctypes.WINFUNCTYPE(HRESULT)(1, "AddRef"), "Release": ctypes.WINFUNCTYPE(HRESULT)(2, "Release"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff554359%28v=vs.85%29.aspx "ReadVirtual": WINFUNCTYPE(HRESULT, ULONG64, PVOID, ULONG, PULONG)(3, "ReadVirtual"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff561468%28v=vs.85%29.aspx "WriteVirtual": WINFUNCTYPE(HRESULT, ULONG64, PVOID, ULONG, PULONG)(4, "WriteVirtual"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff554310%28v=vs.85%29.aspx "ReadPhysical": WINFUNCTYPE(HRESULT, ULONG64, PVOID, ULONG, PULONG)(10, "ReadPhysical"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff561432%28v=vs.85%29.aspx "WritePhysical": WINFUNCTYPE(HRESULT, ULONG64, PVOID, ULONG, PULONG)(11, "WritePhysical"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff553573%28v=vs.85%29.aspx "ReadIo": WINFUNCTYPE(HRESULT, ULONG, ULONG, ULONG, ULONG64, PVOID, ULONG, PULONG)(14, "ReadIo"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff561402%28v=vs.85%29.aspx "WriteIo": WINFUNCTYPE(HRESULT, ULONG, ULONG, ULONG, ULONG64, PVOID, ULONG, PULONG)(15, "WriteIo"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff554289%28v=vs.85%29.aspx "ReadMsr": WINFUNCTYPE(HRESULT, ULONG, PULONG64)(16, "ReadMsr"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff561424%28v=vs.85%29.aspx "WriteMsr": WINFUNCTYPE(HRESULT, ULONG, ULONG64)(17, "WriteMsr"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff553519%28v=vs.85%29.aspx "ReadBusData": WINFUNCTYPE(HRESULT, ULONG, ULONG, ULONG, ULONG, PVOID, ULONG, PULONG)(18, "ReadBusData"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff561371%28v=vs.85%29.aspx "WriteBusData": WINFUNCTYPE(HRESULT, ULONG, ULONG, ULONG, ULONG, PVOID, ULONG, PULONG)(19, "WriteBusData"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff554326%28v=vs.85%29.aspx "ReadProcessorSystemData": WINFUNCTYPE(HRESULT, ULONG, ULONG, PVOID, ULONG, PULONG)(22, "ReadProcessorSystemData"), } class IDebugDataSpaces2(COMInterface): _functions_ = dict(IDebugDataSpaces._functions_) _functions_.update({ # https://msdn.microsoft.com/en-us/library/windows/hardware/ff560335%28v=vs.85%29.aspx "VirtualToPhysical": WINFUNCTYPE(HRESULT, ULONG64, PULONG64)(23, "VirtualToPhysical"), }) # https://msdn.microsoft.com/en-us/library/windows/hardware/ff550856%28v=vs.85%29.aspx class IDebugSymbols(COMInterface): _functions_ = { "QueryInterface": ctypes.WINFUNCTYPE(HRESULT, PVOID, PVOID)(0, "QueryInterface"), "AddRef": ctypes.WINFUNCTYPE(HRESULT)(1, "AddRef"), "Release": ctypes.WINFUNCTYPE(HRESULT)(2, "Release"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff556798%28v=vs.85%29.aspx "SetSymbolOption": WINFUNCTYPE(HRESULT, ctypes.c_ulong)(6, "SetSymbolOption"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff547183%28v=vs.85%29.aspx "GetNameByOffset": WINFUNCTYPE(HRESULT, ULONG64, PVOID, ULONG, PULONG, PULONG64)(7, "GetNameByOffset"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff548035%28v=vs.85%29.aspx "GetOffsetByName": WINFUNCTYPE(HRESULT, c_char_p, POINTER(ctypes.c_uint64))(8, "GetOffsetByName"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff547927%28v=vs.85%29.aspx "GetNumberModules": WINFUNCTYPE(HRESULT, LPDWORD, LPDWORD)(12, "GetNumberModules"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff547080%28v=vs.85%29.aspx "GetModuleByIndex": WINFUNCTYPE(HRESULT, DWORD, PULONG64)(13, "GetModuleByIndex"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff547146%28v=vs.85%29.aspx "GetModuleNames": WINFUNCTYPE(HRESULT, DWORD, c_uint64, PVOID, DWORD, LPDWORD, PVOID, DWORD, LPDWORD, PVOID, DWORD, LPDWORD)(16, "GetModuleNames"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff549408%28v=vs.85%29.aspx "GetTypeName": WINFUNCTYPE(HRESULT, ULONG64, ULONG, PVOID, ULONG, PULONG)(19, "GetTypeName"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff549376%28v=vs.85%29.aspx "GetTypeId": WINFUNCTYPE(HRESULT, ULONG64, c_char_p, PULONG)(20, "GetTypeId"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff549457%28v=vs.85%29.aspx "GetTypeSize": WINFUNCTYPE(HRESULT, ULONG64, ULONG, PULONG)(21, "GetTypeSize"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff546763%28v=vs.85%29.aspx "GetFieldOffset": WINFUNCTYPE(HRESULT, ULONG64, ULONG, c_char_p, PULONG)(22, "GetFieldOffset"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff549173%28v=vs.85%29.aspx "GetSymbolTypeId": WINFUNCTYPE(HRESULT, c_char_p, PULONG, PULONG64)(23, "GetSymbolTypeId"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff558815%28v=vs.85%29.aspx "StartSymbolMatch": WINFUNCTYPE(HRESULT, c_char_p, PULONG64)(36, "StartSymbolMatch"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff547856%28v=vs.85%29.aspx "GetNextSymbolMatch": WINFUNCTYPE(HRESULT, ULONG64, PVOID, ULONG, PULONG, PULONG64)(37, "GetNextSymbolMatch"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff543008%28v=vs.85%29.aspx "EndSymbolMatch": WINFUNCTYPE(HRESULT, ULONG64)(38, "EndSymbolMatch"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff554379%28v=vs.85%29.aspx "Reload": WINFUNCTYPE(HRESULT, c_char_p)(39, "Reload"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff556802%28v=vs.85%29.aspx "SetSymbolPath": WINFUNCTYPE(HRESULT, c_char_p)(41, "SetSymbolPath"), } class IDebugSymbols2(COMInterface): _functions_ = dict(IDebugSymbols._functions_) _functions_.update({ # https://msdn.microsoft.com/en-us/library/windows/hardware/ff546747%28v=vs.85%29.aspx "GetFieldName": WINFUNCTYPE(HRESULT, ULONG64, ULONG, ULONG, PVOID, ULONG, PULONG)(55, "GetFieldName"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff546771%28v=vs.85%29.aspx "GetFieldTypeAndOffset": WINFUNCTYPE(HRESULT, ULONG64, ULONG, c_char_p, PULONG, PULONG)(105, "GetFieldTypeAndOffset"), }) class IDebugSymbols3(COMInterface): _functions_ = dict(IDebugSymbols2._functions_) _functions_.update({ }) class IDebugControl(COMInterface): _functions_ = { "QueryInterface": ctypes.WINFUNCTYPE(HRESULT, PVOID, PVOID)(0, "QueryInterface"), "AddRef": ctypes.WINFUNCTYPE(HRESULT)(1, "AddRef"), "Release": ctypes.WINFUNCTYPE(HRESULT)(2, "Release"), "GetInterrupt": ctypes.WINFUNCTYPE(HRESULT)(3, "GetInterrupt"), "SetInterrupt": ctypes.WINFUNCTYPE(HRESULT, ULONG)(4, "SetInterrupt"), "GetExecutionStatus": ctypes.WINFUNCTYPE(HRESULT, PULONG)(49, "GetExecutionStatus"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff543208%28v=vs.85%29.aspx "Execute": ctypes.WINFUNCTYPE(HRESULT, ULONG, c_char_p, ULONG)(66, "Execute"), # https://msdn.microsoft.com/en-us/library/windows/hardware/ff561229%28v=vs.85%29.aspx "WaitForEvent": WINFUNCTYPE(HRESULT, DWORD, DWORD)(93, "WaitForEvent") } class DummyIATEntry(ctypes.Structure): _fields_ = [ ("value", DWORD)] @classmethod def create(cls, addr, dll, name): self = cls.from_address(addr) self.addr = addr self.hook = None self.nonhookvalue = windows.utils.get_func_addr(dll, name) return self def set_hook(self, callback, types=None): hook = windows.hooks.IATHook(self, callback, types) self.hook = hook hook.enable() return hook @windows.hooks.GetModuleFileNameWCallback def EmulateWinDBGName(hModule, lpFilename, nSize, real_function): if hModule is not None: return real_function() ptr_addr = ctypes.cast(lpFilename, ctypes.c_void_p).value v = (c_char * 100).from_address(ptr_addr) path = "C:\\windbg.exe" path_wchar = "\x00".join(path) + "\x00\x00\x00" v[0:len(path_wchar)] = path_wchar return len(path_wchar) def require_upgraded_driver(f): if IS_SPHINX_BUILD: if f.__doc__.strip().startswith("| "): nextline = "" else: nextline = "| " # Strip all leading space for rst parsing by sphinx new_doc = "| <require upgraded driver>\n" + nextline + f.__doc__ + "\n" new_doc = "\n".join([l.strip() for l in new_doc.split("\n")]) f.__doc__ = new_doc return f @functools.wraps(f) def wrapper(self, *args, **kwargs): if not hasattr(self, 'upgrader') or not self.upgrader.is_upgraded: raise ValueError('Cannot call {0} without upgraded driver'.format(f.__name__)) return f(self, *args, **kwargs) return wrapper # experimental decorator # Just used to inform you that we are not sur if this code really works # and that we are currently working on it # (yes dbgengine type API is not simple nor complete) def experimental(f): if IS_SPHINX_BUILD: f._do_not_generate_doc = True return f class LocalKernelDebuggerBase(object): DEBUG_DLL_PATH = None DRIVER_FILENAME = None DRIVER_RESOURCE = None # Will be used if '_NT_SYMBOL_PATH' is not set DEFAULT_SYMBOL_PATH = "SRV*{0}\\symbols*http://msdl.microsoft.com/download/symbols".format(realpath(dirname(__file__))) SYMBOL_OPT = None def __init__(self, quiet=True): self.quiet = quiet self._output_string = "" self._output_callback = None self._load_debug_dll() self.DebugClient = self._do_debug_create() self._do_kernel_attach() self._ask_other_interface() self._setup_symbols_options() self.set_output_callbacks(self._standard_output_callback) self._wait_local_kernel_connection() self._load_modules_syms() self.reload() self._init_dbghelp_func() self._upgrade_driver() def _setup_driver_resource(self, dbgengmod, k32import): raise NotImplementedError("_setup_driver_resource") def _setup_name_imposture(self, dbgengmod, k32import): raise NotImplementedError("_setup_name_imposture") # Change current process name via GetModuleFileNameW hook def _setup_windbg_imposture(self): dbgengmod = [i for i in windows.current_process.peb.modules if i.name == "dbgeng.dll"][0] k32import = dbgengmod.pe.imports['kernel32.dll'] self._setup_driver_resource(dbgengmod, k32import) self._setup_name_imposture(dbgengmod, k32import) def _do_kernel_attach(self): self._setup_windbg_imposture() res = self.DebugClient.AttachKernel(DEBUG_ATTACH_LOCAL_KERNEL, None) if res: raise WinError(res) def _load_debug_dll(self): self.hmoduledbghelp = winproxy.LoadLibraryA(self.DEBUG_DLL_PATH + "dbghelp.dll") self.hmoduledbgeng = winproxy.LoadLibraryA(self.DEBUG_DLL_PATH + "dbgeng.dll") self.hmodulesymsrv = winproxy.LoadLibraryA(self.DEBUG_DLL_PATH + "symsrv.dll") def _do_debug_create(self): DebugClient = IDebugClient(0) DebugCreateAddr = winproxy.GetProcAddress(self.hmoduledbgeng, "DebugCreate") DebugCreate = WINFUNCTYPE(HRESULT, PVOID, PVOID)(DebugCreateAddr) DebugCreate(IID_IDebugClient, byref(DebugClient)) return DebugClient def _ask_other_interface(self): DebugClient = self.DebugClient self.DebugDataSpaces = IDebugDataSpaces2(0) self.DebugSymbols = IDebugSymbols3(0) self.DebugControl = IDebugControl(0) DebugClient.QueryInterface(IID_IDebugDataSpaces2, ctypes.byref(self.DebugDataSpaces)) DebugClient.QueryInterface(IID_IDebugSymbols3, ctypes.byref(self.DebugSymbols)) DebugClient.QueryInterface(IID_IDebugControl, ctypes.byref(self.DebugControl)) def _wait_local_kernel_connection(self): self.DebugControl.WaitForEvent(0, 0xffffffff) return True def _setup_symbols_options(self): try: symbol_path = os.environ['_NT_SYMBOL_PATH'] except KeyError: symbol_path = self.DEFAULT_SYMBOL_PATH self.DebugSymbols.SetSymbolPath(symbol_path) self.DebugSymbols.SetSymbolOption(self.SYMBOL_OPT) def get_number_modules(self): """Get the number of loaded and unloaded modules :returns: Number of loaded, unloaded modules -- int, int """ numModulesLoaded = DWORD(0) numModulesUnloaded = DWORD(0) self.DebugSymbols.GetNumberModules(byref(numModulesLoaded), byref(numModulesUnloaded)) return (numModulesLoaded.value, numModulesUnloaded.value) def get_module_by_index(self, i): """Get the base of module number **i**""" currModuleBase = ULONG64(0) self.DebugSymbols.GetModuleByIndex(i, byref(currModuleBase)) return self.trim_ulong64_to_address(currModuleBase.value) def get_module_name_by_index(self, i): """Get the name of module number **i**""" currModuleBase = ULONG64(0) currModuleName = (c_char * 1024)() currImageName = (c_char * 1024)() currLoadedImageName = (c_char * 1024)() currModuleNameSize = DWORD(0) currImageNameSize = DWORD(0) currLoadedImageNameSize = DWORD(0) self.DebugSymbols.GetModuleByIndex(i, byref(currModuleBase)) self.DebugSymbols.GetModuleNames(i, currModuleBase, byref(currImageName), 1023, byref(currImageNameSize), byref(currModuleName), 1023, byref(currModuleNameSize), byref(currLoadedImageName), 1023, byref(currLoadedImageNameSize)) return (currImageName.value, currModuleName.value, currLoadedImageName.value) def _load_modules_syms(self): currModuleName = (c_char * 1024)() currImageName = (c_char * 1024)() currLoadedImageName = (c_char * 1024)() currModuleNameSize = DWORD(0) currImageNameSize = DWORD(0) currLoadedImageNameSize = DWORD(0) currModuleBase = ULONG64(0) numModulesLoaded = DWORD(0) numModulesUnloaded = DWORD(0) self.DebugSymbols.GetNumberModules(byref(numModulesLoaded), byref(numModulesUnloaded)) for i in range(numModulesLoaded.value): self.DebugSymbols.GetModuleByIndex(i, byref(currModuleBase)) self.DebugSymbols.GetModuleNames(i, currModuleBase, byref(currImageName), 1023, byref(currImageNameSize), byref(currModuleName), 1023, byref(currModuleNameSize), byref(currLoadedImageName), 1023, byref(currLoadedImageNameSize)) self.DebugSymbols.Reload(currModuleName[:currModuleNameSize.value]) # TODO: SymGetTypeInfo goto winfunc? def _init_dbghelp_func(self): # We need to hack our way to some dbghelp functions # Some info are not reachable through COM API # 0xf0f0f0f0 is the magic handler used by dbgengine for dbghelp dbghelp = ctypes.windll.DbgHelp SymGetTypeInfoPrototype = WINFUNCTYPE(BOOL, HANDLE, DWORD64, ULONG, IMAGEHLP_SYMBOL_TYPE_INFO, PVOID) SymGetTypeInfoParams = ((1, 'hProcess'), (1, 'ModBase'), (1, 'TypeId'), (1, 'GetType'), (1, 'pInfo')) self.SymGetTypeInfo_ctypes = SymGetTypeInfoPrototype(("SymGetTypeInfo", dbghelp), SymGetTypeInfoParams) # Internal helper def resolve_symbol(self, symbol): """| Return **symbol** if it's an :class:`int` else resolve it using :func:`get_symbol_offset` | Used by functions to either accept an :class:`int` or a windbg :class:`Symbol`""" if isinstance(symbol, (int, long)): return symbol x = self.get_symbol_offset(symbol) if x is None: raise ValueError("Unknow symbol <{0}>".format(symbol)) return x def resolve_type(self, imodule, itype): """| Return **imodule** and **itype** if they are an :class:`int` else | resolve then using respectively :func:`get_symbol_offset` and :func:`get_type_id` | Used by functions about types to either accept :class:`int` or windbg :class:`Symbol` """ module = self.resolve_symbol(imodule) if isinstance(itype, (int, long)): return self.expand_address_to_ulong64(module), itype try: type = self.get_type_id(module, itype) except WindowsError: raise ValueError("Unkown type: <{0}!{1}>".format(imodule, itype)) return self.expand_address_to_ulong64(module), type def _get_kldbgdrv_handle(self): if not hasattr(self, "_kldbgdrv_handle"): self._kldbgdrv_handle = windows.winproxy.CreateFileA("\\\\.\\kldbgdrv", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ) return self._kldbgdrv_handle # Actual Interface def execute(self, str, to_string=False): r"""| Execute a windbg command | if **to_string** is False, use the current output callback | (see :file:`example\\output_demo.py`) """ if to_string: old_output = self._output_callback self._init_string_output_callback() self.DebugControl.Execute(0, str, 0) if to_string: if old_output is None: old_output = self._standard_output_callback self.set_output_callbacks(old_output) return self._output_string return None def _standard_output_callback(self, x, y, msg): if not self.quiet: print msg, return 0 def _init_string_output_callback(self): self._output_string = "" self.set_output_callbacks(self._string_output_callback) def _string_output_callback(self, x, y, msg): self._output_string += msg return 0 def set_output_callbacks(self, callback): r"""| Register a new output callback, that must respect the interface of | :func:`IDebugOutputCallbacks::Output` `<https://msdn.microsoft.com/en-us/library/windows/hardware/ff550815%28v=vs.85%29.aspx>`_. | (see :file:`example\\output_demo.py`) """ self._output_callback = callback my_idebugoutput_vtable = IDebugOutputCallbacksVtable.create_vtable(Output=callback) my_debugoutput_obj = ctypes.pointer(my_idebugoutput_vtable) res = self.DebugClient.SetOutputCallbacks(ctypes.addressof(my_debugoutput_obj)) # Need to keep reference to these object else our output callback will be # garbage collected leading to crash # Update self.keep_alive AFTER the call to SetOutputCallbacks because # SetOutputCallbacks may call methods of the old my_debugoutput_obj self.keep_alive = [my_idebugoutput_vtable, my_debugoutput_obj] return res def get_modules(self): """Return a list of (currModuleName, currImageName, currLoadedImageName)""" self.reload("") currModuleName = (c_char * 1024)() currImageName = (c_char * 1024)() currLoadedImageName = (c_char * 1024)() currModuleNameSize = DWORD(0) currImageNameSize = DWORD(0) currLoadedImageNameSize = DWORD(0) currModuleBase = ULONG64(0) numModulesLoaded = DWORD(0) numModulesUnloaded = DWORD(0) self.DebugSymbols.GetNumberModules(byref(numModulesLoaded), byref(numModulesUnloaded)) res = [] for i in range(numModulesLoaded.value): self.DebugSymbols.GetModuleByIndex(i, byref(currModuleBase)) self.DebugSymbols.GetModuleNames(i, c_uint64(currModuleBase.value), byref(currImageName), 1023, byref(currImageNameSize), byref(currModuleName), 1023, byref(currModuleNameSize), byref(currLoadedImageName), 1023, byref(currLoadedImageNameSize)) # Removing trailing \x00 res.append((currModuleName[:currModuleNameSize.value - 1], currImageName[:currImageNameSize.value - 1], currLoadedImageName[:currLoadedImageNameSize.value - 1])) return res def reload(self, module_to_reload=""): """Reload a module or all modules if **module_to_reload** is not specified""" return self.DebugSymbols.Reload(module_to_reload) def detach(self): """End the Debugging session and detach the COM interface""" self.DebugClient.EndSession(DEBUG_END_PASSIVE) self.DebugClient.DetachProcesses() self.DebugClient.Release() del self.DebugClient self.DebugSymbols.Release() del self.DebugSymbols self.DebugControl.Release() del self.DebugControl self.DebugDataSpaces.Release() del self.DebugDataSpaces def current_processor(self): """:returns: The number of the processor we are currently on -- :class:`int`""" return windows.winproxy.GetCurrentProcessorNumber() def set_current_processor(self, proc_nb): """Set the processor we want to be executed on :param proc_nb: the number of the processor :type proc_nb: int""" return windows.winproxy.SetThreadAffinityMask(dwThreadAffinityMask=(1 << proc_nb)) def number_processor(self): """:returns: The number of processors on the machine -- :class:`int`""" return self.read_dword("nt!KeNumberProcessors") def on_each_processor(self): """Iter execution on every processor :yield: current processor number""" for nb_proc in self.number_processor(): self.set_current_processor(nb_proc) yield nb_proc # type stuff @experimental def get_type(self, module, typeid): module, typeid = self.resolve_type(module, typeid) return DbgEngType(module, typeid, self) def get_type_id(self, module, type_name): """Get the typeid of a type :param module: the module containing the type :type module: Symbol :param type_name: the name of the type :type type_name: str :rtype: int""" module = self.resolve_symbol(module) res = ULONG(0) self.DebugSymbols.GetTypeId(self.expand_address_to_ulong64(module), type_name, byref(res)) return res.value def get_symbol_type_id(self, symtype): """Get the module and typeid of a symbol :param symtype: the name of the type :type symtype: str :rtype: int, int -- module ID, type ID""" typeid = ULONG(0) module = ULONG64(0) self.DebugSymbols.GetSymbolTypeId(symtype, byref(typeid), byref(module)) return (module.value, typeid.value) def get_field_offset(self, module, typeid, field): """Get the offset of a field in a type :rtype: int""" module, typeid = self.resolve_type(module, typeid) res = ULONG(0) self.DebugSymbols.GetFieldOffset(module, typeid, field, byref(res)) return res.value def get_type_name(self, module, typeid): """Get the name of a type :rtype: str""" module, typeid = self.resolve_type(module, typeid) buffer_size = 1024 buffer = (c_char * buffer_size)() name_size = ULONG(0) self.DebugSymbols.GetTypeName(module, typeid, byref(buffer), buffer_size, byref(name_size)) res = buffer[:name_size.value] if res[-1] == "\x00": res = res[:-1] return res def get_type_size(self, module, typeid): """Get the size of a type :rtype: int""" module, typeid = self.resolve_type(module, typeid) res = ULONG(0) self.DebugSymbols.GetTypeSize(module, typeid, byref(res)) return res.value def get_field_name(self, module, typeid, fieldindex): """Get the name of a field in a type :param fieldindex: Index of the field to retrieve :type fieldindex: int :rtype: int""" module, typeid = self.resolve_type(module, typeid) buffer_size = 1024 buffer = (c_char * buffer_size)() name_size = ULONG(0) self.DebugSymbols.GetFieldName(module, typeid, fieldindex, byref(buffer), buffer_size, byref(name_size)) res = buffer[:name_size.value] if res[-1] == "\x00": res = res[:-1] return res def get_field_type_and_offset(self, module, typeid, fieldname): """Get the type and the offset of a field in a type :param fieldname: The name of the field we want :type fieldname: str :rtype: int, int -- type ID, field offset""" module, typeid = self.resolve_type(module, typeid) fieldtypeid = ULONG(0) fieldoffset = ULONG(0) self.DebugSymbols.GetFieldTypeAndOffset(module, typeid, fieldname, byref(fieldtypeid), byref(fieldoffset)) return fieldtypeid.value, fieldoffset.value # Custom type functions, it seems that COM api does not return all informations # we may need to directly call Dbghelp @experimental def get_all_field_generator(self, module, typeid): for i in itertools.count(0): try: name = self.get_field_name(module, typeid, i) except WindowsError: if i == 0: # Empty struct: Error in call args raise return yield name @experimental def get_all_field(self, module, typeid): return list(self.get_all_field_generator(module, typeid)) @experimental def get_all_field_type_and_offset(self, module, typeid): fields = self.get_all_field(module, typeid) return [(f,) + self.get_field_type_and_offset(module, typeid, f) for f in fields] # def old_tst(self, module, typeid): # for name, type, offset in self.get_all_field_type_and_offset(module, typeid): # type_name = self.get_type_name(module, type) # #print("+{0} {1}: {2}({3})".format(hex(offset), name, type_name, type)) # if type_name.endswith("[]"): # try: # sub_module, sub_type = self.resolve_type(module, type_name[:-2]) # Get the subtype if not a basic C type # size_of_elt = self.get_type_size(sub_module, sub_type) # is_base_type = False # except ValueError: # If it's an array of basic type # sub_module, sub_type = self.get_symbol_type_id(type_name[:-2]) # size_of_elt = self.get_type_size(sub_module, sub_type) # is_base_type = True # nb_elt = self.get_type_size(module, type) / size_of_elt # print("+{0} {1}: {2}({3})".format(hex(offset), name, type_name, type)) # print "ARRAY of {0} | {1}".format(nb_elt, "BASE_TYPE" if is_base_type else "NO BASE TYPE") # # if not is_base_type: # print("PARSING {0}".format(type_name[:-2])) # self.tst(sub_module, sub_type) # # use kdbg.get_symbol_type_id("nt!_KPRCB.HalReserved[0]") ? # # or look at ctypes.windll.Dbghelp.SymGetTypeInfo ? # def tst(self, module, typeid): # "Create Ctypes" # next_offset = 0 # last_offset = -1 # for name, type, offset in self.get_all_field_type_and_offset(module, typeid): # type_name = self.get_type_name(module, type) # # if offset == 0x2dec: # print("----") # print("+{0} {1}: {2}({3})".format(hex(offset), name, type_name, type)) # x = self.SymGetTypeInfo(module, type, TI_GET_BITPOSITION) # print("TI_GET_BITPOSITION -> {0}".format(x)) # x = self.SymGetTypeInfo(module, type, TI_GET_LENGTH) # print("TI_GET_LENGTH -> {0}".format(x)) # # #if type_name.endswith("[]"): # # print("-------") # # print("+{0} {1}: {2}({3})".format(hex(offset), name, type_name, type)) # # sub_type = self.SymGetTypeInfo(module, type, TI_GET_TYPE) # # print("ARRAY OF {0}".format(self.get_type_name(module, sub_type))) # # x = self.SymGetTypeInfo(module, type, TI_GET_BITPOSITION) # #print(x) # if x: # print("-------") # print("+{0} {1}: {2}({3})".format(hex(offset), name, type_name, type)) # print("BITFIELD POS {0}".format(x)) # # # # HANDLE ARRAY # #if type_name.endswith("[]"): # # try: # # sub_module, sub_type = self.resolve_type(module, type_name[:-2]) # Get the subtype if not a basic C type # # size_of_elt = self.get_type_size(sub_module, sub_type) # # is_base_type = False # # except ValueError: # If it's an array of basic type # # sub_module, sub_type = self.get_symbol_type_id(type_name[:-2]) # # size_of_elt = self.get_type_size(sub_module, sub_type) # # is_base_type = True # # nb_elt = self.get_type_size(module, type) / size_of_elt # # print("+{0} {1}: {2}({3})".format(hex(offset), name, type_name, type)) # # print "ARRAY of {0} | {1}".format(nb_elt, "BASE_TYPE" if is_base_type else "NO BASE TYPE") # # # use kdbg.get_symbol_type_id("nt!_KPRCB.HalReserved[0]") ? # # # or look at ctypes.windll.Dbghelp.SymGetTypeInfo ? # Low level DbgHelp queries @experimental def SymGetTypeInfo(self, module, typeid, GetType, ires=None): # If not: result is a DWORD res = ires if res is None: if GetType == TI_FINDCHILDREN or GetType == TI_GET_VALUE: raise NotImplementedError("SymGetTypeInfo with GetType == TI_FINDCHILDREN need struct passed as argument") result_type = { TI_GET_SYMNAME: c_wchar_p, TI_GET_LENGTH: ULONG64, TI_GET_ADDRESS: ULONG64, TI_GTIEX_REQS_VALID: ULONG64, } res = result_type.get(GetType, DWORD)() module, typeid = self.resolve_type(module, typeid) self.SymGetTypeInfo_ctypes(0xf0f0f0f0, module, typeid, GetType, byref(res)) if ires is None: return res.value return res @experimental def get_number_chid(self, module, typeid): module, typeid = self.resolve_type(module, typeid) return self.SymGetTypeInfo(module, typeid, TI_GET_CHILDRENCOUNT) @experimental def get_childs_types(self, module, typeid): nb_childs = self.get_number_chid(module, typeid) class res_struct(Structure): _fields_ = [("Count", ULONG), ("Start", ULONG), ("Types", (ULONG * nb_childs))] _fields_ = [("Count", ULONG), ("Start", ULONG), ("Types", (ULONG * nb_childs))] res = res_struct() res.Count = nb_childs self.SymGetTypeInfo(module, typeid, TI_FINDCHILDREN, ires=res) return res def trim_ulong64_to_address(self, addr): """ | Used to convert a symbol ULONG64 to the actual symbol. | Problem is that in a 32bits kernel the kernel address are bit expended | :file:`nt` in 32bits kernel would not be :file:`0x8xxxxxxx` but :file:`0xffffffff8xxxxxxx` """ raise NotImplementedError("bitness dependent") def expand_address_to_ulong64(self, addr): """| Used to convert a symbol address to an ULONG64 requested by the API. | Problem is that in a 32bits kernel the kernel address are bit expended | :file:`nt` in 32bits kernel would not be :file:`0x8xxxxxxx` but :file:`0xffffffff8xxxxxxx` """ raise NotImplementedError("bitness dependent") def get_symbol_offset(self, name): """Get the address of a symbol :param name: Name of the symbol :type name: str :rtype: int""" SymbolLocation = ctypes.c_uint64(0) try: self.DebugSymbols.GetOffsetByName(name, ctypes.byref(SymbolLocation)) except WindowsError: return None return self.trim_ulong64_to_address(SymbolLocation.value) def get_symbol(self, addr): """Get the symbol and displacement of an address :param addr: The address to lookup :type addr: int :rtype: str, int -- symbol name, displacement""" addr = self.expand_address_to_ulong64(addr) buffer_size = 1024 buffer = (c_char * buffer_size)() name_size = ULONG() displacement = ULONG64() try: self.DebugSymbols.GetNameByOffset(addr, byref(buffer), buffer_size, byref(name_size), byref(displacement)) except WindowsError as e: if (e.winerror & 0xffffffff) == E_FAIL: return (None, None) return (buffer.value, displacement.value) def symbol_match(self, symbol_pattern): """| <generator> | List of symbol (name, address) that match a symbol pattern :param symbol_pattern: The symbol pattern (nt!Create*, *!CreateFile, ..) :type symbol_pattern: str :yield: str, int -- symbol name, symbol address """ search_handle = ULONG64() buffer_size = 1024 buffer = (c_char * buffer_size)() match_size = ULONG() symbol_addr = ULONG64() self.DebugSymbols.StartSymbolMatch(symbol_pattern, byref(search_handle)) while True: try: self.DebugSymbols.GetNextSymbolMatch(search_handle, byref(buffer), buffer_size, byref(match_size), byref(symbol_addr)) except WindowsError as e: if (e.winerror & 0xffffffff) == S_FALSE: buffer_size = buffer_size buffer = (c_char * buffer_size)() continue if (e.winerror & 0xffffffff) == E_NOINTERFACE: self.DebugSymbols.EndSymbolMatch(search_handle) return yield (buffer.value, self.trim_ulong64_to_address(symbol_addr.value)) def read_virtual_memory(self, addr, size): """Read the memory at a given virtual address :param addr: The Symbol to read from :type addr: Symbol :param size: The size to read :type size: int :returns: str """ addr = self.resolve_symbol(addr) buffer = (c_char * size)() read = DWORD(0) self.DebugDataSpaces.ReadVirtual(c_uint64(addr), buffer, size, byref(read)) return buffer[0:read.value] def write_virtual_memory(self, addr, data): """Write data to a given virtual address :param addr: The Symbol to write to :type addr: Symbol :param size: The Data to write :type size: str or ctypes.Structure :returns: the size written -- :class:`int` """ try: # ctypes structure size = ctypes.sizeof(data) buffer = ctypes.byref(data) except TypeError: # buffer size = len(data) buffer = data written = ULONG(0) addr = self.resolve_symbol(addr) self.DebugDataSpaces.WriteVirtual(c_uint64(addr), buffer, size, byref(written)) return written.value def write_pfv_memory(self, addr, data): """Write physical memory from virtual address Exactly the same as write_physical(virtual_to_physical(addr), data) """ return self.write_physical_memory(self.virtual_to_physical(addr), data) def read_virtual_memory_into(self, addr, struct): """"Read the memory at a given virtual address into a ctypes Structure :param addr: The Symbol to read from :type addr: Symbol :param struct: The structure to fill :type size: ctypes.Structure :returns: the size read -- :class:`int` """ addr = self.resolve_symbol(addr) size = ctypes.sizeof(struct) read = ULONG(0) self.DebugDataSpaces.ReadVirtual(c_uint64(addr), byref(struct), size, byref(read)) return read.value def read_byte(self, addr): """Read a byte from virtual memory""" sizeof_byte = sizeof(BYTE) raw_data = self.read_virtual_memory(addr, sizeof_byte) return struct.unpack("<B", raw_data)[0] def read_byte_p(self, addr): """Read a byte from physical memory""" sizeof_byte = sizeof(BYTE) raw_data = self.read_physical_memory(addr, sizeof_byte) return struct.unpack("<B", raw_data)[0] def read_word(self, addr): """Read a word from virtual memory""" sizeof_word = sizeof(WORD) raw_data = self.read_virtual_memory(addr, sizeof_word) return struct.unpack("<H", raw_data)[0] def read_word_p(self, addr): """Read a word from physical memory""" sizeof_word = sizeof(WORD) raw_data = self.read_physical_memory(addr, sizeof_word) return struct.unpack("<H", raw_data)[0] def read_dword(self, addr): """Read a dword from virtual memory""" sizeof_dword = ctypes.sizeof(DWORD) raw_data = self.read_virtual_memory(addr, sizeof_dword) return struct.unpack("<I", raw_data)[0] def read_dword_p(self, addr): """Read a dword from physical memory""" sizeof_dword = ctypes.sizeof(DWORD) raw_data = self.read_physical_memory(addr, sizeof_dword) return struct.unpack("<I", raw_data)[0] def read_qword(self, addr): """Read a qword from virtual memory""" sizeof_qword = sizeof(ULONG64) raw_data = self.read_virtual_memory(addr, sizeof_qword) return struct.unpack("<Q", raw_data)[0] def read_qword_p(self, addr): """Read a qword from physical memory""" sizeof_qword = sizeof(ULONG64) raw_data = self.read_physical_memory(addr, sizeof_qword) return struct.unpack("<Q", raw_data)[0] def write_byte(self, addr, byte): """Read a byte to virtual memory""" return self.write_virtual_memory(addr, struct.pack("<B", byte)) def write_byte_p(self, addr, byte): """write a byte to physical memory""" return self.write_physical_memory(addr, struct.pack("<B", byte)) def write_word(self, addr, word): """write a word to virtual memory""" return self.write_virtual_memory(addr, struct.pack("<H", word)) def write_word_p(self, addr, word): """write a word to physical memory""" return self.write_physical_memory(addr, struct.pack("<H", word)) def write_dword(self, addr, dword): """write a dword to virtual memory""" return self.write_virtual_memory(addr, struct.pack("<I", dword)) def write_dword_p(self, addr, dword): """write a dword to physical memory""" return self.write_physical_memory(addr, struct.pack("<I", dword)) def write_qword(self, addr, qword): """write a qword to virtual memory""" return self.write_virtual_memory(addr, struct.pack("<Q", qword)) def write_qword_p(self, addr, qword): """write a qword to physical memory""" return self.write_physical_memory(addr, struct.pack("<Q", qword)) def read_ptr(self, addr): """Read a pointer from virtual memory""" raise NotImplementedError("bitness dependent") def read_ptr_p(self, addr): """Read a pointer from physical memory""" raise NotImplementedError("bitness dependent") def write_ptr(self, addr, value): """Write a pointer to virtual memory""" raise NotImplementedError("bitness dependent") def write_ptr_p(self, addr, value): """Write a pointer to physical memory""" raise NotImplementedError("bitness dependent") def write_msr(self, msr_id, value): """Write a Model Specific Register""" return self.DebugDataSpaces.WriteMsr(msr_id, value) def read_msr(self, msr_id): """Read a Model Specific Register""" msr_value = ULONG64() self.DebugDataSpaces.ReadMsr(msr_id, byref(msr_value)) return msr_value.value def virtual_to_physical(self, virtual): """Get the physical address of a virtual one""" virtual = self.resolve_symbol(virtual) res = ULONG64(0) self.DebugDataSpaces.VirtualToPhysical(c_uint64(virtual), byref(res)) return res.value def read_physical_memory(self, addr, size): """Read the physical memory at a given address :param addr: The Symbol to read from :type addr: Symbol :param size: The size to read :type size: int :returns: :class:`str` """ buffer = (c_char * size)() read = DWORD(0) self.DebugDataSpaces.ReadPhysical(c_uint64(addr), buffer, size, byref(read)) return buffer[0:read.value] def write_physical_memory(self, addr, data): """Write data to a given physical address :param addr: The Symbol to write to :type addr: Symbol :param size: The Data to write :type size: str or ctypes.Structure :returns: the size written -- :class:`int` """ try: # ctypes structure size = ctypes.sizeof(data) buffer = ctypes.byref(data) except TypeError: # buffer size = len(data) buffer = data written = ULONG(0) self.DebugDataSpaces.WritePhysical(c_uint64(addr), buffer, size, byref(written)) return written.value def read_processor_system_data(self, processor, type): """| Returns a :class:`DEBUG_PROCESSOR_IDENTIFICATION_X86` if type is :class:`DEBUG_DATA_PROCESSOR_IDENTIFICATION` | else returns an :class:`int` (see :func:`ReadProcessorSystemData` `<https://msdn.microsoft.com/en-us/library/windows/hardware/ff554326%28v=vs.85%29.aspx>`_.) """ if type == DEBUG_DATA_PROCESSOR_IDENTIFICATION: buffer = DEBUG_PROCESSOR_IDENTIFICATION_ALL() elif type == DEBUG_DATA_PROCESSOR_SPEED: buffer = ULONG(0) else: buffer = ULONG64(0) data_size = ULONG(0) self.DebugDataSpaces.ReadProcessorSystemData(processor, type, byref(buffer), sizeof(buffer), byref(data_size)) if type != DEBUG_DATA_PROCESSOR_IDENTIFICATION: buffer = buffer.value return buffer def read_bus_data(self, datatype, busnumber, slot, offset, size): r"""| Read on bus data, only current known use is to read on the PCI bus. | (see :file:`example\\simple_pci_exploration.py`) """ buffer = (c_char * size)() read = ULONG(0) self.DebugDataSpaces.ReadBusData(datatype, busnumber, slot, offset, buffer, size, byref(read)) return buffer[0:read.value] def write_bus_data(self, datatype, busnumber, slot, offset, data): r"""| Write on bus data, only current known use is to write on the PCI bus. | (see :file:`example\\simple_pci_exploration.py`) """ size = len(data) written = ULONG(0) self.DebugDataSpaces.ReadBusData(datatype, busnumber, slot, offset, buffer, size, byref(written)) return written.value def read_io(self, port, size): """| Perform an IN operation | might be subject to some restrictions | (see :file:`README.md` :file:`do_in | do_out VS read_io | write_io`) :param port: port to read :param size: size to read :type port: int :type size: int - 1, 2 or 4 :returns: the value read -- :class:`int` """ InterfaceType = 1 # Isa BusNumber = 0 AddressSpace = 1 Buffer = (c_char * size)() BytesRead = ULONG() self.DebugDataSpaces.ReadIo(InterfaceType, BusNumber, AddressSpace, port, Buffer, size, byref(BytesRead)) format = {1: '<B', 2: '<H', 4: '<I'}[size] return struct.unpack(format, Buffer[:BytesRead.value])[0] def write_io(self, port, value, size=None): """| Perform an OUT operation | might be subject to some restrictions | (see :file:`README.md` :file:`do_in | do_out VS read_io | write_io`) :param port: port to write :param size: size to write :type port: int :type size: int - 1, 2 or 4 :returns: the number of bytes written -- :class:`int` """ InterfaceType = 1 # Isa BusNumber = 0 AddressSpace = 1 if size is None: size = len(value) Buffer = value else: format = {1: '<B', 2: '<H', 4: '<I'}[size] Buffer = struct.pack(format, value) BytesWritten = ULONG() self.DebugDataSpaces.WriteIo(InterfaceType, BusNumber, AddressSpace, port, Buffer, size, byref(BytesWritten)) return BytesWritten.value @require_upgraded_driver def kcall(self, target, *args): """| Call target in kernel mode with given arguments. | KCall respect the calling convention but YOU must | pass the correct number of argument or the kernel | will likely crash. | (see :func:`map_page_to_userland`) for an example :param target: the Symbol to call :type target: Symbol :param args: the arguments of the call :type args: list of int :returns: :class:`int` """ raise NotImplementedError("bitness dependent") # do_in | do_out # There is already a COM API: WriteIo and ReadIo but it seems that # these API check for some alignment in the port and the size. # A ReadIo(size=4) must be on a port aligned on 4. # It may caused by a bad call from use in write_io | read_io # Anyway we implemented our own bypass in upgrade driver @require_upgraded_driver def do_in(self, port, size): """| Perform an IN operation :param port: port to read :param size: size to read :type port: int :type size: int - 1, 2 or 4 :returns: the value read -- :class:`int` """ raise NotImplementedError("bitness dependent") @require_upgraded_driver def do_out(self, port, value, size): """| Perform an OUT operation :param port: port to write :param size: size to write :type port: int :type size: int - 1, 2 or 4 :returns: the number of bytes written -- :class:`int` """ raise NotImplementedError("bitness dependent") @require_upgraded_driver def alloc_memory(self, size=0x1000): """Allocate **size** of NonPaged kernel memory""" raise NotImplementedError("bitness dependent") @require_upgraded_driver def map_page_to_userland(self, virtual_addr, size): """Map **size** bytes of kernel memory **virtual_addr** in the current address space :param virtual_addr: kernel virtual addr :param size: size to map :type virtual_addr: int :type size: int :returns: address of the page in current process -- :class:`int` """ # Check if all exports are known before calling everything IoAllocateMdl = self.resolve_symbol("IoAllocateMdl") MmBuildMdlForNonPagedPool = self.resolve_symbol("MmBuildMdlForNonPagedPool") MmMapLockedPagesSpecifyCache = self.resolve_symbol("MmMapLockedPagesSpecifyCache") mdl = self.kcall(IoAllocateMdl, virtual_addr, size, False, False, None) self.kcall(MmBuildMdlForNonPagedPool, mdl) mapped_addr = self.kcall(MmMapLockedPagesSpecifyCache, mdl, UserMode, MmNonCached, None, False, NormalPagePriority) return mapped_addr class LocalKernelDebugger32(LocalKernelDebuggerBase): DEBUG_DLL_PATH = os.path.join(realpath(dirname(__file__)), "bin\\DBGDLL\\") DRIVER_FILENAME = os.path.join(realpath(dirname(__file__)), "bin\\windbg_driver_x86.sys") DRIVER_RESOURCE = resource_emulation.Ressource(DRIVER_FILENAME, 0x7777, 0x4444) SYMBOL_OPT = (SYMOPT_NO_IMAGE_SEARCH + SYMOPT_AUTO_PUBLICS + SYMOPT_FAIL_CRITICAL_ERRORS + SYMOPT_OMAP_FIND_NEAREST + SYMOPT_LOAD_LINES + SYMOPT_DEFERRED_LOADS + SYMOPT_UNDNAME + SYMOPT_CASE_INSENSITIVE) # In our 32bits dll, GetModuleFileNameW and FindResourceW are not in IAT # There is a safe and secure lazy resolution, so let's just hook this jump table GetModuleFileNameW_addr_jump_offset = 0x3374bc FindResourceW_addr_jump_offset = 0x3374a0 # read_ptr and write_ptr real implementation (bitness dependant) read_ptr = LocalKernelDebuggerBase.read_dword read_ptr_p = LocalKernelDebuggerBase.read_dword_p write_ptr = LocalKernelDebuggerBase.write_dword write_ptr_p = LocalKernelDebuggerBase.write_dword_p def expand_address_to_ulong64(self, addr): if addr is None: return None # bit expansion return (0xFFFFFFFF00000000 * (addr >> 31)) | addr def trim_ulong64_to_address(self, addr): if addr is None: return None return addr & 0xffffffff # Setup DRIVER_RESOURCE as a resource using hooks def _setup_driver_resource(self, dbgengmod, k32import): SizeofResourceIAT = [x for x in k32import if x.name == "SizeofResource"][0] LoadResourceIAT = [x for x in k32import if x.name == "LoadResource"][0] LockResourceIAT = [x for x in k32import if x.name == "LockResource"][0] FindResourceW_addr_jump = dbgengmod.DllBase + self.FindResourceW_addr_jump_offset DummyFindResourceWIAT = DummyIATEntry.create(FindResourceW_addr_jump, "kernel32.dll", "FindResourceW") # Add our driver to emulated resources resource_emulation.resource_list.append(self.DRIVER_RESOURCE) # Setup Resource emulation into dbgeng.dll DummyFindResourceWIAT.set_hook(resource_emulation.FindResourceWHook) SizeofResourceIAT.set_hook(resource_emulation.SizeofResourceHook) LoadResourceIAT.set_hook(resource_emulation.LoadResourceHook) LockResourceIAT.set_hook(resource_emulation.LockResourceHook) def _setup_name_imposture(self, dbgengmod, k32import): GetModuleFileNameW_addr_jump = dbgengmod.DllBase + self.GetModuleFileNameW_addr_jump_offset DummyGetModuleFileNameWIAT = DummyIATEntry.create(GetModuleFileNameW_addr_jump, "kernel32.dll", "GetModuleFileNameW") DummyGetModuleFileNameWIAT.set_hook(EmulateWinDBGName) # Driver upgrade stuff def _upgrade_driver(self): self.upgrader = driver_upgrade.DriverUpgrader32(self) self.upgrader.upgrade_driver() # upgraded driver API @require_upgraded_driver def kcall(self, target, *args): """Call target in kernel mode with given arguments""" target = self.resolve_symbol(target) args = [arg if arg is not None else 0 for arg in args] buffer = struct.pack("<" + "I" * (len(args) + 1), target, *args) h = self._get_kldbgdrv_handle() res = DWORD(0x44444444) windows.winproxy.DeviceIoControl(h, DU_KCALL_IOCTL, buffer, len(buffer), byref(res), ctypes.sizeof(res)) return res.value @require_upgraded_driver def do_in(self, port, size): """Perform IN instruction in kernel mode""" if size not in [1, 2, 4]: raise ValueError("Invalid IN size: {0}".format(size)) h = self._get_kldbgdrv_handle() buffer = struct.pack("<II", size, port) res = DWORD(0x44444444) windows.winproxy.DeviceIoControl(h, DU_IN_IOCTL, buffer, len(buffer), byref(res), ctypes.sizeof(res)) return res.value @require_upgraded_driver def do_out(self, port, value, size): """Perform OUT instruction in kernel mode""" if size not in [1, 2, 4]: raise ValueError("Invalid OUT size: {0}".format(size)) h = self._get_kldbgdrv_handle() buffer = struct.pack("<III", size, port, value) windows.winproxy.DeviceIoControl(h, DU_OUT_IOCTL, buffer, len(buffer), 0, 0) return None @require_upgraded_driver def alloc_memory(self, size=0x1000, type=0, tag=0x45544942): """Allocation <size> of NonPaged kernel memory""" h = self._get_kldbgdrv_handle() buffer = struct.pack("<III", type, size, tag) res = DWORD(0x44444444) windows.winproxy.DeviceIoControl(h, DU_MEMALLOC_IOCTL, buffer, len(buffer), byref(res), 4) return res.value class LocalKernelDebugger64(LocalKernelDebuggerBase): DEBUG_DLL_PATH = os.path.join(realpath(dirname(__file__)), "bin\\DBGDLL64\\") DRIVER_FILENAME = os.path.join(realpath(dirname(__file__)), "bin\\windbg_driver_x64.sys") DRIVER_RESOURCE = resource_emulation.Ressource(DRIVER_FILENAME, 0x7777, 0x4444) SYMBOL_OPT = (SYMOPT_NO_IMAGE_SEARCH + SYMOPT_AUTO_PUBLICS + SYMOPT_FAIL_CRITICAL_ERRORS + SYMOPT_OMAP_FIND_NEAREST + SYMOPT_LOAD_LINES + SYMOPT_DEFERRED_LOADS + SYMOPT_UNDNAME + SYMOPT_CASE_INSENSITIVE) read_ptr = LocalKernelDebuggerBase.read_qword read_ptr_p = LocalKernelDebuggerBase.read_qword_p write_ptr = LocalKernelDebuggerBase.write_qword write_ptr_p = LocalKernelDebuggerBase.write_qword_p # Setup DRIVER_RESOURCE as a resource using hooks def _setup_driver_resource(self, dbgengmod, k32import): SizeofResourceIAT = [x for x in k32import if x.name == "SizeofResource"][0] LoadResourceIAT = [x for x in k32import if x.name == "LoadResource"][0] LockResourceIAT = [x for x in k32import if x.name == "LockResource"][0] FindResourceWIAT = [x for x in k32import if x.name == "FindResourceW"][0] # Add our drive to emulated resources resource_emulation.resource_list.append(self.DRIVER_RESOURCE) # Setup Resource emulation into dbgeng.dll FindResourceWIAT.set_hook(resource_emulation.FindResourceWHook) SizeofResourceIAT.set_hook(resource_emulation.SizeofResourceHook) LoadResourceIAT.set_hook(resource_emulation.LoadResourceHook) LockResourceIAT.set_hook(resource_emulation.LockResourceHook) def _setup_name_imposture(self, dbgengmod, k32import): GetModuleFileNameWIAT = [x for x in k32import if x.name == "GetModuleFileNameW"][0] GetModuleFileNameWIAT.set_hook(EmulateWinDBGName) def _upgrade_driver(self): self.upgrader = driver_upgrade.DriverUpgrader64(self) self.upgrader.upgrade_driver() def expand_address_to_ulong64(self, addr): return addr def trim_ulong64_to_address(self, addr): return addr @require_upgraded_driver def alloc_memory(self, size=0x1000, type=0, tag=0x45544942): """Allocation <size> of NonPaged kernel memory""" h = self._get_kldbgdrv_handle() buffer = struct.pack("<QQQ", type, size, tag) res = c_uint64(0x44444444) windows.winproxy.DeviceIoControl(h, DU_MEMALLOC_IOCTL, buffer, len(buffer), byref(res), sizeof(res)) return res.value @require_upgraded_driver def kcall(self, target, *args): """Call target in kernel mode with given arguments""" target = self.resolve_symbol(target) args = [arg if arg is not None else 0 for arg in args] buffer = struct.pack("<" + "Q" * (len(args) + 1), target, *args) h = self._get_kldbgdrv_handle() res = c_uint64(0x44444444) windows.winproxy.DeviceIoControl(h, DU_KCALL_IOCTL, buffer, len(buffer), byref(res), ctypes.sizeof(res)) return res.value @require_upgraded_driver def do_in(self, port, size): """Perform IN instruction in kernel mode""" if size not in [1, 2, 4]: raise ValueError("Invalid IN size: {0}".format(size)) h = self._get_kldbgdrv_handle() buffer = struct.pack("<QQ", size, port) res = DWORD(0x44444444) windows.winproxy.DeviceIoControl(h, DU_IN_IOCTL, buffer, len(buffer), byref(res), ctypes.sizeof(res)) return res.value @require_upgraded_driver def do_out(self, port, value, size): """Perform OUT instruction in kernel mode""" if size not in [1, 2, 4]: raise ValueError("Invalid OUT size: {0}".format(size)) h = self._get_kldbgdrv_handle() buffer = struct.pack("<QQQ", size, port, value) windows.winproxy.DeviceIoControl(h, DU_OUT_IOCTL, buffer, len(buffer), 0, 0) return None class LocalKernelDebuggerError(Exception): pass def LocalKernelDebugger(quiet=True): """| Check that all conditions to Local Kernel Debugging are met | and return a LKD (subclass of :class:`LocalKernelDebuggerBase` """ if not windows.utils.check_debug(): raise LocalKernelDebuggerError("Cannot perform LocalKernelDebugging on kernel not in DEBUG mode") if not windows.utils.check_is_elevated(): raise LocalKernelDebuggerError("Cannot perform LocalKernelDebugging from non-Admin process") windows.utils.enable_privilege(SE_DEBUG_NAME, True) if windows.system.bitness == 64: if windows.current_process.is_wow_64: raise LocalKernelDebuggerError("Cannot perform LocalKernelDebugging from SysWow64 process (please launch from 64bits python)") return LocalKernelDebugger64(quiet) return LocalKernelDebugger32(quiet) # # We are working on this part, we don't know if we will use it # # We don't know if it really works # # We keep that here for information purposes # class RemoteDebugger(LocalKernelDebugger32): # def __init__(self): # enable_privilege(SE_DEBUG_NAME, True) # self._load_debug_dll() # self.DebugClient = self._do_debug_create() # # def _do_kernel_attach(self, str): # #self._setup_windbg_imposture() # DEBUG_ATTACH_LOCAL_KERNEL = 1 # DEBUG_ATTACH_KERNEL_CONNECTION = 0x00000000 # res = self.DebugClient.AttachKernel(DEBUG_ATTACH_KERNEL_CONNECTION, str) # if res: # raise WinError(res)