mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
354 lines
11 KiB
Python
354 lines
11 KiB
Python
import ctypes
|
|
import windows
|
|
from windows.generated_def.winstructs import *
|
|
import windows.generated_def.windef as windef
|
|
|
|
EXCEPTION_CONTINUE_SEARCH = (0x0)
|
|
EXCEPTION_CONTINUE_EXECUTION = (0xffffffff)
|
|
|
|
exception_type = [
|
|
"EXCEPTION_ACCESS_VIOLATION",
|
|
"EXCEPTION_DATATYPE_MISALIGNMENT",
|
|
"EXCEPTION_BREAKPOINT",
|
|
"EXCEPTION_SINGLE_STEP",
|
|
"EXCEPTION_ARRAY_BOUNDS_EXCEEDED",
|
|
"EXCEPTION_FLT_DENORMAL_OPERAND",
|
|
"EXCEPTION_FLT_DIVIDE_BY_ZERO",
|
|
"EXCEPTION_FLT_INEXACT_RESULT",
|
|
"EXCEPTION_FLT_INVALID_OPERATION",
|
|
"EXCEPTION_FLT_OVERFLOW",
|
|
"EXCEPTION_FLT_STACK_CHECK",
|
|
"EXCEPTION_FLT_UNDERFLOW",
|
|
"EXCEPTION_INT_DIVIDE_BY_ZERO",
|
|
"EXCEPTION_INT_OVERFLOW",
|
|
"EXCEPTION_PRIV_INSTRUCTION",
|
|
"EXCEPTION_IN_PAGE_ERROR",
|
|
"EXCEPTION_ILLEGAL_INSTRUCTION",
|
|
"EXCEPTION_NONCONTINUABLE_EXCEPTION",
|
|
"EXCEPTION_STACK_OVERFLOW",
|
|
"EXCEPTION_INVALID_DISPOSITION",
|
|
"EXCEPTION_GUARD_PAGE",
|
|
"EXCEPTION_INVALID_HANDLE",
|
|
"EXCEPTION_POSSIBLE_DEADLOCK",
|
|
]
|
|
|
|
# x -> x dict may seems strange but useful to get the Flags (with name) from the int
|
|
# 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]])
|
|
|
|
class EEXCEPTION_RECORDBase(object):
|
|
@property
|
|
def ExceptionCode(self):
|
|
"""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):
|
|
"""The Exception Address
|
|
|
|
:type: :class:`int`"""
|
|
x = super(EEXCEPTION_RECORDBase, self).ExceptionAddress
|
|
if x is None:
|
|
return 0x0
|
|
return x
|
|
|
|
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):
|
|
"""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):
|
|
"""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 EEflags(ctypes.Structure):
|
|
"Flag view of the Eflags register"
|
|
_fields_ = [("CF", DWORD, 1),
|
|
("RES_1", DWORD, 1),
|
|
("PF", DWORD, 1),
|
|
("RES_3", DWORD, 1),
|
|
("AF", DWORD, 1),
|
|
("RES_5", DWORD, 1),
|
|
("ZF", DWORD, 1),
|
|
("SF", DWORD, 1),
|
|
("TF", DWORD, 1),
|
|
("IF", DWORD, 1),
|
|
("DF", DWORD, 1),
|
|
("OF", DWORD, 1),
|
|
("IOPL_1", DWORD, 1),
|
|
("IOPL_2", DWORD, 1),
|
|
("NT", DWORD, 1),
|
|
("RES_15", DWORD, 1),
|
|
("RF", DWORD, 1),
|
|
("VM", DWORD, 1),
|
|
("AC", DWORD, 1),
|
|
("VIF", DWORD, 1),
|
|
("VIP", DWORD, 1),
|
|
("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
|
|
|
|
def set_raw(self, value):
|
|
x = DWORD.from_address(ctypes.addressof(self))
|
|
x.value = value
|
|
return None
|
|
|
|
def dump(self):
|
|
res = []
|
|
for name in [x[0] for x in self._fields_]:
|
|
if name.startswith("RES_"):
|
|
continue
|
|
if getattr(self, name):
|
|
res.append(name)
|
|
return "|".join(res)
|
|
|
|
def __repr__(self):
|
|
return hex(self)
|
|
|
|
def __hex__(self):
|
|
if self.raw == 0:
|
|
return "{0}({1})".format(type(self).__name__, hex(self.raw))
|
|
return "{0}({1}:{2})".format(type(self).__name__, hex(self.raw), self.dump())
|
|
|
|
raw = property(get_raw, set_raw)
|
|
"""Raw value of the eflags
|
|
|
|
:type: :class:`int`
|
|
"""
|
|
|
|
|
|
class EDr7(ctypes.Structure):
|
|
"Flag view of the DR7 register"
|
|
_fields_ = [("L0", DWORD, 1),
|
|
("G0", DWORD, 1),
|
|
("L1", DWORD, 1),
|
|
("G1", DWORD, 1),
|
|
("L2", DWORD, 1),
|
|
("G2", DWORD, 1),
|
|
("L3", DWORD, 1),
|
|
("G3", DWORD, 1),
|
|
("LE", DWORD, 1),
|
|
("GE", DWORD, 1),
|
|
("RES_1", DWORD, 3),
|
|
("GD", DWORD, 1),
|
|
("RES_1", DWORD, 2),
|
|
("RW0", DWORD, 2),
|
|
("LEN0", DWORD, 2),
|
|
("RW1", DWORD, 2),
|
|
("LEN1", DWORD, 2),
|
|
("RW2", DWORD, 2),
|
|
("LEN2", DWORD, 2),
|
|
("RW3", DWORD, 2),
|
|
("LEN3", DWORD, 2),
|
|
]
|
|
|
|
fields = [f[0] for f in _fields_]
|
|
"""The fields of the structure"""
|
|
|
|
class ECONTEXTBase(object):
|
|
"""DAT CONTEXT"""
|
|
default_dump = ()
|
|
pc_reg = ''
|
|
special_reg_type = {}
|
|
|
|
|
|
def regs(self, to_dump=None):
|
|
"""Return the name and values of the registers
|
|
|
|
:returns: [(reg_name, value)] -- A :class:`list` of :class:`tuple`"""
|
|
res = []
|
|
if to_dump is None:
|
|
to_dump = self.default_dump
|
|
for name in to_dump:
|
|
value = getattr(self, name)
|
|
if name in self.special_reg_type:
|
|
value = self.special_reg_type[name](value)
|
|
res.append((name, value))
|
|
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)))
|
|
return None
|
|
|
|
def get_pc(self):
|
|
return getattr(self, self.pc_reg)
|
|
|
|
def set_pc(self, value):
|
|
return setattr(self, self.pc_reg, value)
|
|
|
|
pc = property(get_pc, set_pc, None, "Program Counter register (EIP or RIP)")
|
|
|
|
@property
|
|
def EEFlags(self):
|
|
"""Enhanced view of the Eflags (you also have ``EFlags`` for the raw value)
|
|
|
|
:type: :class:`EEflags`
|
|
"""
|
|
off = type(self).EFlags.offset
|
|
x = EEflags.from_address(ctypes.addressof(self) + off)
|
|
x.self = self
|
|
return x
|
|
|
|
@property
|
|
def EDr7(self):
|
|
"""Enhanced view of the DR7 register (you also have ``Dr7`` for the raw value)
|
|
|
|
:type: :class:`EDr7`
|
|
"""
|
|
off = type(self).Dr7.offset
|
|
x = EDr7.from_address(ctypes.addressof(self) + off)
|
|
x.self = self
|
|
return x
|
|
|
|
class ECONTEXT32(ECONTEXTBase, CONTEXT32):
|
|
default_dump = ('Eip', 'Esp', 'Eax', 'Ebx', 'Ecx', 'Edx', 'Ebp', 'Edi', 'Esi', 'EFlags')
|
|
pc_reg = 'Eip'
|
|
fields = [f[0] for f in CONTEXT32._fields_]
|
|
"""The fields of the structure"""
|
|
|
|
class ECONTEXTWOW64(ECONTEXTBase, WOW64_CONTEXT):
|
|
default_dump = ('Eip', 'Esp', 'Eax', 'Ebx', 'Ecx', 'Edx', 'Ebp', 'Edi', 'Esi', 'EFlags')
|
|
pc_reg = 'Eip'
|
|
fields = [f[0] for f in WOW64_CONTEXT._fields_]
|
|
"""The fields of the structure"""
|
|
|
|
|
|
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'
|
|
fields = [f[0] for f in CONTEXT64._fields_]
|
|
"""The fields of the structure"""
|
|
|
|
@classmethod
|
|
def new_aligned(cls):
|
|
"""Return a new :class:`ECONTEXT64` aligned on 16 bits
|
|
|
|
temporary workaround or horrible hack ? choose your side
|
|
"""
|
|
size = ctypes.sizeof(cls)
|
|
nb_qword = (size + 8) / ctypes.sizeof(ULONGLONG)
|
|
buffer = (nb_qword * ULONGLONG)()
|
|
struct_address = ctypes.addressof(buffer)
|
|
if (struct_address & 0xf) not in [0, 8]:
|
|
raise ValueError("ULONGLONG array not aligned on 8")
|
|
if (struct_address & 0xf) == 8:
|
|
struct_address += 8
|
|
self = cls.from_address(struct_address)
|
|
# Keep the raw buffer alive
|
|
self._buffer = buffer
|
|
return self
|
|
|
|
def bitness():
|
|
"""Return 32 or 64"""
|
|
import platform
|
|
bits = platform.architecture()[0]
|
|
return int(bits[:2])
|
|
|
|
if bitness() == 32:
|
|
ECONTEXT = ECONTEXT32
|
|
else:
|
|
ECONTEXT = ECONTEXT64
|
|
|
|
|
|
class EEXCEPTION_POINTERS(ctypes.Structure):
|
|
_fields_ = [
|
|
("ExceptionRecord", ctypes.POINTER(EEXCEPTION_RECORD)),
|
|
("ContextRecord", ctypes.POINTER(ECONTEXT)),
|
|
]
|
|
|
|
def dump(self):
|
|
"""Dump (print) the EEXCEPTION_POINTERS"""
|
|
record = self.ExceptionRecord[0]
|
|
print("Dumping Exception: ")
|
|
print(" ExceptionCode = {0} at {1}".format(record.ExceptionCode, hex(record.ExceptionAddress)))
|
|
regs = self.ContextRecord[0].regs()
|
|
for name, value in regs:
|
|
print(" {0} -> {1}".format(name, hex(value)))
|
|
|
|
|
|
class VectoredException(object):
|
|
"""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)
|
|
self.func = func
|
|
v = self.func_type(self.decorator)
|
|
v.self = self
|
|
return v
|
|
|
|
def decorator(self, exception_pointers):
|
|
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
|
|
|
|
|
|
class VectoredExceptionHandler(object):
|
|
def __init__(self, pos, handler):
|
|
self.handler = VectoredException(handler)
|
|
self.pos = pos
|
|
|
|
def __enter__(self):
|
|
self.value = windows.winproxy.AddVectoredExceptionHandler(self.pos, self.handler)
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_value, traceback):
|
|
windows.winproxy.RemoveVectoredExceptionHandler(self.value)
|
|
return False
|
|
|
|
class DumpContextOnException(VectoredExceptionHandler):
|
|
def __init__(self, exit=False):
|
|
self.exit = exit
|
|
super(DumpContextOnException, self).__init__(self.print_context_result)
|
|
|
|
def print_context_result(self, exception_pointers):
|
|
except_record = exception_pointers[0].ExceptionRecord[0]
|
|
exception_pointers[0].dump()
|
|
sys.stdout.flush()
|
|
if self.exit:
|
|
windows.current_process.exit()
|
|
return 0
|
|
|