3. windows.native_exec
– Native Code Execution¶
windows.native_exec
allows to create Python
functions calling native code.
it also provides a simple assembler for x86 and x64.
windows.native_exec
provides those functions:
-
windows.native_exec.
create_function
(code, types)[source]¶ Create a python function that call raw machine code
Parameters: Returns: the created function
Return type: function
- The
windows.native_exec
also contains some submodules:
3.1. windows.native_exec.cpuid
– Interface to native CPUID¶
-
class
windows.native_exec.cpuid.
X86AmdCpuidFamilly
[source]¶ -
fields
= ['SteppingID', 'ModelID', 'FamilyID', 'Reserved2', 'ExtendedModel', 'ExtendedFamily', 'Reserved']¶ Fields of the Structure
-
-
class
windows.native_exec.cpuid.
X86CpuidResult
[source]¶ Raw result of the CPUID instruction
-
fields
= ['EAX', 'EBX', 'ECX', 'EDX']¶ Fields of the Structure
-
-
class
windows.native_exec.cpuid.
X86IntelCpuidFamilly
[source]¶ -
fields
= ['SteppingID', 'ModelID', 'FamilyID', 'ProcessorType', 'Reserved2', 'ExtendedModel', 'ExtendedFamily', 'Reserved']¶ Fields of the Structure
-
-
windows.native_exec.cpuid.
do_cpuid
(req)[source]¶ Performs a CPUID for the current process bitness
Return type: X86CpuidResult
-
windows.native_exec.cpuid.
get_proc_family_model
()[source]¶ Extracts the family and model based on vendorId
Return type: (ComputedFamily, ComputedModel)
-
windows.native_exec.cpuid.
get_vendor_id
()[source]¶ Extracts the VendorId string from CPUID
Return type: str
-
windows.native_exec.cpuid.
x64_cpuid
(req)[source]¶ Performs a CPUID in 64bits mode
Return type: X86CpuidResult
-
windows.native_exec.cpuid.
x86_cpuid
(req)[source]¶ Performs a CPUID in 32bits mode
Return type: X86CpuidResult
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)
3.2. windows.native_exec.simple_x86
– X86 Assembler¶
The windows.native_exec.simple_x86
module allows to create simple x86 code.
- Its features are:
- Forward - Backward jump (using labels)
- 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)
-
class
windows.native_exec.simple_x86.
mem_access
(base, index, scale, disp, prefix)¶ -
base
¶ Alias for field number 0
-
disp
¶ Alias for field number 3
-
index
¶ Alias for field number 1
-
prefix
¶ Alias for field number 4
-
scale
¶ Alias for field number 2
-
- The
mem_access
object can be created: - By hand
- Using
create_displacement()
- Using
mem()
-
windows.native_exec.simple_x86.
create_displacement
(base=None, index=None, scale=None, disp=0, prefix=None)[source]¶ Creates a X86 memory access description
-
windows.native_exec.simple_x86.
deref
(disp)[source]¶ Create a memory access for an immediate value
Ex: [0x42424242]
-
windows.native_exec.simple_x86.
mem
(data)[source]¶ Parse a memory access string of format
[EXPR]
orseg:[EXPR]
EXPR
may describe:BASE | INDEX * SCALE | DISPLACEMENT
or any combinaison (in this order)
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'
windows.native_exec.simple_x86
also provides an interface to complex shellcode assembling
including jump and label via the 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.MultipleInstr()
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()
out_ioctl.get_code()
'\x81~\x08\x0c\x00\x00\x00u&\x8bN\x10\x8bQ\x04\x8bA\x08\x8b\t\x81\xf9\x01\x00\x00\x00u\x03\xee\xeb\r\x81\xf9\x02\x00\x00\x00u\x04f\xef\xeb\x01\xef1\xc0\xc3\xc7\xc0\r\x00\x00\xc0\xc3'
3.2.1. Available Instructions in X86 assembler¶
Note
Not all encodings may be implemented for each of the instructions.
Byte
and Raw
are not x86 instruction and allow to insert arbitrary data to the generated code.
Listing:
- Add
- And
- Byte
- Cmp
- CmpsB
- CmpsD
- CmpsW
- Cpuid
- Dec
- In
- Inc
- Int
- Int3
- Iret
- Lea
- Mov
- Movsb
- Movsd
- Nop
- Not
- Or
- Out
- Pop
- Popad
- Popfd
- Push
- Pushad
- Pushfd
- Raw
- Ret
- Retf
- Rol
- Ror
- ScasB
- ScasD
- ScasW
- Shl
- Shr
- StosB
- StosD
- StosW
- Sub
- Test
- Xchg
- Xor
Note
Raw
: Output raw data from an hexadecimal string
>>> windows.native_exec.simple_x64.Raw("90C332FCFF").get_code()
'\x90\xc32\xfc\xff'
Byte
: Output a raw byte
>>> windows.native_exec.simple_x86.Byte(0x42).get_code()
'B'
3.3. windows.native_exec.simple_x64
– X64 Assembler¶
Same things as windows.native_exec.simple_x86
- The only things that change are:
- The registers name
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'
3.3.1. Available Instructions in X64 assembler¶
Note
Not all encodings may be implemented for each of the instructions.
Raw
is not an x64 instruction and allow to insert arbitrary data to the generated code.
- Listing:
- Add
- And
- Cmp
- CmpsB
- CmpsD
- CmpsQ
- CmpsW
- Cpuid
- Dec
- In
- Inc
- Int
- Int3
- Lea
- Mov
- Nop
- Not
- Or
- Out
- Pop
- Popfq
- Push
- Pushfq
- Raw
- Ret
- Retf
- Retf32
- ScasB
- ScasD
- ScasQ
- ScasW
- Shl
- Shr
- Sub
- Test
- Xchg
- Xor
Note
Raw
: Output raw data from an hexadecimal string
>>> windows.native_exec.simple_x64.Raw("90C332FCFF").get_code()
'\x90\xc32\xfc\xff'
3.4. windows.native_exec.nativeutils
– Native utility functions¶
This module contains some native-code functions that can be used for various purposes.
Each function export a label that allow another MultipleInstr
to call the code of the function.
The current functions are:
StrlenW64
A 64bits wide-string STRLEN (Label(":FUNC_STRLENW64")
)
StrlenA64
A 64bits ASCII STRLEN (Label(":FUNC_STRLENA64")
)
GetProcAddress64
A 64bits export resolver (Label(":FUNC_GETPROCADDRESS64")
)
Arg1: The DLL (wstring)
Arg2: The API (string)
Return value:
- 0xfffffffffffffffe if the DLL is not found
- 0xffffffffffffffff if the API is not found
- The address of the function
StrlenW32
A 32bits wide-string STRLEN (Label(":FUNC_STRLENW32")
)
StrlenA32
A 32bits ASCII STRLEN (Label(":FUNC_STRLENA32")
)
GetProcAddress32
A 32bits export resolver (Label(":FUNC_GETPROCADDRESS32")
)
Arg1: The DLL (wstring)
Arg2: The API (string)
Return value:
- 0xfffffffe if the DLL is not found
- 0xffffffff if the API is not found
- The address of the function
To use those functions in a MultipleInstr
just call the label in your code and append the function at
the end of your MultipleInstr
Example:
RemoteManualLoadLibray = x86.MultipleInstr()
RemoteManualLoadLibray += x86.Mov("ECX", x86.mem("[ESP + 4]"))
RemoteManualLoadLibray += x86.Push(x86.mem("[ECX + 4]"))
RemoteManualLoadLibray += x86.Push(x86.mem("[ECX]"))
RemoteManualLoadLibray += x86.Call(":FUNC_GETPROCADDRESS32")
RemoteManualLoadLibray += x86.Push(x86.mem("[ECX + 8]"))
RemoteManualLoadLibray += x86.Call("EAX") # LoadLibrary
RemoteManualLoadLibray += x86.Pop("ECX")
RemoteManualLoadLibray += x86.Pop("ECX")
RemoteManualLoadLibray += x86.Ret()
RemoteManualLoadLibray += GetProcAddress32