Improve debugger + pe_parse 'guess' the PE type (32/64)

This commit is contained in:
Clement Rouault
2016-02-10 16:27:53 +01:00
parent 401db9c347
commit b789a12040
5 changed files with 140 additions and 97 deletions
+17 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)