diff --git a/TODO b/TODO index 7db2ed4..3daa803 100644 --- a/TODO +++ b/TODO @@ -18,6 +18,15 @@ TODO: - code generated by generate_python_exec_shellcode_64[32] may be reused Just need to passe the address of the python string as argument + - rewrite generate_stub_64[32] : it's a non-sens to not save stuff on the stack.. + I can re-copy the args on stack.. + + - Winproxy: + - rethink OptionalExport ? not useful with lazy resolution (or we need to force resolution..) + + - Debugger + - TODO: test breakpoint with specific target + FIXME: - WMI @@ -28,4 +37,11 @@ FIXME: - setup.py build seems to raise an error - winutils.create_process : use WinProcess._from_handle - - Push("[ECX]") in simple_x64 as a "H" rex and i think it should not.. \ No newline at end of file + - Push("[ECX]") in simple_x64 as a "H" rex and i think it should not.. + +Documentation: + - debug.py + - exception.py + - WinProcess/WinThread new methods + - the new samples + - native_exec.nativeutils \ No newline at end of file diff --git a/windows/debug.py b/windows/debug.py index aa5794f..88dfd5e 100644 --- a/windows/debug.py +++ b/windows/debug.py @@ -103,6 +103,7 @@ class Debugger(object): return x def _resolve(self, addr, target): + print("Resolving <{0}> for {1}".format(addr, self.current_process)) if not isinstance(addr, basestring): return addr dll, api = addr.split("!") @@ -115,6 +116,12 @@ class Debugger(object): 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: raise ValueError("Unknown API <{0}> in DLL {1}".format(api, dll)) @@ -124,13 +131,15 @@ class Debugger(object): def add_pending_breakpoint(self, bp, target): self._pending_breakpoints_new[target].append(bp) - def _setup_breakpoint(self, bp, targets): + def _setup_breakpoint(self, bp, target): _setup_method = getattr(self, "_setup_breakpoint_" + bp.type) - if targets is None: + if target is None: if bp.type == STANDARD_BP: #TODO: better.. targets = self.processes else: targets = self.threads + else: + targets = [target] for target in targets: return _setup_method(bp, target) @@ -277,9 +286,13 @@ class Debugger(object): def _get_loaded_dll(self, load_dll): + name_sufix = "" + pe = windows.pe_parse.GetPEFile(load_dll.lpBaseOfDll, self.current_process) + if self.current_process.bitness == 32 and pe.bitness == 64: + name_sufix = "64" + if not load_dll.lpImageName: - pe = windows.pe_parse.GetPEFile(load_dll.lpBaseOfDll, self.current_process) - return pe.export_name + return pe.export_name + name_sufix try: addr = self.current_process.read_ptr(load_dll.lpImageName) except: @@ -287,11 +300,11 @@ class Debugger(object): if not addr: pe = windows.pe_parse.GetPEFile(load_dll.lpBaseOfDll, self.current_process) - return pe.export_name + return pe.export_name + name_sufix if load_dll.fUnicode: - return self.current_process.read_wstring(addr) - return self.current_process.read_string(addr) + return self.current_process.read_wstring(addr) + name_sufix + return self.current_process.read_string(addr) + name_sufix def _handle_create_process(self, debug_event): """Handle CREATE_PROCESS_DEBUG_EVENT""" @@ -456,7 +469,7 @@ class Debugger(object): self.add_pending_breakpoint(bp, None) elif target is not None: # Check that targets are accepted - if target not in self.processes + self.threads: + if target not in self.processes.values() + self.threads.values(): if target == self.target: # Original target (that have not been lauched yet) return self.add_pending_breakpoint(bp, target) else: diff --git a/windows/vectored_exception.py b/windows/exception.py similarity index 75% rename from windows/vectored_exception.py rename to windows/exception.py index 325faf4..a5b5299 100644 --- a/windows/vectored_exception.py +++ b/windows/exception.py @@ -36,83 +36,61 @@ exception_type = [ # exception_name_by_value[0x80000001] -> EXCEPTION_GUARD_PAGE(0x80000001L) exception_name_by_value = dict([(x, x) for x in [getattr(windows.generated_def.windef, name) for name in exception_type]]) -def generate_enhanced_exception_record(base, name_suffix=""): - class EnhancedEXCEPTION_RECORD(base): +class EEXCEPTION_RECORDBase(object): @property def ExceptionCode(self): - real_code = super(EnhancedEXCEPTION_RECORD, self).ExceptionCode + """The Exception code + + :type: :class:`int`""" + real_code = super(EEXCEPTION_RECORDBase, self).ExceptionCode return exception_name_by_value.get(real_code, windows.generated_def.windef.Flag("UNKNOW_EXCEPTION", real_code)) @property def ExceptionAddress(self): - x = super(EnhancedEXCEPTION_RECORD, self).ExceptionAddress + """The Exception Address + + :type: :class:`int`""" + x = super(EEXCEPTION_RECORDBase, self).ExceptionAddress if x is None: return 0x0 return x - EnhancedEXCEPTION_RECORD.__name__ += name_suffix - return EnhancedEXCEPTION_RECORD -EnhancedEXCEPTION_RECORD = generate_enhanced_exception_record(EXCEPTION_RECORD) -EnhancedEXCEPTION_RECORD32 = generate_enhanced_exception_record(EXCEPTION_RECORD32, "32") -EnhancedEXCEPTION_RECORD64 = generate_enhanced_exception_record(EXCEPTION_RECORD64, "64") +class EEXCEPTION_RECORD(EEXCEPTION_RECORDBase, EXCEPTION_RECORD): + """Enhanced exception record""" + + fields = [f[0] for f in EXCEPTION_RECORD._fields_] + """The fields of the structure""" + +class EEXCEPTION_RECORD32(EEXCEPTION_RECORDBase, EXCEPTION_RECORD32): + """Enhanced exception record (32bits)""" + + fields = [f[0] for f in EXCEPTION_RECORD32._fields_] + """The fields of the structure""" + +class EEXCEPTION_RECORD64(EEXCEPTION_RECORDBase, EXCEPTION_RECORD64): + """Enhanced exception record (64bits)""" + + fields = [f[0] for f in EXCEPTION_RECORD64._fields_] + """The fields of the structure""" class EEXCEPTION_DEBUG_INFO32(ctypes.Structure): - _fields_ = windows.utils.transform_ctypes_fields(EXCEPTION_DEBUG_INFO, {"ExceptionRecord": EnhancedEXCEPTION_RECORD32}) + """Enhanced Debug info""" + _fields_ = windows.utils.transform_ctypes_fields(EXCEPTION_DEBUG_INFO, {"ExceptionRecord": EEXCEPTION_RECORD32}) + + fields = [f[0] for f in _fields_] + """The fields of the structure""" class EEXCEPTION_DEBUG_INFO64(ctypes.Structure): - _fields_ = windows.utils.transform_ctypes_fields(EXCEPTION_DEBUG_INFO, {"ExceptionRecord": EnhancedEXCEPTION_RECORD64}) + """Enhanced Debug info""" + _fields_ = windows.utils.transform_ctypes_fields(EXCEPTION_DEBUG_INFO, {"ExceptionRecord": EEXCEPTION_RECORD64}) + + fields = [f[0] for f in _fields_] + """The fields of the structure""" -#class Eflags(int): -# _flags_ = [("CF", 1), -# ("RES_1", 1), -# ("PF", 1), -# ("RES_3", 1), -# ("AF", 1), -# ("RES_5", 1), -# ("ZF", 1), -# ("SF", 1), -# ("TF", 1), -# ("IF", 1), -# ("DF", 1), -# ("OF", 1), -# ("IOPL_1", 1), -# ("IOPL_2", 1), -# ("NT", 1), -# ("RES_15", 1), -# ("RF", 1), -# ("VM", 1), -# ("AC", 1), -# ("VIF", 1), -# ("VIP", 1), -# ("ID", 1), -# ] -# -# _flag_mask_ = dict([(name, 1 << i) for i, (name, size) in enumerate(_flags_)]) -# -# def __getattr__(self, name): -# if name in self._flag_mask_: -# return bool(self & self._flag_mask_[name]) -# return super(Eflags, self).__getattr_(name) -# -# def dump(self): -# res = [] -# for name in self._flag_mask_: -# if name.startswith("RES_"): -# continue -# if getattr(self, name): -# res.append(name) -# return "|".join(res) -# -# def __repr__(self): -# return "{0}({1})".format(type(self).__name__, self.dump()) -# -# __str__ = __repr__ -# -# def __hex__(self): -# return "{0}({1}:{2})".format(type(self).__name__, int.__hex__(self), self.dump()) class EEflags(ctypes.Structure): + "Flag view of the Eflags register" _fields_ = [("CF", DWORD, 1), ("RES_1", DWORD, 1), ("PF", DWORD, 1), @@ -137,6 +115,9 @@ class EEflags(ctypes.Structure): ("ID", DWORD, 1), ] + fields = [f[0] for f in _fields_] + """The fields of the structure""" + def get_raw(self): x = DWORD.from_address(ctypes.addressof(self)) return x.value @@ -166,6 +147,7 @@ class EEflags(ctypes.Structure): raw = property(get_raw, set_raw) class EDr7(ctypes.Structure): + "Flag view of the DR7 register" _fields_ = [("L0", DWORD, 1), ("G0", DWORD, 1), ("L1", DWORD, 1), @@ -189,7 +171,11 @@ class EDr7(ctypes.Structure): ("LEN3", DWORD, 2), ] -class EnhancedCONTEXTBase(object): + fields = [f[0] for f in _fields_] + """The fields of the structure""" + +class ECONTEXTBase(object): + """DAT CONTEXT""" default_dump = () pc_reg = '' special_reg_type = {} @@ -206,6 +192,7 @@ class EnhancedCONTEXTBase(object): return res def dump(self, to_dump=None): + """Dump (print) the current context""" regs = self.regs() for name, value in regs: print("{0} -> {1}".format(name, hex(value))) @@ -221,6 +208,10 @@ class EnhancedCONTEXTBase(object): @property def EEFlags(self): + """Enhanced view of the Eflags + + :type: :class:`EEflags` + """ off = type(self).EFlags.offset x = EEflags.from_address(ctypes.addressof(self) + off) x.self = self @@ -228,31 +219,39 @@ class EnhancedCONTEXTBase(object): @property def EDr7(self): + """Enhanced view of the DR7 register + + :type: :class:`EDr7` + """ off = type(self).Dr7.offset x = EDr7.from_address(ctypes.addressof(self) + off) x.self = self return x -class EnhancedCONTEXT32(EnhancedCONTEXTBase, (CONTEXT32)): +class ECONTEXT32(ECONTEXTBase, CONTEXT32): default_dump = ('Eip', 'Esp', 'Eax', 'Ebx', 'Ecx', 'Edx', 'Ebp', 'Edi', 'Esi', 'EFlags') pc_reg = 'Eip' - #special_reg_type = {'EFlags': Eflags} + fields = [f[0] for f in CONTEXT32._fields_] + """The fields of the structure""" -class EnhancedCONTEXTWOW64(EnhancedCONTEXTBase, (WOW64_CONTEXT)): +class ECONTEXTWOW64(ECONTEXTBase, WOW64_CONTEXT): default_dump = ('Eip', 'Esp', 'Eax', 'Ebx', 'Ecx', 'Edx', 'Ebp', 'Edi', 'Esi', 'EFlags') pc_reg = 'Eip' - #special_reg_type = {'EFlags': Eflags} + fields = [f[0] for f in WOW64_CONTEXT._fields_] + """The fields of the structure""" -class EnhancedCONTEXT64(EnhancedCONTEXTBase, (CONTEXT64)): +class ECONTEXT64(ECONTEXTBase, CONTEXT64): default_dump = ('Rip', 'Rsp', 'Rax', 'Rbx', 'Rcx', 'Rdx', 'Rbp', 'Rdi', 'Rsi', 'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15', 'EFlags') pc_reg = 'Rip' - #special_reg_type = {'EFlags': Eflags} + fields = [f[0] for f in CONTEXT64._fields_] + """The fields of the structure""" @classmethod def new_aligned(cls): - """Return a new EnhancedCONTEXT64 aligned on 16 bits + """Return a new :class:`ECONTEXT64` aligned on 16 bits + temporary workaround or horrible hack ? choose your side """ size = ctypes.sizeof(cls) @@ -275,18 +274,19 @@ def bitness(): return int(bits[:2]) if bitness() == 32: - EnhancedCONTEXT = EnhancedCONTEXT32 + ECONTEXT = ECONTEXT32 else: - EnhancedCONTEXT = EnhancedCONTEXT64 + ECONTEXT = ECONTEXT64 -class EnhancedEXCEPTION_POINTERS(ctypes.Structure): +class EEXCEPTION_POINTERS(ctypes.Structure): _fields_ = [ - ("ExceptionRecord", ctypes.POINTER(EnhancedEXCEPTION_RECORD)), - ("ContextRecord", ctypes.POINTER(EnhancedCONTEXT)), + ("ExceptionRecord", ctypes.POINTER(EEXCEPTION_RECORD)), + ("ContextRecord", ctypes.POINTER(ECONTEXT)), ] def dump(self): + """Dump the EEXCEPTION_POINTERS""" record = self.ExceptionRecord[0] print("Dumping Exception: ") print(" ExceptionCode = {0} at {1}".format(record.ExceptionCode, hex(record.ExceptionAddress))) @@ -296,7 +296,8 @@ class EnhancedEXCEPTION_POINTERS(ctypes.Structure): class VectoredException(object): - func_type = ctypes.WINFUNCTYPE(ctypes.c_uint, ctypes.POINTER(EnhancedEXCEPTION_POINTERS)) + """A decorator that create a callable which can be passed to :func:`AddVectoredExceptionHandler`""" + func_type = ctypes.WINFUNCTYPE(ctypes.c_uint, ctypes.POINTER(EEXCEPTION_POINTERS)) def __new__(cls, func): self = object.__new__(cls) @@ -309,7 +310,9 @@ class VectoredException(object): try: return self.func(exception_pointers) except BaseException as e: + import traceback print("Ignored Python Exception in Vectored Exception: {0}".format(e)) + traceback.print_exc() return windef.EXCEPTION_CONTINUE_SEARCH diff --git a/windows/pe_parse.py b/windows/pe_parse.py index 2f2f9e6..a417533 100644 --- a/windows/pe_parse.py +++ b/windows/pe_parse.py @@ -26,18 +26,21 @@ def transform_ctypes_fields(struct, replacement): return [(name, replacement.get(name, type)) for name, type in struct._fields_] -def get_structure_transformer_for_target(target): +def get_structure_transformer_for_target(target, targetbitness=None): current_bitness = windows.current_process.bitness if target is None: ctypes_structure_transformer = lambda x:x create_structure_at = lambda structcls, addr: structcls.from_address(addr) return ctypes_structure_transformer, create_structure_at - if target.bitness == 32 and current_bitness == 64: + if targetbitness is None: + targetbitness = target.bitness + + if targetbitness == 32 and current_bitness == 64: ctypes_structure_transformer = rctypes.transform_type_to_remote32bits - elif target.bitness == 64 and current_bitness == 32: + elif targetbitness == 64 and current_bitness == 32: ctypes_structure_transformer = rctypes.transform_type_to_remote64bits - elif target.bitness == current_bitness: + elif targetbitness == current_bitness: ctypes_structure_transformer = rctypes.transform_type_to_remote else: raise NotImplementedError("Parsing {0} PE from {1} Process".format(targetedbitness, proc_bitness)) @@ -46,8 +49,19 @@ def get_structure_transformer_for_target(target): return ctypes_structure_transformer(structcls)(addr, target) return ctypes_structure_transformer, create_structure_at +def get_pe_bitness(baseaddr, target): + # We can force bitness as the filed we access are bitness-independant + pe = GetPEFile(baseaddr, target, force_bitness=32) + machine = pe.get_NT_HEADER().FileHeader.Machine + if machine == 0x14c: + return 32 + elif machine == 0x8664: + return 64 + else: + raise ValueError("Unknow PE target machine <0x{0:x}>".format(machine)) -def GetPEFile(baseaddr, target=None): + +def GetPEFile(baseaddr, target=None, force_bitness=None): """Returns a :class:`PEFile` to explore a PE loaded at `baseaddr` in process `target`. :rtype: :class:`PEFile` @@ -57,15 +71,15 @@ def GetPEFile(baseaddr, target=None): If target is ``None`` it refers to the curent process """ proc_bitness = windows.current_process.bitness - if target is None: - targetedbitness = proc_bitness + + if force_bitness is None: + targetedbitness = get_pe_bitness(baseaddr, target) else: - targetedbitness = target.bitness + targetedbitness = force_bitness - transformers = get_structure_transformer_for_target(target) + transformers = get_structure_transformer_for_target(target, targetedbitness) ctypes_structure_transformer, create_structure_at = transformers - if targetedbitness == 32: IMAGE_ORDINAL_FLAG = IMAGE_ORDINAL_FLAG32 else: @@ -164,6 +178,7 @@ def GetPEFile(baseaddr, target=None): """Represent a PE loaded in a process (current or remote)""" def __init__(self): self.baseaddr = baseaddr + self.bitness = targetedbitness def get_DOS_HEADER(self): return create_structure_at(IMAGE_DOS_HEADER, baseaddr) @@ -345,5 +360,4 @@ def GetPEFile(baseaddr, target=None): if targetedbitness == 32: return create_structure_at(IMAGE_NT_HEADERS32, baseaddr + self.e_lfanew) return create_structure_at(IMAGE_NT_HEADERS64, baseaddr + self.e_lfanew) - return current_pe \ No newline at end of file diff --git a/windows/syswow64.py b/windows/syswow64.py index 6736752..f62e9e8 100644 --- a/windows/syswow64.py +++ b/windows/syswow64.py @@ -187,13 +187,10 @@ def get_current_process_syswow_peb(): return windows.winobject.RemotePEB64(peb_addr, CurrentProcessReadSyswow()) - - class ReadSyswow64Process(object): def __init__(self, target): self.target = target self.bitness = target.bitness - pass def read_memory(self, addr, size): buffer_addr = ctypes.create_string_buffer(size) @@ -282,7 +279,7 @@ def NtQueryVirtualMemory_32_to_64(ProcessHandle, BaseAddress, MemoryInformationC @Syswow64ApiProxy(windows.winproxy.NtGetContextThread) def NtGetContextThread_32_to_64(hThread, lpContext): - if type(lpContext) == windows.vectored_exception.EnhancedCONTEXT64: + if type(lpContext) == windows.exception.ECONTEXT64: lpContext = byref(lpContext) return NtGetContextThread_32_to_64.ctypes_function(hThread, lpContext)