I have the disassamble bytes of a simple function
89 4C 24 08 mov dword ptr [sum],ecx
while (sum>=1) {
83 7C 24 08 01 cmp dword ptr [sum],1
7C 0C jl doNothing+17h (07FF636C61017h)
sum--;
8B 44 24 08 mov eax,dword ptr [sum]
FF C8 dec eax
89 44 24 08 mov dword ptr [sum],eax
}
EB ED jmp doNothing+4h (07FF636C61004h)
}
C3 ret
which is a bytes object in python bytes.fromhex('89 4c 24 08 83 7c 24 08 01 7c 0c 8b 44 24 08 ff c8 89 44 24 08 eb ed c3 ')
How to call this micro codes in python using ctypes? I tried the code as below, but it crashes.
import ctypes
# raw disassamble bytes
buf = bytes.fromhex('89 4c 24 08 83 7c 24 08 01 7c 0c 8b 44 24 08 ff c8 89 44 24 08 eb ed c3 ')
# function type definition
nothFn = ctypes.CFUNCTYPE(None, ctypes.c_int)
# ctypes buffer
codebuf = ctypes.create_string_buffer(buf)
# raw buffer's address as the function
cfunc = nothFn(ctypes.addressof(codebuf))
# call it then it crashes
cfunc(ctypes.c_int(3))
I also tried to use the address returned from str(codebuf)
but it also crashes.
Questions:
Modern processors protect data memory from exploits by making it non-executable. Changing that protection is OS-specific.
Below is an example for Windows to execute code from a buffer. I used a simple code example since dword ptr [sum]
is referring to a stack frame variable that is undefined.
import sys
import ctypes as ct
import ctypes.wintypes as w
print(sys.version)
PAGE_EXECUTE_READWRITE = 0x40
def boolcheck(result, func, args):
if not result:
raise ct.WinError(ct.get_last_error())
kernel32 = ct.WinDLL('kernel32', use_last_error=True)
# BOOL VirtualProtect(
# [in] LPVOID lpAddress,
# [in] SIZE_T dwSize,
# [in] DWORD flNewProtect,
# [out] PDWORD lpflOldProtect
# );
VirtualProtect = kernel32.VirtualProtect
VirtualProtect.argtypes = w.LPVOID, ct.c_size_t, w.DWORD, w.LPDWORD
VirtualProtect.restype = w.BOOL
VirtualProtect.errcheck = boolcheck
# Simple function with no stack frame and four parameters.
# In Microsoft's x64 calling convention RCX, RDX, R8, R9 are
# the registers used for the first four parameters.
# The return value is the sum of the four registers.
# 0: 48 89 c8 mov rax,rcx
# 3: 48 01 d0 add rax,rdx
# 6: 4c 01 c0 add rax,r8
# 9: 4c 01 c8 add rax,r9
# c: c3 ret
buf = ct.create_string_buffer(bytes.fromhex('4889C84801D04C01C04C01C8C3'))
# Get the address of the buffer and make it executable.
addr = ct.addressof(buf)
old = w.DWORD()
VirtualProtect(addr, len(buf), PAGE_EXECUTE_READWRITE, ct.byref(old))
FUNC = ct.CFUNCTYPE(ct.c_uint64, ct.c_uint64, ct.c_uint64, ct.c_uint64, ct.c_uint64)
func = FUNC(ct.addressof(buf))
ret = func(0x1234000000000000, 0x567800000000, 0x9abc0000, 0xdef0)
print(hex(ret))
Output (Windows 10 64-bit):
3.12.6 (tags/v3.12.6:a4a2d2b, Sep 6 2024, 20:11:23) [MSC v.1940 64 bit (AMD64)]
0x123456789abcdef0
Note that the x64 calling convention varies per OS.
References: