diff --git a/__init__.py b/__init__.py index 9113bf4..05ed690 100644 --- a/__init__.py +++ b/__init__.py @@ -11,7 +11,7 @@ Exported: current_thread : :class:`windows.winobject.CurrentThread` """ -import windows.winproxy +from . import winproxy from .utils import VirtualProtected from .winobject import System, CurrentProcess, CurrentThread @@ -19,9 +19,7 @@ system = System() current_process = CurrentProcess() current_thread = CurrentThread() - -__all__ = ["system", "VirtualProtected", 'current_process', 'current_thread'] - -# Late import +# Late import: other imports should go here # Do not move it: risk of circular import -import windows.vectored_exception \ No newline at end of file + +__all__ = ["system", "VirtualProtected", 'current_process', 'current_thread', 'winproxy'] diff --git a/dbgprint.py b/dbgprint.py index 1b75584..a09df90 100644 --- a/dbgprint.py +++ b/dbgprint.py @@ -3,32 +3,35 @@ import sys import logging import inspect -options = {'active':False, 'cats':None} +options = {'active': False, 'cats': None} + def get_stack_func_name(lvl): info = inspect.stack()[lvl] return info[0], info[3] + def do_dbgprint(msg, type=None): if (options['cats'] is None) or type.upper() in options['cats']: frame, func = get_stack_func_name(2) logger = logging.getLogger(frame.f_globals['__name__'] + ":" + func) logger.debug(msg) - - + + def do_nothing(*args, **kwargs): return None + def parse_option(s): if s[0] == "=": s = s[1:] if s: cats = [x.upper() for x in s.split('-')] options['cats'] = cats - - formt='DBG|%(name)s|%(message)s' + + formt = 'DBG|%(name)s|%(message)s' logging.basicConfig(format=formt, level=logging.DEBUG) - + try: if 'DBGPRINT' in os.environ: parse_option(os.environ['DBGPRINT']) @@ -42,4 +45,4 @@ try: except Exception as e: dbgprint = do_nothing print("dbgprint Error: {0}({1})".format(type(e), e)) - x = type(e), e \ No newline at end of file + x = type(e), e diff --git a/hooks.py b/hooks.py index 9ebb4fc..f5cee61 100644 --- a/hooks.py +++ b/hooks.py @@ -3,7 +3,8 @@ import ctypes import windows.utils as utils from . import native_exec -from .generated_def import winfuncs, windef +from .generated_def import winfuncs +from .generated_def.windef import PAGE_EXECUTE_READWRITE from .generated_def.winstructs import * @@ -15,6 +16,7 @@ class Callback(object): func._types_info = self.types return func + class KnownCallback(object): types = () @@ -22,6 +24,7 @@ class KnownCallback(object): func._types_info = self.types return func + def add_callback_to_module(callback): setattr(sys.modules[__name__], type(callback).__name__, callback) @@ -31,13 +34,12 @@ for func in winfuncs.functions: callback_name = func + "Callback" class CallBackDeclaration(KnownCallback): - types = (prototype._restype_,) + prototype._argtypes_ + types = (prototype._restype_,) + prototype._argtypes_ CallBackDeclaration.__name__ = callback_name add_callback_to_module(CallBackDeclaration()) - class IATHook(object): """Look at my hook <3""" @@ -64,17 +66,16 @@ class IATHook(object): return res def enable(self): - with utils.VirtualProtected(self.entry.addr, ctypes.sizeof(PVOID), windef.PAGE_EXECUTE_READWRITE): + with utils.VirtualProtected(self.entry.addr, ctypes.sizeof(PVOID), PAGE_EXECUTE_READWRITE): self.entry.value = self.stub self.is_enable = True def disable(self): - with utils.VirtualProtected(self.entry.addr, ctypes.sizeof(PVOID), windef.PAGE_EXECUTE_READWRITE): + with utils.VirtualProtected(self.entry.addr, ctypes.sizeof(PVOID), PAGE_EXECUTE_READWRITE): self.entry.value = self.entry.nonhookvalue self.is_enable = False def hook_callback(self, *args): - original_args = args adapted_args = [] for value, type in zip(args, self.original_types[1:]): if type == ctypes.c_wchar_p: @@ -88,4 +89,4 @@ class IATHook(object): if args == (): args = adapted_args return self.realfunction(*args) - return self.callback(*adapted_args, real_function=real_function) \ No newline at end of file + return self.callback(*adapted_args, real_function=real_function) diff --git a/injection.py b/injection.py index 08db0cb..3140aac 100644 --- a/injection.py +++ b/injection.py @@ -1,6 +1,3 @@ -import sys -import os - import windows import windows.utils as utils @@ -21,12 +18,7 @@ def get_loadlib_getproc(target): # 32 to 32 injection def generate_python_exec_shellcode_32(target, PYDLL_addr, PyInit, PyRun, PYCODE_ADDR): - #LoadLibraryA = utils.get_func_addr('kernel32', 'LoadLibraryA') - #GetProcAddress = utils.get_func_addr('kernel32', 'GetProcAddress') LoadLibraryA, GetProcAddress = get_loadlib_getproc(target) - print("LoadLibraryA = {0}".format(hex(LoadLibraryA))) - print("LoadLibraryA = {0}".format(hex(GetProcAddress))) - code = x86.MultipleInstr() # Load python27.dll code += x86.Push(PYDLL_addr) @@ -52,6 +44,7 @@ def generate_python_exec_shellcode_32(target, PYDLL_addr, PyInit, PyRun, PYCODE_ code += x86.Ret() return code.get_code() + # 64 to 64 injection def generate_python_exec_shellcode_64(target, PYDLL_addr, PyInit, PyRun, PYCODE_ADDR): @@ -112,7 +105,6 @@ def inject_python_command(process, code_injected, PYDLL="python27.dll\x00"): process.write_memory(remote_addr, PYDLL) remote_addr += len(PYDLL) - PyInitT_ADDR = remote_addr process.write_memory(remote_addr, PyInitT) remote_addr += len(PyInitT) @@ -127,14 +119,14 @@ def inject_python_command(process, code_injected, PYDLL="python27.dll\x00"): SHELLCODE_ADDR = remote_addr if process.bitness == 32: - shellcode = generate_python_exec_shellcode_32(process, PYDLL_addr, PyInitT_ADDR, Pyrun_ADDR, PYCODE_ADDR) + shellcode_generator = generate_python_exec_shellcode_32 else: - shellcode = generate_python_exec_shellcode_64(process, PYDLL_addr, PyInitT_ADDR, Pyrun_ADDR, PYCODE_ADDR) + shellcode_generator = generate_python_exec_shellcode_64 + shellcode = shellcode_generator(process, PYDLL_addr, PyInitT_ADDR, Pyrun_ADDR, PYCODE_ADDR) process.write_memory(SHELLCODE_ADDR, shellcode) return SHELLCODE_ADDR - def execute_python_code(process, code): print("me = {0}".format(windows.current_process.bitness)) print("him = {0}".format(process.bitness)) diff --git a/native_exec/__init__.py b/native_exec/__init__.py index 804edf7..f0d7a52 100644 --- a/native_exec/__init__.py +++ b/native_exec/__init__.py @@ -1 +1,3 @@ -from .native_function import generate_callback_stub, create_function \ No newline at end of file +from .native_function import generate_callback_stub, create_function + +__all__ = ["generate_callback_stub", "create_function"] diff --git a/native_exec/cpuid.py b/native_exec/cpuid.py index 400a7ae..8961b03 100644 --- a/native_exec/cpuid.py +++ b/native_exec/cpuid.py @@ -6,24 +6,28 @@ import simple_x86 as x86 import simple_x64 as x64 from windows.generated_def.winstructs import * + def bitness(): """Return 32 or 64""" import platform bits = platform.architecture()[0] return int(bits[:2]) + class X86CpuidResult(ctypes.Structure): _fields_ = [("EAX", DWORD), ("EBX", DWORD), ("ECX", DWORD), ("EDX", DWORD)] - + + class X64CpuidResult(ctypes.Structure): _fields_ = [("RAX", ULONG64), ("RBX", ULONG64), ("RCX", ULONG64), ("RDX", ULONG64)] + class X86IntelCpuidFamilly(ctypes.Structure): _fields_ = [("SteppingID", DWORD, 4), ("ModelID", DWORD, 4), @@ -34,6 +38,7 @@ class X86IntelCpuidFamilly(ctypes.Structure): ("ExtendedFamily", DWORD, 8), ("Reserved", DWORD, 2)] + class X86AmdCpuidFamilly(ctypes.Structure): _fields_ = [("SteppingID", DWORD, 4), ("ModelID", DWORD, 4), @@ -43,6 +48,7 @@ class X86AmdCpuidFamilly(ctypes.Structure): ("ExtendedFamily", DWORD, 8), ("Reserved", DWORD, 2)] + cpuid32_code = x86.MultipleInstr() cpuid32_code += x86.Push('EDI') cpuid32_code += x86.Mov('EAX', x86.mem('[ESP + 0x8]')) @@ -56,6 +62,7 @@ cpuid32_code += x86.Pop('EDI') cpuid32_code += x86.Ret() do_cpuid32 = native_function.create_function(cpuid32_code.get_code(), [DWORD, DWORD, PVOID]) + cpuid64_code = x64.MultipleInstr() cpuid64_code += x64.Mov('RAX', 'RCX') cpuid64_code += x64.Mov('R10', 'RDX') @@ -68,34 +75,40 @@ cpuid64_code += x64.Mov(x64.mem('[R10 + 0x18]'), 'RDX') cpuid64_code += x64.Ret() do_cpuid64 = native_function.create_function(cpuid64_code.get_code(), [DWORD, DWORD, PVOID]) + def x86_cpuid(req): cpuid_res = X86CpuidResult() do_cpuid32(req, ctypes.addressof(cpuid_res)) return cpuid_res - + + def x64_cpuid(req): cpuid_res = X64CpuidResult() do_cpuid64(req, ctypes.addressof(cpuid_res)) # For now assembler cannot do 32bits register in x64 return X86CpuidResult(cpuid_res.RAX, cpuid_res.RBX, cpuid_res.RCX, cpuid_res.RDX) - + + if bitness() == 32: do_cpuid = x86_cpuid else: do_cpuid = x64_cpuid + def get_vendor_id(): cpuid_res = do_cpuid(0) - return struct.pack(" in mem access".format(index)) try: scale = int(scale, 0) - except ValueError as e: + except ValueError: raise ValueError("Invalid scale <{0}> in mem access".format(scale)) parsed_items['scale'] = scale parsed_items['index'] = index else: # displacement / base / index alone if X64.is_reg(item): - if not 'base' in parsed_items: + if 'base' not in parsed_items: parsed_items['base'] = item continue # Already have base + index -> cannot avec another register in expression @@ -216,7 +220,7 @@ def mem(data): continue try: disp = int(item, 0) - except ValueError as e: + except ValueError: raise ValueError("Invalid base/index or displacement <{0}> in mem access".format(item)) if 'disp' in parsed_items: raise ValueError("Multiple displacement in mem expression <{0}>".format(data)) @@ -224,11 +228,10 @@ def mem(data): return create_displacement(**parsed_items) - class X64RegisterSelector(object): - reg_opcode = {v : BitArray.from_int(size=3, x=i) for i, v in enumerate(reg_order)} - new_reg_opcode = {v : BitArray.from_int(size=3, x=i) for i, v in enumerate(new_reg_order)} + reg_opcode = {v: BitArray.from_int(size=3, x=i) for i, v in enumerate(reg_order)} + new_reg_opcode = {v: BitArray.from_int(size=3, x=i) for i, v in enumerate(new_reg_order)} def accept_arg(self, args, instr_state): x = args[0] @@ -248,6 +251,7 @@ class X64RegisterSelector(object): except KeyError: return cls.new_reg_opcode[name.upper()] + class FixedRegister(object): def __init__(self, register): self.reg = register.upper() @@ -260,31 +264,37 @@ class FixedRegister(object): RegisterRax = lambda: FixedRegister('RAX') + class RawBits(BitArray): def accept_arg(self, args, instr_state): return (0, self.copy(), None) + class ImmediatOverflow(ValueError): pass + def accept_as_8immediat(x): try: return struct.pack("> 3), X64RegisterSelector()), (RawBits.from_int(8, 0x68), Imm32())] + class Pop(Instruction): encoding = [(RawBits.from_int(5, 0x58 >> 3), X64RegisterSelector())] + class Call(Instruction): encoding = [(RawBits.from_int(8, 0xff), Slash(2))] + class Xchg(Instruction): default_32_bits = True encoding = [(RawBits.from_int(5, 0x90 >> 3), RegisterRax(), X64RegisterSelector()), (RawBits.from_int(5, 0x90 >> 3), X64RegisterSelector(), RegisterRax())] + class Ret(Instruction): encoding = [(RawBits.from_int(8, 0xc3),)] + class Int3(Instruction): encoding = [(RawBits.from_int(8, 0xcc),)] + class Dec(Instruction): default_32_bits = True encoding = [(RawBits.from_int(8, 0xff), Slash(1))] + class Inc(Instruction): default_32_bits = True encoding = [(RawBits.from_int(8, 0xff), Slash(0))] + class Add(Instruction): default_32_bits = True encoding = [(RawBits.from_int(8, 0x05), RegisterRax(), Imm32()), (RawBits.from_int(8, 0x81), Slash(0), Imm32()), - #(RawBits.from_int(8, 0x01), ModRM(ModRM_REG64__REG64, ModRM_REG__DEREF_REG, ModRM_REG__DEREF_REG_IMM, ModRM_REG__DEREF_BASE_INDEX, )),] - (RawBits.from_int(8, 0x01), ModRM([ModRM_REG64__REG64, ModRM_REG64__MEM])),] + (RawBits.from_int(8, 0x01), ModRM([ModRM_REG64__REG64, ModRM_REG64__MEM]))] class Sub(Instruction): @@ -642,21 +672,26 @@ class Sub(Instruction): encoding = [(RawBits.from_int(8, 0x2D), RegisterRax(), Imm32()), (RawBits.from_int(8, 0x81), Slash(5), Imm32())] + class Out(Instruction): encoding = [(RawBits.from_int(8, 0xee), FixedRegister('DX'), FixedRegister('AL')), - (RawBits.from_int(16, 0x66ef), FixedRegister('DX'), FixedRegister('AX')), # Fuck-it hardcoded prefix for now + (RawBits.from_int(16, 0x66ef), FixedRegister('DX'), FixedRegister('AX')), # Fuck-it hardcoded prefix for now (RawBits.from_int(8, 0xef), FixedRegister('DX'), FixedRegister('EAX'))] + class In(Instruction): encoding = [(RawBits.from_int(8, 0xec), FixedRegister('AL'), FixedRegister('DX')), - (RawBits.from_int(16, 0x66ed), FixedRegister('AX'), FixedRegister('DX')), # Fuck-it hardcoded prefix for now + (RawBits.from_int(16, 0x66ed), FixedRegister('AX'), FixedRegister('DX')), # Fuck-it hardcoded prefix for now (RawBits.from_int(8, 0xed), FixedRegister('EAX'), FixedRegister('DX'))] + class Cpuid(Instruction): encoding = [(RawBits.from_int(16, 0x0fa2),)] + class JmpImm(object): accept_as_Ximmediat = (None) + def __init__(self, sub): self.sub = sub @@ -672,33 +707,41 @@ class JmpImm(object): return (None, None, None) return (1, BitArray.from_string(jmp_imm), None) + class JmpImm8(JmpImm): accept_as_Ximmediat = staticmethod(accept_as_8immediat) + class JmpImm32(JmpImm): accept_as_Ximmediat = staticmethod(accept_as_32immediat) + class Jmp(JmpType): encoding = [(RawBits.from_int(8, 0xeb), JmpImm8(2)), (RawBits.from_int(8, 0xe9), JmpImm32(5)), (RawBits.from_int(13, 0xffe0 >> 3), X64RegisterSelector())] + class Jz(JmpType): encoding = [(RawBits.from_int(8, 0x74), JmpImm8(2)), (RawBits.from_int(16, 0x0f84), JmpImm32(6))] + class Jnz(JmpType): encoding = [(RawBits.from_int(8, 0x75), JmpImm8(2)), (RawBits.from_int(16, 0x0f85), JmpImm32(6))] + class Jb(JmpType): encoding = [(RawBits.from_int(8, 0x72), JmpImm8(2)), (RawBits.from_int(16, 0x0f82), JmpImm32(6))] + class Jbe(JmpType): encoding = [(RawBits.from_int(8, 0x76), JmpImm8(2)), (RawBits.from_int(16, 0x0f86), JmpImm32(6))] + class Jnb(JmpType): encoding = [(RawBits.from_int(8, 0x73), JmpImm8(2)), (RawBits.from_int(16, 0x0f83), JmpImm32(6))] @@ -708,46 +751,57 @@ class Lea(Instruction): refuse_reverse = True encoding = [(RawBits.from_int(8, 0x8d), ModRM([ModRM_REG64__MEM], accept_reverse=False, has_direction_bit=False))] + class Mov(Instruction): - default_32_bits = True - encoding = [(Mov_RAX_OFF64(),), (Mov_OFF64_RAX(),), (RawBits.from_int(8, 0x89), ModRM([ModRM_REG64__REG64, ModRM_REG64__MEM])), + default_32_bits = True + encoding = [(Mov_RAX_OFF64(),), (Mov_OFF64_RAX(),), (RawBits.from_int(8, 0x89), ModRM([ModRM_REG64__REG64, ModRM_REG64__MEM])), (RawBits.from_int(5, 0xb8 >> 3), X64RegisterSelector(), Imm64())] + class Cmp(Instruction): default_32_bits = True encoding = [(RawBits.from_int(8, 0x3d), RegisterRax(), Imm32()), (RawBits.from_int(8, 0x81), Slash(7), Imm32()), - (RawBits.from_int(8, 0x3b), ModRM([ModRM_REG64__REG64, ModRM_REG64__MEM])),] + (RawBits.from_int(8, 0x3b), ModRM([ModRM_REG64__REG64, ModRM_REG64__MEM]))] + class Xor(Instruction): default_32_bits = True encoding = [(RawBits.from_int(8, 0x31), ModRM([ModRM_REG64__REG64, ModRM_REG64__MEM]))] + class Nop(Instruction): encoding = [(RawBits.from_int(8, 0x90),)] + class Retf(Instruction): default_32_bits = True encoding = [(RawBits.from_int(8, 0xcb),)] + class Retf32(Instruction): encoding = [(RawBits.from_int(8, 0xcb),)] + class _NopArtifact(Nop): pass + def JmpAt(addr): code = MultipleInstr() code += Mov('RAX', addr) code += Jmp('RAX') return code + class Label(object): def __init__(self, name): self.name = name + class MultipleInstr(object): JUMP_SIZE = 6 + def __init__(self, init_instrs=()): self.instrs = {} self.labels = {} @@ -822,12 +876,12 @@ class MultipleInstr(object): return def _reduce_shellcode(self): - to_remove = [offset for offset,instr in self.instrs.items() if type(instr) == _NopArtifact] + to_remove = [offset for offset, instr in self.instrs.items() if type(instr) == _NopArtifact] while to_remove: self._remove_nop_artifact(to_remove[0]) # _remove_nop_artifact will change the offsets of the nop # Need to refresh these offset - to_remove = [offset for offset,instr in self.instrs.items() if type(instr) == _NopArtifact] + to_remove = [offset for offset, instr in self.instrs.items() if type(instr) == _NopArtifact] def _remove_nop_artifact(self, offset): """Remove a NOP from the shellcode, adjust jump and labels""" @@ -851,7 +905,7 @@ class MultipleInstr(object): # dec offset of all Label after the NOP for name, labeloffset in self.labels.items(): if labeloffset > offset: - self.labels[name] = labeloffset - 1 + self.labels[name] = labeloffset - 1 # dec offset of all instr after the NOP new_instr = {} @@ -925,5 +979,4 @@ if in_IDA: midap.here(idc.MinEA()).write(s.get_code()) idc.MakeFunction(idc.MinEA()) - #tst() - + # tst() diff --git a/native_exec/simple_x86.py b/native_exec/simple_x86.py index 51e9c83..68ec2b8 100644 --- a/native_exec/simple_x86.py +++ b/native_exec/simple_x86.py @@ -1,6 +1,6 @@ import collections import struct -import sys + class BitArray(object): def __init__(self, size, bits): @@ -61,9 +61,11 @@ class BitArray(object): x = x & ((2 ** size) - 1) return cls(size, bin(x)[2:]) + # Prefix class Prefix(object): PREFIX_VALUE = None + def __init__(self, next=None): self.next = next @@ -73,36 +75,38 @@ class Prefix(object): def get_code(self): return chr(self.PREFIX_VALUE) + self.next.get_code() -def create_prefix(name, value): - prefix_type = type(name + "Type", (Prefix,), {'PREFIX_VALUE' : value}) - setattr(sys.modules[__name__], name, prefix_type()) -create_prefix('LockPrefix', 0xf0) -create_prefix('Repne', 0xf2) -create_prefix('Rep', 0xf3) -create_prefix('SSPrefix', 0x36) -create_prefix('CSPrefix', 0x2e) -create_prefix('DSPrefix', 0x3e) -create_prefix('ESPrefix', 0x26) -create_prefix('FSPrefix', 0x64) -create_prefix('GSPrefix', 0x65) -create_prefix('OperandSizeOverride', 0x66) -create_prefix('AddressSizeOverride', 0x67) +def create_prefix(name, value): + prefix_type = type(name + "Type", (Prefix,), {'PREFIX_VALUE': value}) + return prefix_type() + +LockPrefix = create_prefix('LockPrefix', 0xf0) +Repne = create_prefix('Repne', 0xf2) +Rep = create_prefix('Rep', 0xf3) +SSPrefix = create_prefix('SSPrefix', 0x36) +CSPrefix = create_prefix('CSPrefix', 0x2e) +DSPrefix = create_prefix('DSPrefix', 0x3e) +ESPrefix = create_prefix('ESPrefix', 0x26) +FSPrefix = create_prefix('FSPrefix', 0x64) +GSPrefix = create_prefix('GSPrefix', 0x65) +OperandSizeOverride = create_prefix('OperandSizeOverride', 0x66) +AddressSizeOverride = create_prefix('AddressSizeOverride', 0x67) # Main informations about X86 mem_access = collections.namedtuple('mem_access', ['base', 'index', 'scale', 'disp', 'prefix']) x86_regs = ['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI'] x86_16bits_regs = ['AX', 'CX', 'DX', 'BX', 'SP', 'BP', 'SI', 'DI'] -x86_segment_selectors = {'CS' : CSPrefix, 'DS' : DSPrefix, 'ES' : ESPrefix, 'SS' : SSPrefix, - 'FS': FSPrefix, 'GS' : GSPrefix} +x86_segment_selectors = {'CS': CSPrefix, 'DS': DSPrefix, 'ES': ESPrefix, 'SS': SSPrefix, + 'FS': FSPrefix, 'GS': GSPrefix} + class X86(object): @staticmethod def is_reg(name): try: return name.upper() in x86_regs + x86_16bits_regs - except AttributeError: # Not a string + except AttributeError: # Not a string return False @staticmethod @@ -141,9 +145,11 @@ def create_displacement(base=None, index=None, scale=None, disp=0, prefix=None): raise ValueError("Cannot create displacement with index == ESP") return mem_access(base, index, scale, disp, prefix) + def deref(disp): return create_displacement(disp=disp) + def mem(data): """Parse a memory access string of format [EXPR] or seg:[EXPR] EXPR may describe: BASE | INDEX * SCALE | DISPLACEMENT or any combinaison (in this order) @@ -165,7 +171,7 @@ def mem(data): # A l'arrache.. j'aime pas le parsing de trucs data = data[1:-1] items = data.split("+") - parsed_items = {'prefix' : prefix} + parsed_items = {'prefix': prefix} for item in items: item = item.strip() # Index * scale @@ -183,7 +189,7 @@ def mem(data): raise NotImplementedError("16bits modrm") try: scale = int(scale, 0) - except ValueError as e: + except ValueError: raise ValueError("Invalid scale <{0}> in mem access".format(scale)) parsed_items['scale'] = scale parsed_items['index'] = index @@ -192,7 +198,7 @@ def mem(data): if X86.is_reg(item): if X86.reg_size(item) == 16: raise NotImplementedError("16bits modrm") - if not 'base' in parsed_items: + if 'base' not in parsed_items: parsed_items['base'] = item continue # Already have base + index -> cannot avec another register in expression @@ -202,19 +208,19 @@ def mem(data): continue try: disp = int(item, 0) - except ValueError as e: + except ValueError: raise ValueError("Invalid base/index or displacement <{0}> in mem access".format(item)) if 'disp' in parsed_items: raise ValueError("Multiple displacement in mem expression <{0}>".format(data)) parsed_items['disp'] = disp return create_displacement(**parsed_items) -# Helper to get the BitArray associated to a register +# Helper to get the BitArray associated to a register class X86RegisterSelector(object): - size = 3 # bits - reg_opcode = {v : BitArray.from_int(size=3, x=i) for i, v in enumerate(x86_regs)} - reg_opcode.update({v : BitArray.from_int(size=3, x=i) for i, v in enumerate(x86_16bits_regs)}) + size = 3 # bits + reg_opcode = {v: BitArray.from_int(size=3, x=i) for i, v in enumerate(x86_regs)} + reg_opcode.update({v: BitArray.from_int(size=3, x=i) for i, v in enumerate(x86_16bits_regs)}) def accept_arg(self, args, instr_state): x = args[0] @@ -227,8 +233,8 @@ class X86RegisterSelector(object): def get_reg_bits(cls, name): return cls.reg_opcode[name.upper()] -## Instruction Parameters +# Instruction Parameters class FixedRegister(object): def __init__(self, register): self.reg = register.upper() @@ -241,28 +247,32 @@ class FixedRegister(object): RegisterEax = lambda: FixedRegister('EAX') + class RawBits(BitArray): def accept_arg(self, args, instr_state): return (0, self) + # Immediat value logic # All 8/16 bits stuff are sign extended - class ImmediatOverflow(ValueError): pass + def accept_as_8immediat(x): try: return struct.pack("> 3), X86RegisterSelector()), (RawBits.from_int(8, 0x68), Imm32())] + class Pop(Instruction): encoding = [(RawBits.from_int(5, 0x58 >> 3), X86RegisterSelector())] + class Dec(Instruction): encoding = [(RawBits.from_int(5, 0x48 >> 3), X86RegisterSelector())] + class Inc(Instruction): encoding = [(RawBits.from_int(5, 0x40 >> 3), X86RegisterSelector()), - (RawBits.from_int(8, 0xff), Slash(0)),] + (RawBits.from_int(8, 0xff), Slash(0))] + class Add(Instruction): encoding = [(RawBits.from_int(8, 0x05), RegisterEax(), Imm32()), (RawBits.from_int(8, 0x81), Slash(0), Imm32()), - (RawBits.from_int(8, 0x01), ModRM([ModRM_REG__REG, ModRM_REG__MEM])),] + (RawBits.from_int(8, 0x01), ModRM([ModRM_REG__REG, ModRM_REG__MEM]))] + class Sub(Instruction): encoding = [(RawBits.from_int(8, 0x2D), RegisterEax(), Imm32()), (RawBits.from_int(8, 0x81), Slash(5), Imm32())] + class Mov(Instruction): encoding = [(RawBits.from_int(8, 0x89), ModRM([ModRM_REG__REG, ModRM_REG__MEM])), (RawBits.from_int(5, 0xb8 >> 3), X86RegisterSelector(), Imm32())] + class Movsb(Instruction): encoding = [(RawBits.from_int(8, 0xa4),)] + class Movsd(Instruction): encoding = [(RawBits.from_int(8, 0xa5),)] + class Lea(Instruction): encoding = [(RawBits.from_int(8, 0x8d), ModRM([ModRM_REG__MEM], accept_reverse=False, has_direction_bit=False))] + class Cmp(Instruction): encoding = [(RawBits.from_int(8, 0x3d), RegisterEax(), Imm32()), (RawBits.from_int(8, 0x81), Slash(7), Imm32()), - (RawBits.from_int(8, 0x3b), ModRM([ModRM_REG__REG, ModRM_REG__MEM])),] + (RawBits.from_int(8, 0x3b), ModRM([ModRM_REG__REG, ModRM_REG__MEM]))] + class Out(Instruction): encoding = [(RawBits.from_int(8, 0xee), FixedRegister('DX'), FixedRegister('AL')), - (RawBits.from_int(16, 0x66ef), FixedRegister('DX'), FixedRegister('AX')), # Fuck-it hardcoded prefix for now + (RawBits.from_int(16, 0x66ef), FixedRegister('DX'), FixedRegister('AX')), # Fuck-it hardcoded prefix for now (RawBits.from_int(8, 0xef), FixedRegister('DX'), FixedRegister('EAX'))] + class In(Instruction): encoding = [(RawBits.from_int(8, 0xec), FixedRegister('AL'), FixedRegister('DX')), - (RawBits.from_int(16, 0x66ed), FixedRegister('AX'), FixedRegister('DX')), # Fuck-it hardcoded prefix for now + (RawBits.from_int(16, 0x66ed), FixedRegister('AX'), FixedRegister('DX')), # Fuck-it hardcoded prefix for now (RawBits.from_int(8, 0xed), FixedRegister('EAX'), FixedRegister('DX'))] + class Xor(Instruction): encoding = [(RawBits.from_int(8, 0x31), ModRM([ModRM_REG__REG]))] + class Xchg(Instruction): encoding = [(RawBits.from_int(5, 0x90 >> 3), RegisterEax(), X86RegisterSelector()), (RawBits.from_int(5, 0x90 >> 3), X86RegisterSelector(), RegisterEax())] + class Call(Instruction): encoding = [(RawBits.from_int(8, 0xff), Slash(2))] + class Cpuid(Instruction): encoding = [(RawBits.from_int(16, 0x0fa2),)] + class Ret(Instruction): encoding = [(RawBits.from_int(8, 0xc3),)] + class Nop(Instruction): encoding = [(RawBits.from_int(8, 0x90),)] + class Retf(Instruction): encoding = [(RawBits.from_int(8, 0xcb),)] + class Int3(Instruction): encoding = [(RawBits.from_int(8, 0xcc),)] + class _NopArtifact(Nop): """Special NOP used in shellcode reduction""" pass + class Label(object): + def __init__(self, name): self.name = name + def JmpAt(addr): code = MultipleInstr() code += Push(addr) code += Ret() return code + class MultipleInstr(object): JUMP_SIZE = 6 + def __init__(self, init_instrs=()): self.instrs = {} self.labels = {} @@ -712,12 +770,12 @@ class MultipleInstr(object): return def _reduce_shellcode(self): - to_remove = [offset for offset,instr in self.instrs.items() if type(instr) == _NopArtifact] + to_remove = [offset for offset, instr in self.instrs.items() if type(instr) == _NopArtifact] while to_remove: self._remove_nop_artifact(to_remove[0]) # _remove_nop_artifact will change the offsets of the nop # Need to refresh these offset - to_remove = [offset for offset,instr in self.instrs.items() if type(instr) == _NopArtifact] + to_remove = [offset for offset, instr in self.instrs.items() if type(instr) == _NopArtifact] def _remove_nop_artifact(self, offset): # Remove a NOP from the shellcode @@ -741,7 +799,7 @@ class MultipleInstr(object): # dec offset of all Label after the NOP for name, labeloffset in self.labels.items(): if labeloffset > offset: - self.labels[name] = labeloffset - 1 + self.labels[name] = labeloffset - 1 # dec offset of all instr after the NOP new_instr = {} @@ -805,4 +863,4 @@ if in_IDA: def tst(): reset() midap.here(idc.MinEA()).write(s.get_code()) - idc.MakeFunction(idc.MinEA()) \ No newline at end of file + idc.MakeFunction(idc.MinEA()) diff --git a/native_exec/test_simple_x64.py b/native_exec/test_simple_x64.py index e1e55c3..00df47c 100644 --- a/native_exec/test_simple_x64.py +++ b/native_exec/test_simple_x64.py @@ -4,10 +4,12 @@ from simple_x64 import * disassembleur = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_64) disassembleur.detail = True + def disas(x): return list(disassembleur.disasm(x, 0)) -mnemonic_name_exception = {'movabs' : 'mov'} +mnemonic_name_exception = {'movabs': 'mov'} + class TestInstr(object): def __init__(self, instr_to_test, immediat_accepted=None): @@ -38,7 +40,7 @@ class TestInstr(object): if len(args) != len(capres_op): raise AssertionError("Expected {0} operands got {1}".format(len(args), len(capres_op))) for op_args, cap_op in zip(args, capres_op): - if isinstance(op_args, str): # Register + if isinstance(op_args, str): # Register if cap_op.type != capstone.x86.X86_OP_REG: raise AssertionError("Expected args {0} operands got {1}".format(op_args, capres_op)) if op_args.lower() != capres.reg_name(cap_op.reg).lower(): @@ -56,7 +58,7 @@ class TestInstr(object): raise AssertionError("Expected Memaccess <{0}> got {1}".format(memaccess, cap_op)) if memaccess.prefix is not None and capres.prefix[1] != x64_segment_selectors[memaccess.prefix].PREFIX_VALUE: try: - get_prefix = [n for n,x in x64_segment_selectors.items() if x.PREFIX_VALUE == capres.prefix[1]][0] + get_prefix = [n for n, x in x64_segment_selectors.items() if x.PREFIX_VALUE == capres.prefix[1]][0] except IndexError: get_prefix = None raise AssertionError("Expected Segment overide <{0}> got {1}".format(memaccess.prefix, get_prefix)) @@ -108,8 +110,6 @@ TestInstr(Push)(-1) TestInstr(Call)('RAX') TestInstr(Call)(mem('[RAX + RCX * 8]')) TestInstr(Cpuid)() - - TestInstr(Xchg)('RAX', 'RSP') assert Xchg('RAX', 'RCX').get_code() == Xchg('RCX', 'RAX').get_code() @@ -118,4 +118,4 @@ code += Nop() code += Rep + Nop() code += Ret() print(repr(code.get_code())) -assert code.get_code() == "\x90\xf3\x90\xc3" \ No newline at end of file +assert code.get_code() == "\x90\xf3\x90\xc3" diff --git a/native_exec/test_simple_x86.py b/native_exec/test_simple_x86.py index beb1c7d..101ab37 100644 --- a/native_exec/test_simple_x86.py +++ b/native_exec/test_simple_x86.py @@ -4,6 +4,7 @@ from simple_x86 import * disassembleur = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_32) disassembleur.detail = True + def disas(x): return list(disassembleur.disasm(x, 0)) @@ -42,7 +43,7 @@ class TestInstr(object): if len(args) != len(capres_op): raise AssertionError("Expected {0} operands got {1}".format(len(args), len(capres_op))) for op_args, cap_op in zip(args, capres_op): - if isinstance(op_args, str): # Register + if isinstance(op_args, str): # Register if cap_op.type != capstone.x86.X86_OP_REG: raise AssertionError("Expected args {0} operands got {1}".format(op_args, capres_op)) if op_args.lower() != capres.reg_name(cap_op.reg).lower(): @@ -60,7 +61,7 @@ class TestInstr(object): raise AssertionError("Expected Memaccess <{0}> got {1}".format(memaccess, cap_op)) if memaccess.prefix is not None and capres.prefix[1] != x86_segment_selectors[memaccess.prefix].PREFIX_VALUE: try: - get_prefix = [n for n,x in x86_segment_selectors.items() if x.PREFIX_VALUE == capres.prefix[1]][0] + get_prefix = [n for n, x in x86_segment_selectors.items() if x.PREFIX_VALUE == capres.prefix[1]][0] except IndexError: get_prefix = None raise AssertionError("Expected Segment overide <{0}> got {1}".format(memaccess.prefix, get_prefix)) @@ -87,38 +88,28 @@ TestInstr(Mov)('EDX', mem('[0x11223344]')) TestInstr(Mov)('EDX', mem('[ESP + EBP * 2 + 0x223344]')) TestInstr(Mov)(mem('[EBP + EBP * 2 + 0x223344]'), 'ESP') TestInstr(Mov)('ESI', mem('[ESI + EDI * 1]')) - TestInstr(Mov)('EAX', mem('fs:[0x30]')) TestInstr(Mov)('EDI', mem('gs:[EAX + ECX * 4]')) - TestInstr(Mov)('AX', 'AX') TestInstr(Mov)('SI', 'DI') - TestInstr(Mov)('AX', 'AX') TestInstr(Mov)('AX', mem('fs:[0x30]')) TestInstr(Mov)('AX', mem('fs:[EAX + 0x30]')) TestInstr(Mov)('AX', mem('fs:[EAX + ECX * 4+0x30]')) - TestInstr(Add)('EAX', 8) TestInstr(Add)('EAX', 0xffffffff) - TestInstr(Inc)('EAX') TestInstr(Inc)(mem('[0x42424242]')) - TestInstr(Lea)('EAX', mem('[EAX + 1]')) TestInstr(Lea)('ECX', mem('[EDI + -0xff]')) - TestInstr(Call)('EAX') TestInstr(Call)(mem('[EAX + ECX * 8]')) TestInstr(Cpuid)() - TestInstr(Movsb, expected_result='movsb byte ptr es:[edi], byte ptr [esi]')() TestInstr(Movsd, expected_result='movsd dword ptr es:[edi], dword ptr [esi]')() - TestInstr(Xchg)('EAX', 'ESP') assert Xchg('EAX', 'ECX').get_code() == Xchg('ECX', 'EAX').get_code() - code = MultipleInstr() code += Nop() code += Rep + Nop() diff --git a/network.py b/network.py index b96bf5e..4d576a4 100644 --- a/network.py +++ b/network.py @@ -56,7 +56,7 @@ class TCP4Connection(MIB_TCPROW_OWNER_PID): def __repr__(self): if not self.established: return "".format(self.local_addr, self.local_port) - return " {2}:{3}>".format(self.local_addr, self.local_port, self.remote_addr, self.remote_port) + return " {s.remote_addr}:{s.remote_port}>".format(s=self) class TCP6Connection(MIB_TCP6ROW_OWNER_PID): @@ -107,11 +107,13 @@ def get_MIB_TCPTABLE_OWNER_PID_from_buffer(buffer): class _GENERATED_MIB_TCPTABLE_OWNER_PID(ctypes.Structure): _fields_ = [ - ("dwNumEntries", DWORD), - ("table", TCP4Connection * nb_entry), - ] + ("dwNumEntries", DWORD), + ("table", TCP4Connection * nb_entry), + ] + return _GENERATED_MIB_TCPTABLE_OWNER_PID.from_buffer(buffer) + def get_MIB_TCP6TABLE_OWNER_PID_from_buffer(buffer): x = windows.generated_def.winstructs.MIB_TCP6TABLE_OWNER_PID.from_buffer(buffer) nb_entry = x.dwNumEntries @@ -119,33 +121,32 @@ def get_MIB_TCP6TABLE_OWNER_PID_from_buffer(buffer): # Struct _MIB_TCP6TABLE_OWNER_PID definitions class _GENERATED_MIB_TCP6TABLE_OWNER_PID(Structure): _fields_ = [ - ("dwNumEntries", DWORD), - ("table", TCP6Connection * nb_entry), - ] - return _GENERATED_MIB_TCP6TABLE_OWNER_PID.from_buffer(buffer) + ("dwNumEntries", DWORD), + ("table", TCP6Connection * nb_entry), + ] + return _GENERATED_MIB_TCP6TABLE_OWNER_PID.from_buffer(buffer) def get_tcp_ipv4_sockets(): size = ctypes.c_uint(0) try: - windows.winproxy.GetExtendedTcpTable(None, ctypes.byref(size), ulAf=windows.generated_def.windef.AF_INET) - except windows.winproxy.IphlpapiError as e: - pass # Allow us to set size to the needed value + windows.winproxy.GetExtendedTcpTable(None, ctypes.byref(size), ulAf=AF_INET) + except windows.winproxy.IphlpapiError: + pass # Allow us to set size to the needed value buffer = (ctypes.c_char * size.value)() - windows.winproxy.GetExtendedTcpTable(buffer, ctypes.byref(size), ulAf=windows.generated_def.windef.AF_INET) + windows.winproxy.GetExtendedTcpTable(buffer, ctypes.byref(size), ulAf=AF_INET) t = get_MIB_TCPTABLE_OWNER_PID_from_buffer(buffer) return list(t.table) + def get_tcp_ipv6_sockets(): size = ctypes.c_uint(0) try: - windows.winproxy.GetExtendedTcpTable(None, ctypes.byref(size), ulAf=windows.generated_def.windef.AF_INET6) - except windows.winproxy.IphlpapiError as e: - pass # Allow us to set size to the needed value + windows.winproxy.GetExtendedTcpTable(None, ctypes.byref(size), ulAf=AF_INET6) + except windows.winproxy.IphlpapiError: + pass # Allow us to set size to the needed value buffer = (ctypes.c_char * size.value)() - windows.winproxy.GetExtendedTcpTable(buffer, ctypes.byref(size), ulAf=windows.generated_def.windef.AF_INET6) + windows.winproxy.GetExtendedTcpTable(buffer, ctypes.byref(size), ulAf=AF_INET6) t = get_MIB_TCP6TABLE_OWNER_PID_from_buffer(buffer) return list(t.table) - - diff --git a/pe_parse.py b/pe_parse.py index 3609dd0..ba47580 100644 --- a/pe_parse.py +++ b/pe_parse.py @@ -1,6 +1,4 @@ -import sys import ctypes -import struct import windows import windows.hooks as hooks import windows.utils as utils @@ -22,10 +20,12 @@ def RedefineCtypesStruct(struct, replacement): NewStruct.__name__ = struct.__name__ return NewStruct + # type replacement based on name def transform_ctypes_fields(struct, replacement): return [(name, replacement.get(name, type)) for name, type in struct._fields_] + def PEFile(baseaddr, target=None): # TODO: 32 with target 32 # 64 with target 64 @@ -40,14 +40,12 @@ def PEFile(baseaddr, target=None): raise NotImplementedError("Parse 32bits PE with 64bits current_process") elif targetedbitness == 64 and proc_bitness == 32: ctypes_structure_transformer = rctypes.transform_type_to_remote64bits + def create_structure_at(structcls, addr): - # try: return rctypes.transform_type_to_remote64bits(structcls)(addr, target) - #except: - #import pdb;pdb.set_trace() - #return rctypes.transform_type_to_remote64bits(structcls)(addr, target) - elif targetedbitness == proc_bitness: # Does not handle remote of same bitness.. + elif targetedbitness == proc_bitness: # Does not handle remote of same bitness.. ctypes_structure_transformer = lambda x: x + def create_structure_at(structcls, addr): return structcls.from_address(addr) else: @@ -123,15 +121,13 @@ def PEFile(baseaddr, target=None): self.hook.disable() self.hook = None return True - - class PEFile(object): def __init__(self): self.baseaddr = baseaddr def get_DOS_HEADER(self): - return create_structure_at(IMAGE_DOS_HEADER, baseaddr) + return create_structure_at(IMAGE_DOS_HEADER, baseaddr) def get_NT_HEADER(self): return self.get_DOS_HEADER().get_NT_HEADER() @@ -148,13 +144,11 @@ def PEFile(baseaddr, target=None): return [] import_descriptor_addr = RVA(import_datadir.VirtualAddress).addr current_import_descriptor = create_structure_at(self.IMAGE_IMPORT_DESCRIPTOR, import_descriptor_addr) - #current_import_descriptor = self.IMAGE_IMPORT_DESCRIPTOR.from_address(import_descriptor_addr) res = [] while current_import_descriptor.FirstThunk: - res.append(current_import_descriptor) - import_descriptor_addr += ctypes.sizeof(self.IMAGE_IMPORT_DESCRIPTOR) - #current_import_descriptor = self.IMAGE_IMPORT_DESCRIPTOR.from_address(import_descriptor_addr) - current_import_descriptor = create_structure_at(self.IMAGE_IMPORT_DESCRIPTOR, import_descriptor_addr) + res.append(current_import_descriptor) + import_descriptor_addr += ctypes.sizeof(self.IMAGE_IMPORT_DESCRIPTOR) + current_import_descriptor = create_structure_at(self.IMAGE_IMPORT_DESCRIPTOR, import_descriptor_addr) return res def get_EXPORT_DIRECTORY(self): @@ -163,22 +157,20 @@ def PEFile(baseaddr, target=None): return None export_directory_addr = baseaddr + export_directory_rva return create_structure_at(self._IMAGE_EXPORT_DIRECTORY, export_directory_addr) - #return self._IMAGE_EXPORT_DIRECTORY.from_address(export_directory_addr) class PESection(ctypes_structure_transformer(IMAGE_SECTION_HEADER)): @utils.fixedpropety def name(self): return ctypes.c_char_p(ctypes.addressof(self.Name)).value - + def __repr__(self): return "".format(self.name) - + @utils.fixedpropety def sections(self): nt_header = self.get_NT_HEADER() nb_section = nt_header.FileHeader.NumberOfSections base_section = ctypes.addressof(nt_header) + ctypes.sizeof(nt_header) - #IMAGE_SECTION_H = ctypes_structure_transformer(IMAGE_SECTION_HEADER) sections_array = create_structure_at(self.PESection * nb_section, base_section) return list(sections_array) @@ -195,7 +187,6 @@ def PEFile(baseaddr, target=None): res[rva_name.str] = rva_addr.addr return res - # TODO: get imports by parsing other modules exports if no INT @utils.fixedpropety def imports(self): @@ -208,19 +199,18 @@ def PEFile(baseaddr, target=None): # str(name.decode()) -> python2 and python3 compatible for str result iat_entry.ord = ord iat_entry.name = str(name.decode()) if name else "" - res.setdefault(import_descriptor.Name.str.lower(),[]).extend(IAT) + res.setdefault(import_descriptor.Name.str.lower(), []).extend(IAT) return res # Will be usable as `self.IMAGE_IMPORT_DESCRIPTOR` class IMAGE_IMPORT_DESCRIPTOR(ctypes.Structure): - _fields_ = transform_ctypes_fields(IMAGE_IMPORT_DESCRIPTOR, {"Name" : StringRVa, "OriginalFirstThunk" : RVA, "FirstThunk" : RVA}) + _fields_ = transform_ctypes_fields(IMAGE_IMPORT_DESCRIPTOR, {"Name": StringRVa, "OriginalFirstThunk": RVA, "FirstThunk": RVA}) def get_INT(self): - if not self.OriginalFirstThunk.value: return None int_addr = self.OriginalFirstThunk.addr - int_entry = create_structure_at(THUNK_DATA, int_addr) + int_entry = create_structure_at(THUNK_DATA, int_addr) res = [] while int_entry.Ordinal: if int_entry.Ordinal & IMAGE_ORDINAL_FLAG: @@ -234,30 +224,30 @@ def PEFile(baseaddr, target=None): name = ctypes.c_char_p(name_address).value res.append((import_by_name.Hint, name)) int_addr += ctypes.sizeof(type(int_entry)) - int_entry = create_structure_at(THUNK_DATA, int_addr) + int_entry = create_structure_at(THUNK_DATA, int_addr) return res def get_IAT(self): iat_addr = self.FirstThunk.addr - iat_entry = create_structure_at(THUNK_DATA, iat_addr) + iat_entry = create_structure_at(THUNK_DATA, iat_addr) res = [] while iat_entry.Ordinal: res.append(IATEntry.create(iat_addr, -1, "??")) iat_addr += ctypes.sizeof(type(iat_entry)) - iat_entry = create_structure_at(THUNK_DATA, iat_addr) + iat_entry = create_structure_at(THUNK_DATA, iat_addr) return res # Will be usable as `self._IMAGE_EXPORT_DIRECTORY` class _IMAGE_EXPORT_DIRECTORY(ctypes.Structure): - _fields_ = transform_ctypes_fields(IMAGE_EXPORT_DIRECTORY, {"Name" : StringRVa, "AddressOfFunctions" : RVA, "AddressOfNames" : RVA, "AddressOfNameOrdinals": RVA}) + _fields_ = transform_ctypes_fields(IMAGE_EXPORT_DIRECTORY, {"Name": StringRVa, "AddressOfFunctions": RVA, "AddressOfNames": RVA, "AddressOfNameOrdinals": RVA}) def get_exports(self): - NameOrdinals = create_structure_at((WORD * self.NumberOfNames), self.AddressOfNameOrdinals.addr) + NameOrdinals = create_structure_at((WORD * self.NumberOfNames), self.AddressOfNameOrdinals.addr) NameOrdinals = list(NameOrdinals) Functions = create_structure_at((RVA * self.NumberOfFunctions), self.AddressOfFunctions.addr) Names = create_structure_at((StringRVa * self.NumberOfNames), self.AddressOfNames.addr) res = [] - for nb,func in enumerate(Functions): + for nb, func in enumerate(Functions): if nb in NameOrdinals: name = Names[NameOrdinals.index(nb)] else: @@ -297,4 +287,4 @@ def PEFile(baseaddr, target=None): return current_pe -tst = PEFile.__code__.co_consts[13] \ No newline at end of file +tst = PEFile.__code__.co_consts[13] diff --git a/remotectypes.py b/remotectypes.py index a6f324c..17c0193 100644 --- a/remotectypes.py +++ b/remotectypes.py @@ -4,41 +4,48 @@ import ctypes.wintypes import itertools from _ctypes import _SimpleCData -# 64bits pointeurs and long -### Utils #### +# ## Utils ### # def is_pointer(x): return isinstance(x, _ctypes._Pointer) + def is_pointer_type(x): return issubclass(x, _ctypes._Pointer) + def is_array(x): return isinstance(x, _ctypes.Array) + def is_array_type(x): return issubclass(x, _ctypes.Array) + def is_structure_type(x): return issubclass(x, ctypes.Structure) + def is_union_type(x): return issubclass(x, ctypes.Union) -#### My types #### +# ### My types ### # -## 64bits pointer types ## +# # 64bits pointer types # # # I know direct inheritance from _SimpleCData seems bad # But it seems to be the only way to have the normal # ctypes.Structure way of working (need to investigate) + class c_void_p64(_SimpleCData): _type_ = "Q" + class c_char_p64(_SimpleCData): _type_ = "Q" + class c_wchar_p64(_SimpleCData): _type_ = "Q" @@ -47,11 +54,9 @@ class c_wchar_p64(_SimpleCData): # maybe force import before ctypes and modif stuff into ctypes ? -## Remote Value - +# # Remote Value # Used by the RemoteStructure to access the target memory - class RemoteValue(object): @classmethod def from_buffer_with_target(cls, buffer, offset=0, target=None): @@ -59,11 +64,13 @@ class RemoteValue(object): x.target = target return x + class RemotePtr(RemoteValue): @property def raw_value(self): return ctypes.cast(self, ctypes.c_void_p).value + class RemoteCCharP(RemotePtr, ctypes.c_char_p): @property def value(self): @@ -77,6 +84,7 @@ class RemoteCCharP(RemotePtr, ctypes.c_char_p): res.append(x) return "".join(res) + class RemoteWCharP(RemotePtr, ctypes.c_char_p): @property def value(self): @@ -91,6 +99,7 @@ class RemoteWCharP(RemotePtr, ctypes.c_char_p): res.extend(x) return "".join(res).decode('utf16') + class RemoteStructurePointer(RemotePtr, ctypes.c_void_p): @classmethod def from_buffer_with_target_and_ptr_type(cls, buffer, offset=0, target=None, ptr_type=None): @@ -101,7 +110,6 @@ class RemoteStructurePointer(RemotePtr, ctypes.c_void_p): @property def contents(self): - #print(self.real_pointer_type) remote_pointed_type = RemoteStructure.from_structure(self.real_pointer_type._type_) return remote_pointed_type(self.raw_value, self.target) @@ -110,9 +118,11 @@ class RemoteStructurePointer(RemotePtr, ctypes.c_void_p): def create_remote_array(subtype, len): + class RemoteArray(_ctypes.Array): _length_ = len _type_ = subtype + def __init__(self, addr, target): self._base_addr = addr self.target = target @@ -123,16 +133,15 @@ def create_remote_array(subtype, len): if slice >= len: raise IndexError("Access to {0} for a RemoteArray of size {1}".format(slice, len)) item_addr = self._base_addr + (ctypes.sizeof(subtype) * slice) - #TODO: do better ? + + # TODO: do better ? class TST(ctypes.Structure): _fields_ = [("TST", subtype)] return RemoteStructure.from_structure(TST)(item_addr, target=self.target).TST - #return subtype(item_addr, target=self.target) return RemoteArray # 64bits pointers - class RemotePtr64(RemoteValue): def __init__(self, value, target): self.target = target @@ -144,12 +153,12 @@ class RemotePtr64(RemoteValue): # Even if we are a subclass of c_ulonglong my_addr = ctypes.addressof(self) return ctypes.c_ulonglong.from_address(my_addr).value - return ctypes.cast(self, ctypes.c_ulonglong).value class Remote_c_void_p64(RemotePtr64, c_void_p64): pass + # base explanation: # RemotePtr64 for the good `raw_value` implem # RemoteCCharP for the good `value` implem @@ -158,10 +167,12 @@ class Remote_c_char_p64(c_char_p64, RemotePtr64, RemoteCCharP): def __repr__(self): return "".format(self.raw_value) + class Remote_w_char_p64(c_wchar_p64, RemotePtr64, RemoteWCharP): def __repr__(self): return "".format(self.raw_value) + class RemoteStructurePointer64(Remote_c_void_p64): @property def raw_value(self): @@ -181,27 +192,27 @@ class RemoteStructurePointer64(Remote_c_void_p64): type_32_64_translation_table = { - ctypes.c_void_p : Remote_c_void_p64, - ctypes.c_char_p : Remote_c_char_p64, + ctypes.c_void_p: Remote_c_void_p64, + ctypes.c_char_p: Remote_c_char_p64, ctypes.c_wchar_p: Remote_w_char_p64, - } +} + class RemoteStructureUnion(object): """Target is a process object""" _reserved_name = ["_target", "_fields_", "_fields_dict_", "_base_addr", "_get_field_by_name", - "_get_field_descrptor_by_name", "_handle_field_getattr", "_field_type_to_remote_type", "__getattribute__", "_fields_"] + "_get_field_descrptor_by_name", "_handle_field_getattr", "_field_type_to_remote_type", + "__getattribute__", "_fields_"] _field_type_to_remote_type = { - ctypes.c_char_p : RemoteCCharP, - ctypes.c_wchar_p : RemoteWCharP, - Remote_c_void_p64 : Remote_c_void_p64, - Remote_c_char_p64 : Remote_c_char_p64, - Remote_w_char_p64 : Remote_w_char_p64} - + ctypes.c_char_p: RemoteCCharP, + ctypes.c_wchar_p: RemoteWCharP, + Remote_c_void_p64: Remote_c_void_p64, + Remote_c_char_p64: Remote_c_char_p64, + Remote_w_char_p64: Remote_w_char_p64 + } def __init__(self, base_addr, target): - if type(base_addr) not in (int, long): - import pdb;pdb.set_trace() self._target = target self._base_addr = base_addr self._fields_dict_ = dict(self._fields_) @@ -213,25 +224,24 @@ class RemoteStructureUnion(object): raise AttributeError(fieldname + "is not a field of {0}".format(type(self))) def _get_field_descrptor_by_name(self, fieldname): - return getattr(type(self), fieldname) # ctypes metaclass fill this for us + return getattr(type(self), fieldname) # ctypes metaclass fill this for us def _handle_field_getattr(self, ftype, fosset, fsize): s = self._target.read_memory(self._base_addr + fosset, fsize) if ftype in self._field_type_to_remote_type: return self._field_type_to_remote_type[ftype].from_buffer_with_target(bytearray(s), target=self._target).value - if issubclass(ftype, _ctypes._Pointer): # Pointer + if issubclass(ftype, _ctypes._Pointer): # Pointer return RemoteStructurePointer.from_buffer_with_target_and_ptr_type(bytearray(s), target=self._target, ptr_type=ftype) - if issubclass(ftype, RemotePtr64): # Pointer to remote64 bits process + if issubclass(ftype, RemotePtr64): # Pointer to remote64 bits process return RemoteStructurePointer64.from_buffer_with_target_and_ptr_type(bytearray(s), target=self._target, ptr_type=ftype) - if issubclass(ftype, RemoteStructureUnion): # Structure|Union already transfomed in remote + if issubclass(ftype, RemoteStructureUnion): # Structure|Union already transfomed in remote return ftype(self._base_addr + fosset, self._target) - if issubclass(ftype, ctypes.Structure): # Structure that must be transfomed + if issubclass(ftype, ctypes.Structure): # Structure that must be transfomed return RemoteStructure.from_structure(ftype)(self._base_addr + fosset, self._target) - if issubclass(ftype, ctypes.Union): # Union that must be transfomed + if issubclass(ftype, ctypes.Union): # Union that must be transfomed return RemoteUnion.from_structure(ftype)(self._base_addr + fosset, self._target) - if issubclass(ftype, _ctypes.Array): # Arrays + if issubclass(ftype, _ctypes.Array): # Arrays return create_remote_array(ftype._type_, ftype._length_)(self._base_addr + fosset, self._target) - #return ftype.from_buffer(bytearray(s)) # Normal types # Follow the ctypes usage: if it's not directly inherited from _SimpleCData # We do not apply the .value @@ -241,18 +251,18 @@ class RemoteStructureUnion(object): return ftype.from_buffer(bytearray(s)).value def __getattribute__(self, fieldname): - if fieldname in type(self)._reserved_name: #Prevent recursion ! + if fieldname in type(self)._reserved_name: # Prevent recursion ! return super(RemoteStructureUnion, self).__getattribute__(fieldname) try: t = self._get_field_by_name(fieldname) - except AttributeError as e: # Not a real attribute + except AttributeError: # Not a real attribute return super(RemoteStructureUnion, self).__getattribute__(fieldname) descr = self._get_field_descrptor_by_name(fieldname) return self._handle_field_getattr(t, descr.offset, descr.size) @classmethod def from_structure(cls, structcls): - class MyStruct(cls, structcls): # inherit of structcls to keep property (see winobject.LoadedModule) + class MyStruct(cls, structcls): # inherit of structcls to keep property (see winobject.LoadedModule) _fields_ = structcls._fields_ MyStruct.__name__ = "Remote" + structcls.__name__ @@ -264,17 +274,20 @@ class RemoteStructureUnion(object): if base_cls: bases.append(base_cls) # inherit of structcls to keep property (see winobject.LoadedModule) - RemoteStruct = type("RemoteStruct", tuple(bases), {"_fields_" : fields}) + RemoteStruct = type("RemoteStruct", tuple(bases), {"_fields_": fields}) if base_cls: RemoteStruct.__name__ = "Remote" + base_cls.__name__ return RemoteStruct + class RemoteStructure(RemoteStructureUnion, ctypes.Structure): pass + class RemoteUnion(RemoteStructureUnion, ctypes.Union): pass + remote_struct = RemoteStructure.from_structure if ctypes.sizeof(ctypes.c_void_p) == 4: @@ -310,4 +323,4 @@ if ctypes.sizeof(ctypes.c_void_p) == 4: if is_union_type(ftype): return transform_union_to_remote64bits(ftype) # Normal types - return type_32_64_translation_table.get(ftype, ftype) \ No newline at end of file + return type_32_64_translation_table.get(ftype, ftype) diff --git a/syswow64.py b/syswow64.py index c79d2eb..6e1a9e6 100644 --- a/syswow64.py +++ b/syswow64.py @@ -2,7 +2,6 @@ import struct import ctypes import codecs import windows -import windows.winproxy as winproxy import windows.native_exec.simple_x64 as x64 from generated_def.winstructs import * @@ -10,16 +9,18 @@ from generated_def.winstructs import * CS_32bits = 0x23 CS_64bits = 0x33 + def genere_return_32bits_stub(ret_addr): ret_32b = x64.MultipleInstr() ret_32b += x64.Mov('RCX', (CS_32bits << 32) + ret_addr) ret_32b += x64.Push('RCX') - ret_32b += x64.Retf32() #32 bits return addr + ret_32b += x64.Retf32() # 32 bits return addr return ret_32b.get_code() # The format of a jump to 64bits mode dummy_jump = "\xea" + struct.pack("32") data = calc.virtual_alloc(0x1000) - remote_python_code =""" + remote_python_code = """ import ctypes import windows # windows.utils.create_console() # remove comment for debug @@ -182,7 +181,7 @@ class WindowsTestCase(unittest.TestCase): k32 = mods[0] get_current_proc_id = k32.pe.exports['GetCurrentProcessId'] data = calc.virtual_alloc(0x1000) - remote_python_code =""" + remote_python_code = """ import ctypes import windows # windows.utils.create_console() # remove comment for debug @@ -200,13 +199,14 @@ class WindowsTestCase(unittest.TestCase): RegOpenKeyExA = [n for n in pythondll_mod.pe.imports['advapi32.dll'] if n.name == "RegOpenKeyExA"][0] hook_value = [] + @windows.hooks.RegOpenKeyExACallback def open_reg_hook(hKey, lpSubKey, ulOptions, samDesired, phkResult, real_function): hook_value.append((hKey, lpSubKey.value)) phkResult[0] = 12345678 return 0 - x = RegOpenKeyExA.set_hook(open_reg_hook) + RegOpenKeyExA.set_hook(open_reg_hook) import _winreg open_args = (0x12345678, "MY_KEY_VALUE") k = _winreg.OpenKey(*open_args) @@ -221,14 +221,15 @@ class WindowsTestCase(unittest.TestCase): def open_reg_hook_fail(hKey, lpSubKey, ulOptions, samDesired, phkResult, real_function): return 0x11223344 - x = RegOpenKeyExA.set_hook(open_reg_hook_fail) + RegOpenKeyExA.set_hook(open_reg_hook_fail) import _winreg open_args = (0x12345678, "MY_KEY_VALUE") with self.assertRaises(WindowsError) as ar: - k = _winreg.OpenKey(*open_args) + _winreg.OpenKey(*open_args) self.assertEqual(ar.exception.winerror, 0x11223344) + if __name__ == '__main__': alltests = unittest.TestSuite() alltests.addTest(unittest.makeSuite(WindowsTestCase)) - unittest.TextTestRunner(verbosity=2).run(alltests) \ No newline at end of file + unittest.TextTestRunner(verbosity=2).run(alltests) diff --git a/utils/__init__.py b/utils/__init__.py index 5cf9c13..5f666fc 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -1,2 +1,2 @@ from pythonutils import * -from winutils import * \ No newline at end of file +from winutils import * diff --git a/utils/pythonutils.py b/utils/pythonutils.py index a9fc8a5..f97ee50 100644 --- a/utils/pythonutils.py +++ b/utils/pythonutils.py @@ -4,6 +4,7 @@ import ctypes def fixedpropety(f): cache_name = "_" + f.__name__ + def prop(self): try: return getattr(self, cache_name) @@ -12,7 +13,8 @@ def fixedpropety(f): return getattr(self, cache_name) return property(prop) + def swallow_ctypes_copy(ctypes_object): new_copy = type(ctypes_object)() ctypes.memmove(ctypes.byref(new_copy), ctypes.byref(ctypes_object), ctypes.sizeof(new_copy)) - return new_copy \ No newline at end of file + return new_copy diff --git a/utils/winutils.py b/utils/winutils.py index d8e810c..e9f31e9 100644 --- a/utils/winutils.py +++ b/utils/winutils.py @@ -1,7 +1,6 @@ import ctypes import msvcrt import os -import copy import sys import code @@ -13,13 +12,15 @@ from ..generated_def.winstructs import * # Function resolution ! def get_func_addr(dll_name, func_name): - dll = ctypes.WinDLL(dll_name) + # Load the DLL + ctypes.WinDLL(dll_name) modules = windows.current_process.peb.modules if not dll_name.lower().endswith(".dll"): dll_name += ".dll" mod = [x for x in modules if x.name == dll_name][0] return mod.pe.exports[func_name] + def get_remote_func_addr(target, dll_name, func_name): name_modules = [m for m in target.peb.modules if m.name == dll_name] if not len(name_modules): @@ -27,27 +28,31 @@ def get_remote_func_addr(target, dll_name, func_name): mod = name_modules[0] return mod.pe.exports[func_name] + def is_wow_64(hProcess): try: - fnIsWow64Process = get_func_addr("kernel32.dll", "IsWow64Process") + fnIsWow64Process = get_func_addr("kernel32.dll", "IsWow64Process") except winproxy.Kernel32Error: return False - IsWow64Process = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.POINTER(BOOL))(fnIsWow64Process) + IsWow64Process = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.POINTER(BOOL))(fnIsWow64Process) Wow64Process = BOOL() res = IsWow64Process(hProcess, ctypes.byref(Wow64Process)) if res: return bool(Wow64Process) raise ctypes.WinError() + def create_file_from_handle(handle, mode="r"): """Return a Python :class:`file` arround a windows HANDLE""" fd = msvcrt.open_osfhandle(handle, os.O_TEXT) return os.fdopen(fd, mode, 0) + def get_handle_from_file(f): """Get the windows HANDLE of a python :class:`file`""" return msvcrt.get_osfhandle(f.fileno()) + def create_console(): """Create a new console displaying STDOUT Useful in injection of GUI process""" @@ -64,6 +69,7 @@ def create_console(): console_stderr = create_file_from_handle(stderr_handle, "w") sys.stderr = console_stderr + def create_process(path, show_windows=False): proc_info = PROCESS_INFORMATION() lpStartupInfo = None @@ -75,7 +81,8 @@ def create_process(path, show_windows=False): windows.winproxy.CreateProcessA(path, lpProcessInformation=ctypes.byref(proc_info), lpStartupInfo=lpStartupInfo) proc = [p for p in windows.system.processes if p.pid == proc_info.dwProcessId][0] return proc - + + def enable_privilege(lpszPrivilege, bEnablePrivilege): """Enable of disable a privilege: enable_privilege(SE_DEBUG_NAME, True)""" tp = TOKEN_PRIVILEGES() @@ -94,21 +101,21 @@ def enable_privilege(lpszPrivilege, bEnablePrivilege): winproxy.CloseHandle(hToken) if winproxy.GetLastError() == windef.ERROR_NOT_ALL_ASSIGNED: raise ValueError("Failed to get privilege {0}".format(lpszPrivilege)) - return True - + return True + + def check_is_elevated(): """Return True if process is Admin""" - tp = TOKEN_PRIVILEGES() hToken = HANDLE() elevation = TOKEN_ELEVATION() cbsize = DWORD() - bcsize = sizeof(elevation) winproxy.OpenProcessToken(winproxy.GetCurrentProcess(), TOKEN_ALL_ACCESS, byref(hToken)) winproxy.GetTokenInformation(hToken, TokenElevation, byref(elevation), sizeof(elevation), byref(cbsize)) winproxy.CloseHandle(hToken) return elevation.TokenIsElevated - + + def check_debug(): """Check that kernel is in debug mode beware if NOUMEX (https://msdn.microsoft.com/en-us/library/windows/hardware/ff556253(v=vs.85).aspx#_______noumex______)""" @@ -119,31 +126,34 @@ def check_debug(): winproxy.RegOpenKeyExA(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Control", 0, KEY_READ, byref(hkresult)) winproxy.RegGetValueA(hkresult, None, "SystemStartOptions", RRF_RT_REG_SZ, None, byref(bufferres), byref(cbsize)) winproxy.RegCloseKey(hkresult) - + control = bufferres[:] if "DEBUG" not in control: - #print "[-] Enable debug boot!" - #print "> bcdedit /debug on" + # print "[-] Enable debug boot!" + # print "> bcdedit /debug on" return False if "DEBUG=NOUMEX" not in control: pass - #print "[*] Warning noumex not set!" - #print "> bcdedit /set noumex on" + # print "[*] Warning noumex not set!" + # print "> bcdedit /set noumex on" return True + class FixedInteractiveConsole(code.InteractiveConsole): def raw_input(self, prompt=">>>"): sys.stdout.write(prompt) return raw_input("") + def pop_shell(): """Pop a console with an InterativeConsole""" create_console() FixedInteractiveConsole(locals()).interact() + def get_kernel_modules(): cbsize = DWORD() - + winproxy.NtQuerySystemInformation(SystemModuleInformation, None, 0, byref(cbsize)) raw_buffer = (cbsize.value * c_char)() buffer = SYSTEM_MODULE_INFORMATION.from_address(ctypes.addressof(raw_buffer)) @@ -151,6 +161,7 @@ def get_kernel_modules(): modules = (SYSTEM_MODULE * buffer.ModulesCount).from_address(addressof(buffer) + SYSTEM_MODULE_INFORMATION.Modules.offset) return list(modules) + class VirtualProtected(object): """A context manager usable like `VirtualProtect` that will restore the old protection at exit @@ -175,6 +186,7 @@ class VirtualProtected(object): winproxy.VirtualProtect(self.addr, self.size, self.old_protect.value, ctypes.byref(self.old_protect)) return False + class DisableWow64FsRedirection(object): """A context manager that disable the Wow64 Fs Redirection""" def __enter__(self): diff --git a/vectored_exception.py b/vectored_exception.py index 371f380..f3566e1 100644 --- a/vectored_exception.py +++ b/vectored_exception.py @@ -1,43 +1,40 @@ -import windows - import ctypes import windows from windows.generated_def.winstructs import * import windows.generated_def.windef as windef -import time -EXCEPTION_CONTINUE_SEARCH = (0x0) -EXCEPTION_CONTINUE_EXECUTION = (0xffffffff) +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", + "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]]) +exception_name_by_value = dict([(x, x) for x in [getattr(windows.generated_def.windef, name) for name in exception_type]]) class EnhancedEXCEPTION_RECORD(EXCEPTION_RECORD): @@ -52,39 +49,40 @@ class EnhancedEXCEPTION_RECORD(EXCEPTION_RECORD): if x is None: return 0x0 return x - + + 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_)]) - + ("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_: @@ -93,15 +91,16 @@ class Eflags(int): 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 EnhancedCONTEXTBase(CONTEXT): default_dump = () pc_reg = '' @@ -112,7 +111,7 @@ class EnhancedCONTEXTBase(CONTEXT): if to_dump is None: to_dump = self.default_dump for name in to_dump: - value = getattr(self, name) + value = getattr(self, name) if name in self.special_reg_type: value = self.special_reg_type[name](value) res.append((name, value)) @@ -123,31 +122,34 @@ class EnhancedCONTEXTBase(CONTEXT): 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)") + class EnhancedCONTEXT32(EnhancedCONTEXTBase): default_dump = ('Eip', 'Esp', 'Eax', 'Ebx', 'Ecx', 'Edx', 'Ebp', 'Edi', 'Esi', 'EFlags') pc_reg = 'Eip' - special_reg_type = {'EFlags' : Eflags} + special_reg_type = {'EFlags': Eflags} + class EnhancedCONTEXT64(EnhancedCONTEXTBase): 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} + special_reg_type = {'EFlags': Eflags} if windows.current_process.bitness == 32: EnhancedCONTEXT = EnhancedCONTEXT32 else: EnhancedCONTEXT = EnhancedCONTEXT64 + class EnhancedEXCEPTION_POINTERS(ctypes.Structure): _fields_ = [ ("ExceptionRecord", ctypes.POINTER(EnhancedEXCEPTION_RECORD)), @@ -177,7 +179,7 @@ class VectoredException(object): except BaseException as e: print("Ignored Python Exception in Vectored Exception: {0}".format(e)) return windef.EXCEPTION_CONTINUE_SEARCH - + class WithExceptionHandler(object): def __init__(self, handler): @@ -189,4 +191,4 @@ class WithExceptionHandler(object): def __exit__(self, exc_type, exc_value, traceback): windows.winproxy.RemoveVectoredExceptionHandler(self.value) - return False \ No newline at end of file + return False diff --git a/winobject.py b/winobject.py index e11b870..8a5c64c 100644 --- a/winobject.py +++ b/winobject.py @@ -1,6 +1,5 @@ import ctypes import os -import codecs import copy import time import struct @@ -42,9 +41,10 @@ class AutoHandle(object): return self._handle def __del__(self): - if hasattr(self, "_handle") and self._handle: + if hasattr(self, "_handle") and self._handle: winproxy.CloseHandle(self._handle) + class System(object): """Represent the current windows system python is running on""" @property @@ -127,7 +127,7 @@ class WinThread(THREADENTRY32, AutoHandle): @property def context(self): - x = windows.vectored_exception.EnhancedCONTEXT() + x = windows.vectored_exception.EnhancedCONTEXT() x.ContextFlags = CONTEXT_FULL winproxy.GetThreadContext(self.handle, x) return x @@ -164,6 +164,7 @@ class WinThread(THREADENTRY32, AutoHandle): except IndexError: return (tid, handle) + class Process(AutoHandle): @utils.fixedpropety def is_wow_64(self): @@ -231,20 +232,19 @@ class CurrentThread(AutoHandle): return winproxy.ExitThread(code) - class CurrentProcess(Process): """The current process""" get_peb = None - get_peb_32_code = x86.MultipleInstr() + get_peb_32_code = x86.MultipleInstr() get_peb_32_code += x86.Mov('EAX', x86.mem('fs:[0x30]')) get_peb_32_code += x86.Ret() - get_peb_32_code = get_peb_32_code.get_code() + get_peb_32_code = get_peb_32_code.get_code() - get_peb_64_code = x64.MultipleInstr() + get_peb_64_code = x64.MultipleInstr() get_peb_64_code += x64.Mov('RAX', x64.mem('gs:[0x60]')) get_peb_64_code += x64.Ret() - get_peb_64_code = get_peb_64_code.get_code() + get_peb_64_code = get_peb_64_code.get_code() allocator = native_exec.native_function.allocator @@ -330,6 +330,7 @@ class CurrentProcess(Process): """Exit the process""" return winproxy.ExitProcess(code) + class WinProcess(PROCESSENTRY32, Process): """A Process on the system""" is_pythondll_injected = 0 @@ -378,7 +379,7 @@ class WinProcess(PROCESSENTRY32, Process): def low_read_memory(self, addr, buffer_addr, size): if windows.current_process.bitness == 32 and self.bitness == 64: - #OptionalExport can be None (see winproxy.py) + # OptionalExport can be None (see winproxy.py) if winproxy.NtWow64ReadVirtualMemory64 is None: raise ValueError("NtWow64ReadVirtualMemory64 non available in ntdll: cannot write into 64bits processus") return winproxy.NtWow64ReadVirtualMemory64(self.handle, addr, buffer_addr, size) @@ -386,27 +387,27 @@ class WinProcess(PROCESSENTRY32, Process): def read_memory(self, addr, size): """Read `size` from `addr`""" - buffer = ctypes.create_string_buffer(size) + buffer = ctypes.create_string_buffer(size) self.low_read_memory(addr, ctypes.byref(buffer), size) return buffer[:] - ##Simple cache test - #real_read = read_memory + # Simple cache test + # real_read = read_memory # - #def read_memory(self, addr, size): - # """Cached version for test""" - # dbgprint('Read remote Memory of {0}'.format(self), 'READMEM') - # if not hasattr(self, "_cache_cache"): - # self._cache_cache = {} - # page_addr = addr & 0xfffffffffffff000 - # if page_addr in self._cache_cache: - # #print("CACHED Read on page {0}".format(hex(page_addr))) - # page_data = self._cache_cache[page_addr] - # return page_data[addr & 0xfff: (addr & 0xfff) + size] - # else: - # page_data = self.real_read(page_addr, 0x1000) - # self._cache_cache[page_addr] = page_data - # return page_data[addr & 0xfff: (addr & 0xfff) + size] + # def read_memory(self, addr, size): + # """Cached version for test""" + # dbgprint('Read remote Memory of {0}'.format(self), 'READMEM') + # if not hasattr(self, "_cache_cache"): + # self._cache_cache = {} + # page_addr = addr & 0xfffffffffffff000 + # if page_addr in self._cache_cache: + # #print("CACHED Read on page {0}".format(hex(page_addr))) + # page_data = self._cache_cache[page_addr] + # return page_data[addr & 0xfff: (addr & 0xfff) + size] + # else: + # page_data = self.real_read(page_addr, 0x1000) + # self._cache_cache[page_addr] = page_data + # return page_data[addr & 0xfff: (addr & 0xfff) + size] def read_memory_into(self, addr, struct): """Read a :mod:`ctypes` struct from `addr`""" @@ -417,7 +418,7 @@ class WinProcess(PROCESSENTRY32, Process): """Create a remote thread""" if windows.current_process.bitness == 32 and self.bitness == 64: return windows.syswow64.NtCreateThreadEx_32_to_64(self, addr, param) - return WinThread._from_handle(winproxy.CreateRemoteThread(hProcess=self.handle, lpStartAddress=addr, lpParameter=param)) + return WinThread._from_handle(winproxy.CreateRemoteThread(hProcess=self.handle, lpStartAddress=addr, lpParameter=param)) def load_library(self, dll_path): """Load the library in remote process""" @@ -433,7 +434,6 @@ class WinProcess(PROCESSENTRY32, Process): def get_peb_addr(self): dest = self.virtual_alloc(0x1000) if self.bitness == 32: - #get_peb_code = get_peb_32_code store_peb = x86.MultipleInstr() store_peb += x86.Mov('EAX', x86.mem('fs:[0x30]')) store_peb += x86.Mov(x86.create_displacement(disp=dest), 'EAX') @@ -506,6 +506,7 @@ class LoadedModule(LDR_DATA_TABLE_ENTRY): """ return pe_parse.PEFile(self.baseaddr) + class WinUnicodeString(LSA_UNICODE_STRING): """LSA_UNICODE_STRING with a nice `__repr__`""" def __repr__(self): @@ -514,20 +515,25 @@ class WinUnicodeString(LSA_UNICODE_STRING): class LIST_ENTRY_PTR(PVOID): def TO_LDR_ENTRY(self): - return LDR_DATA_TABLE_ENTRY.from_address(self.value - sizeof(PVOID) * 2) + return LDR_DATA_TABLE_ENTRY.from_address(self.value - sizeof(PVOID) * 2) + def transform_ctypes_fields(struct, replacement): return [(name, replacement.get(name, type)) for name, type in struct._fields_] + class RTL_USER_PROCESS_PARAMETERS(Structure): - _fields_ = transform_ctypes_fields(RTL_USER_PROCESS_PARAMETERS, # The one in generated_def - {"ImagePathName" : WinUnicodeString, - "CommandLine" : WinUnicodeString}) + _fields_ = transform_ctypes_fields(RTL_USER_PROCESS_PARAMETERS, # The one in generated_def + {"ImagePathName": WinUnicodeString, + "CommandLine": WinUnicodeString} + ) + class PEB(Structure): """The PEB (Process Environment Block) of the current process""" - _fields_ = transform_ctypes_fields(PEB, # The one in generated_def - {"ProcessParameters" : POINTER(RTL_USER_PROCESS_PARAMETERS)}) + _fields_ = transform_ctypes_fields(PEB, # The one in generated_def + {"ProcessParameters": POINTER(RTL_USER_PROCESS_PARAMETERS)} + ) @property def imagepath(self): @@ -564,14 +570,11 @@ class PEB(Structure): import windows.remotectypes as rctypes - - - class RemotePEB(rctypes.RemoteStructure.from_structure(PEB)): RemoteLoadedModule = rctypes.RemoteStructure.from_structure(LoadedModule) def ptr_flink_to_remote_module(self, ptr_value): - return self.RemoteLoadedModule(ptr_value - ctypes.sizeof(ctypes.c_void_p) * 2, self._target) + return self.RemoteLoadedModule(ptr_value - ctypes.sizeof(ctypes.c_void_p) * 2, self._target) @property def modules(self): @@ -589,6 +592,7 @@ class RemotePEB(rctypes.RemoteStructure.from_structure(PEB)): current_dll = self.ptr_flink_to_remote_module(list_entry_ptr) return res + if CurrentProcess().bitness == 32: class RemoteLoadedModule64(rctypes.transform_type_to_remote64bits(LoadedModule)): @property @@ -600,10 +604,9 @@ if CurrentProcess().bitness == 32: return pe_parse.PEFile(self.baseaddr, target=self._target) class RemotePEB64(rctypes.transform_type_to_remote64bits(PEB)): - #RemoteLoadedModule64 = rctypes.transform_type_to_remote64bits(LoadedModule) def ptr_flink_to_remote_module(self, ptr_value): - return RemoteLoadedModule64(ptr_value - ctypes.sizeof(rctypes.c_void_p64) * 2, self._target) + return RemoteLoadedModule64(ptr_value - ctypes.sizeof(rctypes.c_void_p64) * 2, self._target) @property def modules(self): @@ -619,4 +622,4 @@ if CurrentProcess().bitness == 32: res.append(current_dll) list_entry_ptr = current_dll.InMemoryOrderLinks.Flink.raw_value current_dll = self.ptr_flink_to_remote_module(list_entry_ptr) - return res \ No newline at end of file + return res diff --git a/winproxy.py b/winproxy.py index 199a7a4..77a4af6 100644 --- a/winproxy.py +++ b/winproxy.py @@ -14,13 +14,14 @@ advapi32 = ctypes.windll.Advapi32 iphlpapi = ctypes.windll.iphlpapi ntdll = ctypes.windll.ntdll + class Kernel32Error(WindowsError): def __new__(cls, func_name): win_error = ctypes.WinError() api_error = super(Kernel32Error, cls).__new__(cls) api_error.api_name = func_name api_error.winerror = win_error.winerror - api_error.strerror = win_error.strerror + api_error.strerror = win_error.strerror api_error.args = (func_name, win_error.winerror, win_error.strerror) return api_error @@ -30,6 +31,7 @@ class Kernel32Error(WindowsError): def __str__(self): return "{0}: {1}".format(self.api_name, super(Kernel32Error, self).__str__()) + class IphlpapiError(Kernel32Error): def __new__(cls, func_name, code): @@ -37,18 +39,20 @@ class IphlpapiError(Kernel32Error): api_error = super(Kernel32Error, cls).__new__(cls) api_error.api_name = func_name api_error.winerror = win_error.winerror - api_error.strerror = win_error.strerror + api_error.strerror = win_error.strerror api_error.args = (func_name, win_error.winerror, win_error.strerror) return api_error def __init__(self, func_name, code): pass + # Error check method def no_error_check(func_name, result, func, args): """Nothing special""" return args + def minus_one_error_check(func_name, result, func, args): if result == -1: raise Kernel32Error(func_name) @@ -61,30 +65,34 @@ def kernel32_error_check(func_name, result, func, args): raise Kernel32Error(func_name) return args + def kernel32_zero_check(func_name, result, func, args): """raise Kernel32Error if result is NOT 0""" if result: raise Kernel32Error(func_name) return args + def iphlpapi_error_check(func_name, result, func, args): """raise IphlpapiError if result is NOT 0""" if result: raise IphlpapiError(func_name, result) return args + def error_ntstatus(func_name, result, func, args): if result: raise NtStatusException(result & 0xffffffff) return args + class ExportNotFound(AttributeError): def __init__(self, func_name, api_name): self.func_name = func_name self.api_name = api_name super(ExportNotFound, self).__init__("Function {0} not found into {1}".format(func_name, api_name)) -# Design 1 + class ApiProxy(object): APIDLL = None """Create a python wrapper around a kernel32 function""" @@ -107,6 +115,7 @@ class ApiProxy(object): doc = doc if doc else "" python_proxy.__doc__ = doc + "\nErrcheck:\n " + self.error_check.__doc__ params_name = [param[1] for param in params] + def perform_call(*args): if len(params_name) != len(args): print("ERROR:") @@ -120,22 +129,27 @@ class ApiProxy(object): setattr(python_proxy, "ctypes_function", perform_call) return python_proxy + class Kernel32Proxy(ApiProxy): APIDLL = kernel32 default_error_check = staticmethod(kernel32_error_check) + class Advapi32Proxy(ApiProxy): APIDLL = advapi32 default_error_check = staticmethod(kernel32_error_check) + class IphlpapiProxy(ApiProxy): APIDLL = iphlpapi default_error_check = staticmethod(iphlpapi_error_check) + class NtdllProxy(ApiProxy): APIDLL = ntdll default_error_check = staticmethod(kernel32_zero_check) + class OptionalExport(object): """used 'around' a Proxy decorator Should be used for export that are not available everywhere (ntdll internals | 32/64 bits stuff) @@ -156,6 +170,7 @@ class OptionalExport(object): dbgprint("Export <{e.func_name}> not found in <{e.api_name}>".format(e=e), "EXPORTNOTFOUND") return None + def TransparentApiProxy(APIDLL, func_name, error_check): """Create a ctypes function for 'func_name' with no python arg pre-check""" @@ -169,12 +184,14 @@ def TransparentApiProxy(APIDLL, func_name, error_check): return c_prototyped -TransparentKernel32Proxy = lambda func_name, error_check=kernel32_error_check : TransparentApiProxy(kernel32, func_name, error_check) -TransparentAdvapi32Proxy = lambda func_name, error_check=kernel32_error_check : TransparentApiProxy(advapi32, func_name, error_check) -TransparentIphlpapiProxy = lambda func_name, error_check=iphlpapi_error_check : TransparentApiProxy(iphlpapi, func_name, error_check) +TransparentKernel32Proxy = lambda func_name, error_check=kernel32_error_check: TransparentApiProxy(kernel32, func_name, error_check) +TransparentAdvapi32Proxy = lambda func_name, error_check=kernel32_error_check: TransparentApiProxy(advapi32, func_name, error_check) +TransparentIphlpapiProxy = lambda func_name, error_check=iphlpapi_error_check: TransparentApiProxy(iphlpapi, func_name, error_check) + class NeededParameterType(object): _inst = None + def __new__(cls): if cls._inst is None: cls._inst = super(NeededParameterType, cls).__new__(cls) @@ -182,8 +199,6 @@ class NeededParameterType(object): def __repr__(self): return "NeededParameter" - - NeededParameter = NeededParameterType() ExitProcess = TransparentKernel32Proxy("ExitProcess") @@ -201,7 +216,6 @@ FreeConsole = TransparentKernel32Proxy("FreeConsole") GetStdHandle = TransparentKernel32Proxy("GetStdHandle") SetStdHandle = TransparentKernel32Proxy("SetStdHandle") GetCurrentThreadId = TransparentKernel32Proxy("GetCurrentThreadId") - TerminateThread = TransparentKernel32Proxy("TerminateThread") ExitThread = TransparentKernel32Proxy("ExitThread") SuspendThread = TransparentKernel32Proxy("SuspendThread", minus_one_error_check) @@ -209,45 +223,54 @@ ResumeThread = TransparentKernel32Proxy("ResumeThread", minus_one_error_check) GetThreadId = TransparentKernel32Proxy("GetThreadId") Wow64DisableWow64FsRedirection = OptionalExport(TransparentKernel32Proxy)("Wow64DisableWow64FsRedirection") -Wow64RevertWow64FsRedirection = OptionalExport(TransparentKernel32Proxy)("Wow64RevertWow64FsRedirection") -Wow64EnableWow64FsRedirection = OptionalExport(TransparentKernel32Proxy)("Wow64EnableWow64FsRedirection") +Wow64RevertWow64FsRedirection = OptionalExport(TransparentKernel32Proxy)("Wow64RevertWow64FsRedirection") +Wow64EnableWow64FsRedirection = OptionalExport(TransparentKernel32Proxy)("Wow64EnableWow64FsRedirection") + @Kernel32Proxy("CreateFileA") def CreateFileA(lpFileName, dwDesiredAccess, dwShareMode=0, lpSecurityAttributes=None, dwCreationDisposition=OPEN_EXISTING, dwFlagsAndAttributes=FILE_ATTRIBUTE_NORMAL, hTemplateFile=None): return CreateFileA.ctypes_function(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile) + @Kernel32Proxy("VirtualAlloc") -def VirtualAlloc(lpAddress=0, dwSize=NeededParameter, flAllocationType=MEM_COMMIT, flProtect=PAGE_EXECUTE_READWRITE): +def VirtualAlloc(lpAddress=0, dwSize=NeededParameter, flAllocationType=MEM_COMMIT, flProtect=PAGE_EXECUTE_READWRITE): return VirtualAlloc.ctypes_function(lpAddress, dwSize, flAllocationType, flProtect) + @Kernel32Proxy("VirtualFree") def VirtualFree(lpAddress, dwSize=0, dwFreeType=MEM_RELEASE): return VirtualFree.ctypes_function(lpAddress, dwSize, dwFreeType) + @Kernel32Proxy("VirtualAllocEx") -def VirtualAllocEx(hProcess, lpAddress=0, dwSize=NeededParameter, flAllocationType=MEM_COMMIT, flProtect=PAGE_EXECUTE_READWRITE): +def VirtualAllocEx(hProcess, lpAddress=0, dwSize=NeededParameter, flAllocationType=MEM_COMMIT, flProtect=PAGE_EXECUTE_READWRITE): return VirtualAllocEx.ctypes_function(hProcess, lpAddress, dwSize, flAllocationType, flProtect) + @Kernel32Proxy("VirtualFreeEx") def VirtualFreeEx(hProcess, lpAddress, dwSize=0, dwFreeType=MEM_RELEASE): return VirtualFreeEx.ctypes_function(hProcess, lpAddress, dwSize, dwFreeType) + @Kernel32Proxy("CreateThread") def CreateThread(lpThreadAttributes=None, dwStackSize=0, lpStartAddress=NeededParameter, lpParameter=NeededParameter, dwCreationFlags=0, lpThreadId=None): return CreateThread.ctypes_function(lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId) + @Kernel32Proxy("CreateRemoteThread") def CreateRemoteThread(hProcess=NeededParameter, lpThreadAttributes=None, dwStackSize=0, - lpStartAddress=NeededParameter, lpParameter=NeededParameter, dwCreationFlags=0, lpThreadId=None): + lpStartAddress=NeededParameter, lpParameter=NeededParameter, dwCreationFlags=0, lpThreadId=None): return CreateRemoteThread.ctypes_function(hProcess, lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId) + @Kernel32Proxy("VirtualProtect") def VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect=0): return VirtualProtect.ctypes_function(lpAddress, dwSize, flNewProtect, lpflOldProtect) + @Kernel32Proxy("CreateProcessA") def CreateProcessA(lpApplicationName, lpCommandLine=None, lpProcessAttributes=None, lpThreadAttributes=None, bInheritHandles=False, - dwCreationFlags=0, lpEnvironment=None, lpCurrentDirectory=None, lpStartupInfo=None, lpProcessInformation=None): + dwCreationFlags=0, lpEnvironment=None, lpCurrentDirectory=None, lpStartupInfo=None, lpProcessInformation=None): if lpStartupInfo is None: StartupInfo = STARTUPINFOA() StartupInfo.cb = ctypes.sizeof(StartupInfo) @@ -258,9 +281,10 @@ def CreateProcessA(lpApplicationName, lpCommandLine=None, lpProcessAttributes=No lpProcessInformation = ctypes.byref(PROCESS_INFORMATION()) return CreateProcessA.ctypes_function(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation) + @Kernel32Proxy("CreateProcessW") def CreateProcessW(lpApplicationName, lpCommandLine=None, lpProcessAttributes=None, lpThreadAttributes=None, bInheritHandles=False, - dwCreationFlags=0, lpEnvironment=None, lpCurrentDirectory=None, lpStartupInfo=None, lpProcessInformation=None): + dwCreationFlags=0, lpEnvironment=None, lpCurrentDirectory=None, lpStartupInfo=None, lpProcessInformation=None): if lpStartupInfo is None: StartupInfo = STARTUPINFOW() StartupInfo.cb = ctypes.sizeof(StartupInfo) @@ -271,6 +295,7 @@ def CreateProcessW(lpApplicationName, lpCommandLine=None, lpProcessAttributes=No lpProcessInformation = ctypes.byref(PROCESS_INFORMATION()) return CreateProcessW.ctypes_function(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation) + @Kernel32Proxy("GetThreadContext") def GetThreadContext(hThread, lpContext=None): if lpContext is None: @@ -279,6 +304,7 @@ def GetThreadContext(hThread, lpContext=None): lpContext = ctypes.byref(Context) return GetThreadContext.ctypes_function(hThread, lpContext) + @Kernel32Proxy("SetThreadContext") def SetThreadContext(hThread, lpContext): """ Allows to directly pass a CONTEXT and will call with byref(CONTEXT) by itself""" @@ -286,18 +312,22 @@ def SetThreadContext(hThread, lpContext): lpContext = ctypes.byref(lpContext) return SetThreadContext.ctypes_function(hThread, lpContext) + @Kernel32Proxy("OpenThread") def OpenThread(dwDesiredAccess=THREAD_ALL_ACCESS, bInheritHandle=0, dwThreadId=NeededParameter): return OpenThread.ctypes_function(dwDesiredAccess, bInheritHandle, dwThreadId) + @Kernel32Proxy("OpenProcess") def OpenProcess(dwDesiredAccess=PROCESS_ALL_ACCESS, bInheritHandle=0, dwProcessId=NeededParameter): return OpenProcess.ctypes_function(dwDesiredAccess, bInheritHandle, dwProcessId) + @Kernel32Proxy("ReadProcessMemory") def ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead=None): return ReadProcessMemory.ctypes_function(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead) + @Kernel32Proxy("WriteProcessMemory") def WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize=None, lpNumberOfBytesWritten=None): """Computer nSize with len(lpBuffer) if not given""" @@ -305,6 +335,7 @@ def WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize=None, lpNumberOf nSize = len(lpBuffer) return WriteProcessMemory.ctypes_function(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesWritten) + @Kernel32Proxy('SetThreadAffinityMask') def SetThreadAffinityMask(hThread=None, dwThreadAffinityMask=NeededParameter): """If hThread is not given, it will be the current thread""" @@ -312,37 +343,43 @@ def SetThreadAffinityMask(hThread=None, dwThreadAffinityMask=NeededParameter): hThread = GetCurrentThread() return SetThreadAffinityMask.ctypes_function(hThread, dwThreadAffinityMask) + @Kernel32Proxy("CreateToolhelp32Snapshot") def CreateToolhelp32Snapshot(dwFlags, th32ProcessID=0): return CreateToolhelp32Snapshot.ctypes_function(dwFlags, th32ProcessID) + @Kernel32Proxy("Thread32First", no_error_check) def Thread32First(hSnapshot, lpte): """Set byref(lpte) if needed""" if type(lpte) == THREADENTRY32: lpte = ctypes.byref(lpte) - return Thread32First.ctypes_function(hSnapshot, lpte) + return Thread32First.ctypes_function(hSnapshot, lpte) + @Kernel32Proxy("Thread32Next", no_error_check) def Thread32Next(hSnapshot, lpte): """Set byref(lpte) if needed""" if type(lpte) == THREADENTRY32: lpte = ctypes.byref(lpte) - return Thread32Next.ctypes_function(hSnapshot, lpte) + return Thread32Next.ctypes_function(hSnapshot, lpte) + @Kernel32Proxy("Process32First", no_error_check) def Process32First(hSnapshot, lpte): """Set byref(lpte) if needed""" if type(lpte) == THREADENTRY32: lpte = ctypes.byref(lpte) - return Process32First.ctypes_function(hSnapshot, lpte) + return Process32First.ctypes_function(hSnapshot, lpte) + @Kernel32Proxy("Process32Next", no_error_check) def Process32Next(hSnapshot, lpte): """Set byref(lpte) if needed""" if type(lpte) == THREADENTRY32: lpte = ctypes.byref(lpte) - return Process32Next.ctypes_function(hSnapshot, lpte) + return Process32Next.ctypes_function(hSnapshot, lpte) + # File stuff @Kernel32Proxy("WriteFile") @@ -353,23 +390,28 @@ def WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite=None, lpNumberOfBytesWritte lpNumberOfBytesWritten = ctypes.byref(DWORD()) return WriteFile.ctypes_function(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped) + # Exception stuff @Kernel32Proxy("AddVectoredContinueHandler") def AddVectoredContinueHandler(FirstHandler=1, VectoredHandler=NeededParameter): return AddVectoredContinueHandler.ctypes_function(FirstHandler, VectoredHandler) + @Kernel32Proxy("AddVectoredExceptionHandler") def AddVectoredExceptionHandler(FirstHandler=1, VectoredHandler=NeededParameter): return AddVectoredExceptionHandler.ctypes_function(FirstHandler, VectoredHandler) + @Kernel32Proxy("RemoveVectoredExceptionHandler") def RemoveVectoredExceptionHandler(Handler): return RemoveVectoredExceptionHandler.ctypes_function(Handler) + @Kernel32Proxy("WaitForSingleObject", kernel32_zero_check) def WaitForSingleObject(hHandle, dwMilliseconds=INFINITE): return WaitForSingleObject.ctypes_function(hHandle, dwMilliseconds) + @Kernel32Proxy("DeviceIoControl") def DeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize=None, lpOutBuffer=NeededParameter, nOutBufferSize=None, lpBytesReturned=None, lpOverlapped=None): if nInBufferSize is None: @@ -380,14 +422,15 @@ def DeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize=None, lp # Some windows check 0 / others does not lpBytesReturned = ctypes.byref(DWORD()) return DeviceIoControl.ctypes_function(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, lpBytesReturned, lpOverlapped) - -#### NTDLL ##### + +# ### NTDLL #### # @OptionalExport(NtdllProxy('NtWow64ReadVirtualMemory64', error_ntstatus)) def NtWow64ReadVirtualMemory64(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead=None): return NtWow64ReadVirtualMemory64.ctypes_function(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead) + def ntquerysysteminformation_error_check(func_name, result, func, args): if result == 0: return args @@ -396,13 +439,15 @@ def ntquerysysteminformation_error_check(func_name, result, func, args): return args raise Kernel32Error("{0} failed with NTStatus {1}".format(func_name, hex(result))) + @NtdllProxy('NtQuerySystemInformation', ntquerysysteminformation_error_check) def NtQuerySystemInformation(SystemInformationClass, SystemInformation=None, SystemInformationLength=0, ReturnLength=NeededParameter): if SystemInformation is not None and SystemInformation == 0: SystemInformationLength = ctypes.sizeof(SystemInformation) return NtQuerySystemInformation.ctypes_function(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength) -###### ADVAPI32 ######## + +# ##### ADVAPI32 ####### # @Advapi32Proxy('OpenProcessToken') def OpenProcessToken(ProcessHandle=None, DesiredAccess=NeededParameter, TokenHandle=NeededParameter): @@ -411,20 +456,24 @@ def OpenProcessToken(ProcessHandle=None, DesiredAccess=NeededParameter, TokenHan ProcessHandle = GetCurrentProcess() return OpenProcessToken.ctypes_function(ProcessHandle, DesiredAccess, TokenHandle) + @Advapi32Proxy('LookupPrivilegeValueA') def LookupPrivilegeValueA(lpSystemName=None, lpName=NeededParameter, lpLuid=NeededParameter): return LookupPrivilegeValueA.ctypes_function(lpSystemName, lpName, lpLuid) + @Advapi32Proxy('LookupPrivilegeValueW') def LookupPrivilegeValueW(lpSystemName=None, lpName=NeededParameter, lpLuid=NeededParameter): return LookupPrivilegeValueW.ctypes_function(lpSystemName, lpName, lpLuid) + @Advapi32Proxy('AdjustTokenPrivileges') def AdjustTokenPrivileges(TokenHandle, DisableAllPrivileges=False, NewState=NeededParameter, BufferLength=None, PreviousState=None, ReturnLength=None): if BufferLength is None: BufferLength = ctypes.sizeof(NewState) return AdjustTokenPrivileges.ctypes_function(TokenHandle, DisableAllPrivileges, NewState, BufferLength, PreviousState, ReturnLength) + # Registry stuff @Advapi32Proxy('GetTokenInformation') @@ -433,33 +482,38 @@ def GetTokenInformation(TokenHandle=NeededParameter, TokenInformationClass=Neede ReturnLength = ctypes.byref(DWORD()) return GetTokenInformation.ctypes_function(TokenHandle, TokenInformationClass, TokenInformation, TokenInformationLength, ReturnLength) + @Advapi32Proxy('RegOpenKeyExA', kernel32_zero_check) def RegOpenKeyExA(hKey, lpSubKey, ulOptions, samDesired, phkResult): return RegOpenKeyExA.ctypes_function(hKey, lpSubKey, ulOptions, samDesired, phkResult) + # TODO: default values? which ones ? @Advapi32Proxy('RegOpenKeyExW', kernel32_zero_check) def RegOpenKeyExW(hKey, lpSubKey, ulOptions, samDesired, phkResult): return RegOpenKeyExW.ctypes_function(hKey, lpSubKey, ulOptions, samDesired, phkResult) + @Advapi32Proxy('RegGetValueA', kernel32_zero_check) def RegGetValueA(hkey, lpSubKey, lpValue, dwFlags, pdwType, pvData, pcbData): return RegGetValueA.ctypes_function(hkey, lpSubKey, lpValue, dwFlags, pdwType, pvData, pcbData) + @Advapi32Proxy('RegGetValueW', kernel32_zero_check) def RegGetValueW(hkey, lpSubKey, lpValue, dwFlags, pdwType, pvData, pcbData): return RegGetValueW.ctypes_function(hkey, lpSubKey, lpValue, dwFlags, pdwType, pvData, pcbData) + @Advapi32Proxy('RegCloseKey', kernel32_zero_check) def RegCloseKey(hKey): return RegCloseKey.ctypes_function(hKey) -###### Iphlpapi (network list and stuff) ####### - +# ##### Iphlpapi (network list and stuff) ###### # SetTcpEntry = TransparentIphlpapiProxy('SetTcpEntry') + @OptionalExport(IphlpapiProxy('GetExtendedTcpTable')) def GetExtendedTcpTable(pTcpTable, pdwSize=None, bOrder=True, ulAf=NeededParameter, TableClass=TCP_TABLE_OWNER_PID_ALL, Reserved=0): if pdwSize is None: