2

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?

11
  • 1
    It looks like as used, your memcpy(buf, code, info.dwPageSize); will probably overrun the bounds of the array (in)to which code points. Undefined behavior results. Commented Oct 29 at 13:43
  • 3
    I using the x64 ABI for windows correctly - no, you need allocate 32(0x20) bytes for called function in stack, and you not do this, so test can overwrite your rbp and return address. you not need push rbp/pop rbp here, anf you need memcpy(buf, code, sizeof(code)); but not page_size Commented Oct 29 at 14:05
  • 5
    @JohnBollinger we need allocate space for Register parameter stack area - Rcx,Rdx,R8,R9 home (look on picture). this is 8*4=32(0x20) bytes. function B free to use this are, overwrite it. so in current code rbp and ret address can be overwritten. we need sub 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 call Commented Oct 29 at 14:16
  • 5
    RBX is a non-volatile register according to the calling conventions, so you must save and restore it if you use it, but your code does not. It would be simpler to replace your use of RBX by some volatile register, e.g. RDX. Commented Oct 29 at 14:38
  • 2
    @JohnBollinger: The 32 bytes of shadow space are part of the ABI because they let variadic functions dump the register args there. But that's not their only allowed use; just think of it as 32 bytes that every callee is allowed to use for anything, like arbitrary local variables, without spending instructions to sub/add to 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. Commented Oct 29 at 18:55

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.