I am creating heap allocated memory with execute rights using windows.h VirtualAlloc and VirtualProtect.
void* write_into_executable(uint8_t code[]) {
SYSTEM_INFO info;
GetSystemInfo(&info);
void *buf = VirtualAlloc(NULL, info.dwPageSize, MEM_COMMIT, PAGE_READWRITE);
memcpy(buf, code, info.dwPageSize);
DWORD dummy;
VirtualProtect(buf, info.dwPageSize, PAGE_EXECUTE_READ, &dummy);
return buf;
}
then I want to call the test function using heap_exec:
int test(int v) {
return v;
}
void heap_exec() {
uintptr_t fnPt = (uintptr_t)test;
uint8_t code[] = {
0x55, // push rbp
0x48, 0x89, 0xE5, // mov rbp, rsp
0x48, 0xC7, 0xC1, 0x05, 0x00, 0x00, 0x00, // mov rcx, 0x5
0x48, 0xBB, fnPt>>0 , fnPt>>8 , fnPt>>16, fnPt>>24, // mov rbx, fnPt
fnPt>>32, fnPt>>40, fnPt>>48, fnPt>>56,
0xFF, 0xD3, // call rbx
0x5D, // pop rbp
0xC3, // ret
};
void *buf = write_into_executable(code);
typedef int (*callable) ();
printf("%d\n", ((callable)buf)());
VirtualFree(buf, 0, MEM_RELEASE);
}
am I using the x64 ABI for windows correctly (I'm putting the first argument in rcx)? are the instructions push rbp, pop rbp (which I copied from gcc -S -masm=intel main.c) redundant?
memcpy(buf, code, info.dwPageSize);will probably overrun the bounds of the array (in)to whichcodepoints. Undefined behavior results.testcan overwrite yourrbpand return address. you not need push rbp/pop rbp here, anf you needmemcpy(buf, code, sizeof(code));but not page_sizeBfree to use this are, overwrite it. so in current coderbpand ret address can be overwritten. we needsub rsp,32; call rbx; add rsp 32;as example. in real coding, we do sub/add rsp in function, only once in prolog and epilog, but not before every sub function callsub/addto RSP. Like the red zone in x86-64 SysV but worse because it's more work for callers to allocate. (With an upside only for variadic functions.) Anyway, Shadow space example shows a normal function that makes a couple calls.