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'\x8b\xc7' # mov eax, edi
b'\x83\xc0\x01' # add eax, 1
b'\xc3' # ret
)
r = f(42)
print(r)
del fpointer
buf.close()
CodePudding user response:
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'\x8b\xc1' # mov eax, ecx
b'\x83\xc0\x01' # 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'\x8b\xc1' # mov eax, ecx
b'\x83\xc0\x01' # add eax, 1
b'\xc3' # ret
)
# https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc#MEM_COMMIT
MEM_COMMIT = 0x00001000
# https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc#MEM_RESERVE
MEM_RESERVE = 0x00002000
# https://docs.microsoft.com/en-us/windows/win32/memory/memory-protection-constants#PAGE_EXECUTE_READWRITE
PAGE_EXECUTE_READWRITE = 0x40
# https://docs.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://docs.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
.
You can help my country, check my profile info.