how to call assembler code with Python on Windows

Question:

this link gives a good example to execute asm with Python on Linux platform, but I dont know how to call an asm func with return value on Windows, Could you please tell me how to do that or give me an example?

import ctypes
import mmap

buf = mmap.mmap(-1, mmap.PAGESIZE, prot=mmap.PROT_READ | mmap.PROT_WRITE | mmap.PROT_EXEC)

ftype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int)
fpointer = ctypes.c_void_p.from_buffer(buf)

f = ftype(ctypes.addressof(fpointer))

buf.write(
    b'x8bxc7'  # mov eax, edi
    b'x83xc0x01'  # add eax, 1
    b'xc3'  # ret
)

r = f(42)
print(r)

del fpointer
buf.close()
Asked By: tfxidian

||

Answers:

Okay, there are multiple differences between Unix and Windows OS, so such a low-level things generally attached to platform.

Let’s start from assembly listing.

mov eax, edi
add eax, 1
ret

This code specifically written for Unix OS. Why? Because of assumption that first argument passed using edi register. In Windows systems first argument passed using ecx, so correct assembly for Windows systems is:

mov eax, ecx
add eax, 1
ret

So, patched "compiled" assembly:

asm_function = (
    b'x8bxc1'  # mov eax, ecx
    b'x83xc0x01'  # add eax, 1
    b'xc3'  # ret
)

Regarding mmap. I have tried to make it work, but any attempt ended up with Access Violation message. I even tried to set PAGE_EXECUTE_READWRITE protection to allocated (using mmap()) memory using VirtualProtect() but WinAPI function failed with ERROR_INVALID_PARAMETER(0x57) error code.

As I can’t make it work with nmap why not to go one layer down and allocate memory using WinAPI? It’s quite simple, we just need to call VirtualAlloc() to allocate memory and RtlMoveMemory() to copy "precompiled" assembly to this allocated memory.

We need to patch default argtypes and restype of ctypes.windll.kernel32.VirtualAlloc and ctypes.windll.kernel32.RtlMoveMemory to match signatures from MSDN. We have to do this, because on x64 systems pointers is 64 bit, but default return value is 32 bit, so address returned by VirtualAlloc() won’t be processed correctly. Special thanks to lifemaker, he/she pointed it out in his answer.

Final code:

import ctypes

asm_function = (
    b'x8bxc1'      # mov eax, ecx
    b'x83xc0x01'  # add eax, 1
    b'xc3'          # ret
)

# https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc#MEM_COMMIT
MEM_COMMIT = 0x00001000
# https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc#MEM_RESERVE
MEM_RESERVE = 0x00002000
# https://learn.microsoft.com/en-us/windows/win32/memory/memory-protection-constants#PAGE_EXECUTE_READWRITE
PAGE_EXECUTE_READWRITE = 0x40
# https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
ctypes.windll.kernel32.VirtualAlloc.argtypes = (
    ctypes.c_void_p,  # LPVOID
    ctypes.c_size_t,  # SIZE_T
    ctypes.c_long,    # DWORD
    ctypes.c_long,    # DWORD
)
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_void_p  # LPVOID

memory_buffer = ctypes.windll.kernel32.VirtualAlloc(
    0,                         # lpAddress - NULL
    len(asm_function),         # dwSize
    MEM_COMMIT | MEM_RESERVE,  # flAllocationType
    PAGE_EXECUTE_READWRITE     # flProtect
)

if not memory_buffer:  # VirtualAlloc returned NULL
    print("VirtualAlloc call failed. Error code:", ctypes.GetLastError())
    exit(-1)

c_buffer = ctypes.c_char_p(asm_function)

# https://learn.microsoft.com/en-us/windows/win32/devnotes/rtlmovememory
ctypes.windll.kernel32.RtlMoveMemory.argtypes = (
    ctypes.c_void_p,  # VOID*
    ctypes.c_void_p,  # VOID*
    ctypes.c_size_t   # SIZE_T
)

ctypes.windll.kernel32.RtlMoveMemory(
    memory_buffer,     # Destination
    c_buffer,          # Source
    len(asm_function)  # Length
)

f = ctypes.cast(
    memory_buffer,
    ctypes.CFUNCTYPE(
        ctypes.c_int,  # return type
        ctypes.c_int   # argument type
    )
)

r = f(42)
print(r)

P.S. I’d appreciate if anybody will add answer how to make it using mmap.

Answered By: Olvin Roght
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.