Add documentation for windows.native_exec

This commit is contained in:
Clement Rouault
2016-01-04 18:15:03 +01:00
parent 0b2eabde5c
commit 4e9cf7684e
3 changed files with 216 additions and 6 deletions
+1
View File
@@ -15,6 +15,7 @@ Contents:
windows.rst
winproxy.rst
utils.rst
native_exec.rst
Indices and tables
+179
View File
@@ -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)
<windows.native_exec.cpuid.X86CpuidResult object at 0x0330D990>
>>> 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
<class 'windows.native_exec.simple_x86.Mov'>
>>> instr = x86.Mov("EAX", "EBX")
>>> instr
<windows.native_exec.simple_x86.Mov object at 0x03243770>
>>> 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'
+36 -6
View File
@@ -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("<III", cpuid_res.EBX, cpuid_res.EDX, cpuid_res.ECX)
# platform.processor() could do the trick
def is_intel_proc():
"""get_vendor_id() == 'GenuineIntel'"""
return get_vendor_id() == "GenuineIntel"
def is_amd_proc():
"""get_vendor_id() == 'AuthenticAMD'"""
return get_vendor_id() == "AuthenticAMD"
def get_proc_family_model():
"""Extract the family and model based on vendorId
:rtype: (ComputedFamily, ComputedModel)
"""
cpuid_res = do_cpuid(1)
if is_intel_proc():
format = X86IntelCpuidFamilly