From 4e9cf7684e17ae199874a7f90ad62ce280eca9de Mon Sep 17 00:00:00 2001 From: Clement Rouault Date: Mon, 4 Jan 2016 18:15:03 +0100 Subject: [PATCH] Add documentation for windows.native_exec --- doc/source/index.rst | 1 + doc/source/native_exec.rst | 179 +++++++++++++++++++++++++++++++++++ windows/native_exec/cpuid.py | 42 ++++++-- 3 files changed, 216 insertions(+), 6 deletions(-) create mode 100644 doc/source/native_exec.rst diff --git a/doc/source/index.rst b/doc/source/index.rst index ef9e425..f7bbfa5 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -15,6 +15,7 @@ Contents: windows.rst winproxy.rst utils.rst + native_exec.rst Indices and tables diff --git a/doc/source/native_exec.rst b/doc/source/native_exec.rst new file mode 100644 index 0000000..01021a1 --- /dev/null +++ b/doc/source/native_exec.rst @@ -0,0 +1,179 @@ +``windows.native_exec`` -- Native Code Execution +************************************************ + +.. currentmodule:: windows.native_exec + +The :mod:`windows.native_exec` allows to create `Python` functions calling native code. +it also provide a simple assembler for x86 and x64. + +The :mod:`windows.native_exec` provides those functions: + +.. automodule:: windows.native_exec + +The :mod:`windows.native_exec` also contains some submodules: + * :mod:`windows.native_exec.cpuid` + * :mod:`windows.native_exec.simple_x86` + * :mod:`windows.native_exec.simple_x64` + +:mod:`windows.native_exec.cpuid` -- Interface to native CPUID +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +.. automodule:: windows.native_exec.cpuid + :no-show-inheritance: + :no-members: bitness + +Demo:: + + >>> import windows.native_exec.cpuid + >>> windows.native_exec.cpuid.do_cpuid(0) + + >>> x = windows.native_exec.cpuid.do_cpuid(0) + >>> x.EAX + 13L + >>> x.EBX + 1970169159L + >>> windows.native_exec.cpuid.get_vendor_id() + 'GenuineIntel' + >>> windows.native_exec.cpuid.get_proc_family_model() + (6L, 58L) + + +:mod:`windows.native_exec.simple_x86` -- X86 Assembler +"""""""""""""""""""""""""""""""""""""""""""""""""""""" + +.. module:: windows.native_exec.simple_x86 + +The :mod:`windows.native_exec.simple_x86` module allows to create simple x86 code. + +Its features are: + * Forward - Backward jump (using label) + * Non-string interface for conditional/context dependent generation + + +.. note:: + The assembler DOES NOT handle every instruction at all. + + +The assembler instructions are `Python` object that may accept arguments representing +the mnemonic operands. + +These parameters can be of type: + * str (register) + * int (int) + * mem_access (memory access) + +.. autoclass:: windows.native_exec.simple_x86.mem_access + :members: prefix, base, index, scale, disp + +The :class:`mem_access` object can be created: + * By hand + * Using :func:`create_displacement` + * Using :func:`mem` + +.. autofunction:: windows.native_exec.simple_x86.create_displacement +.. autofunction:: windows.native_exec.simple_x86.mem + +Instruction assembling:: + + >>> import windows.native_exec.simple_x86 as x86 + >>> import random + >>> x86.Mov + + >>> instr = x86.Mov("EAX", "EBX") + >>> instr + + >>> instr.get_code() + '\x89\xd8' + >>> x86.Mov("EAX", 0x42424242).get_code() + '\xc7\xc0BBBB' + >>> x86.Mov("EAX", x86.create_displacement(base="EAX", disp=random.randint(0, 0xffffffff))).get_code() + '\x8b\x80\x977\n&' + >>> x86.Mov(x86.mem("[EBX + EDI * 2 + 0x11111111]"), "EAX").get_code() + '\x89\x84{\x11\x11\x11\x11' + >>> x86.Mov(x86.mem("gs:[EBX + EDI * 2 + 0x11111111]"), "EAX").get_code() + 'e\x89\x84{\x11\x11\x11\x11' + +:mod:`windows.native_exec.simple_x86` also provides an interface to complex shellcode assembling +including jump and label via the :class:`MultipleInstr` class. + +Shellcode assembling:: + + import windows.native_exec.simple_x86 as x86 + + code = x86.MultipleInstr() + code += x86.Label(":BEGIN") + code += x86.Jmp(":BEGIN") + print(repr(code.get_code())) + # '\xeb\xfe' + +Another example from a project:: + + IO_STACK_INPUT_BUFFER_LEN = x86.mem('[ESI + 8]') + IO_STACK_INPUT_BUFFER = x86.mem('[ESI + 0x10]') + + INPUT_BUFFER_SIZE = x86.mem('[ECX]') + INPUT_BUFFER_PORT = x86.mem('[ECX + 4]') + INPUT_BUFFER_VALUE = x86.mem('[ECX + 8]') + + out_ioctl += x86.Cmp(IO_STACK_INPUT_BUFFER_LEN, 0xc) # size indicator / port / value + out_ioctl += x86.Jnz(":FAIL") + out_ioctl += x86.Mov('ECX', IO_STACK_INPUT_BUFFER) + out_ioctl += x86.Mov('EDX', INPUT_BUFFER_PORT) + out_ioctl += x86.Mov('EAX', INPUT_BUFFER_VALUE) + out_ioctl += x86.Mov('ECX', INPUT_BUFFER_SIZE) + out_ioctl += x86.Cmp('ECX', 0x1) + out_ioctl += x86.Jnz(":OUT_2_OR_4") + out_ioctl += x86.Out('DX', 'AL') + out_ioctl += x86.Jmp(':SUCCESS') + out_ioctl += x86.Label(":OUT_2_OR_4") + out_ioctl += x86.Cmp('ECX', 0x2) + out_ioctl += x86.Jnz(":OUT_4") + out_ioctl += x86.Out('DX', 'AX') + out_ioctl += x86.Jmp(':SUCCESS') + out_ioctl += x86.Label(":OUT_4") + out_ioctl += x86.Out('DX', 'EAX') + out_ioctl += x86.Label(":SUCCESS") + out_ioctl += x86.Xor('EAX', 'EAX') + out_ioctl += x86.Ret() + out_ioctl += x86.Label(":FAIL") + out_ioctl += x86.Mov('EAX', 0x0C000000D) + out_ioctl += x86.Ret() + +.. note:: + + TODO: prefix + +:mod:`windows.native_exec.simple_x64` -- X64 Assembler +"""""""""""""""""""""""""""""""""""""""""""""""""""""" + +.. module:: windows.native_exec.simple_x64 + +Same things as :mod:`windows.native_exec.simple_x86` + +The only things that change are: + * The registers name + +:mod:`windows.native_exec.simple_x64` handles 32 and 64 bits operations. + +Demo:: + + >>> import windows.native_exec.simple_x64 as x64 + >>> x64.Mov("RAX", "R13").get_code() + 'L\x89\xe8' + >>> x64.Mov("EAX", "EDI").get_code() + '\x89\xf8' + >>> x64.Mov("RAX", "EDI").get_code() + """ + ValueError: Size mismatch + """ + >>> x64.Mov("RAX", x64.mem("[EAX]")).get_code() + 'gH\x8b\x00' + >>> x64.Mov("RAX", x64.mem("[RAX]")).get_code() + 'H\x8b\x00' + >>> x64.Mov("EAX", x64.mem("[RAX]")).get_code() + '\x8b\x00' + >>> x64.Mov("EAX", x64.mem("[EAX]")).get_code() + 'g\x8b\x00' + + + diff --git a/windows/native_exec/cpuid.py b/windows/native_exec/cpuid.py index 8961b03..b32abbf 100644 --- a/windows/native_exec/cpuid.py +++ b/windows/native_exec/cpuid.py @@ -7,7 +7,7 @@ import simple_x64 as x64 from windows.generated_def.winstructs import * -def bitness(): +def _bitness(): """Return 32 or 64""" import platform bits = platform.architecture()[0] @@ -15,11 +15,13 @@ def bitness(): class X86CpuidResult(ctypes.Structure): + """Raw result of the CPUID instruction""" _fields_ = [("EAX", DWORD), ("EBX", DWORD), ("ECX", DWORD), ("EDX", DWORD)] - + fields = [f[0] for f in _fields_] + """Fields of the Structure""" class X64CpuidResult(ctypes.Structure): _fields_ = [("RAX", ULONG64), @@ -37,6 +39,8 @@ class X86IntelCpuidFamilly(ctypes.Structure): ("ExtendedModel", DWORD, 4), ("ExtendedFamily", DWORD, 8), ("Reserved", DWORD, 2)] + fields = [f[0] for f in _fields_] + """Fields of the Structure""" class X86AmdCpuidFamilly(ctypes.Structure): @@ -47,7 +51,8 @@ class X86AmdCpuidFamilly(ctypes.Structure): ("ExtendedModel", DWORD, 4), ("ExtendedFamily", DWORD, 8), ("Reserved", DWORD, 2)] - + fields = [f[0] for f in _fields_] + """Fields of the Structure""" cpuid32_code = x86.MultipleInstr() cpuid32_code += x86.Push('EDI') @@ -77,39 +82,64 @@ do_cpuid64 = native_function.create_function(cpuid64_code.get_code(), [DWORD, DW def x86_cpuid(req): + """Perform a CPUID in 32bits mode + + :rtype: :class:`X86CpuidResult` + """ cpuid_res = X86CpuidResult() do_cpuid32(req, ctypes.addressof(cpuid_res)) return cpuid_res def x64_cpuid(req): + """Perform a CPUID in 64bits mode + + :rtype: :class:`X86CpuidResult` + """ 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 +if _bitness() == 32: + _do_cpuid = x86_cpuid else: - do_cpuid = x64_cpuid + _do_cpuid = x64_cpuid + +def do_cpuid(req): + """Perform a CPUID for the current process bitness + + :rtype: :class:`X86CpuidResult` + """ + return _do_cpuid(req) def get_vendor_id(): + """Extract the VendorId string from CPUID + + :rtype: :class:`str` + """ cpuid_res = do_cpuid(0) return struct.pack("