mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
Improve debugger + pe_parse 'guess' the PE type (32/64)
This commit is contained in:
@@ -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..
|
||||
- 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
|
||||
+21
-8
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+25
-11
@@ -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
|
||||
+1
-4
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user