mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
Add documentation for windows.native_exec
This commit is contained in:
@@ -15,6 +15,7 @@ Contents:
|
||||
windows.rst
|
||||
winproxy.rst
|
||||
utils.rst
|
||||
native_exec.rst
|
||||
|
||||
|
||||
Indices and tables
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user