1

I'm generating machine code for buffer overflow execution and wanted a quick and easy method to plug the bytecode into a program and see how it runs on my host machine.

I looked for ways to dynamically generate functions in C++ and came across this question with this very interesting answer. It has been upvoted quite a bit and there doesn't seem to be anyone challenging what they said.

However, when trying to implement what they wrote in my own program I get the error "Expression cannot be used as a function".

Here's my code:

int main()
{
    uint8_t machinecode[] = {0x90, 0x0F, 0x01};

    *reinterpret_cast<void**>(&machinecode)();

    return 0;
}
  • 1
    The OS won't allow you to execute an arbitrary chunk of memory. For that you need `mmap` (or equivalent, depending on your OS) in order to create an executable chunk of memory. Also `reinterpret_cast` to `void**`? No, you need to reinterpret to a function pointer. You may want to read this: https://eli.thegreenplace.net/2013/11/05/how-to-jit-an-introduction – freakish Sep 08 '18 at 14:05
  • @freakish I completely forgot about page protection in windows. Can't execute code on a page lacking PAGE_EXECUTE privileges. Thanks for the reminder. – Edward Severinsen Sep 08 '18 at 14:06
  • 1
    For starters, `()` has higher precedence than `*`, in this context. So that's wrong, right off the bat. Secondly, `void *` is not a callable function, it's a typeless pointer, and it cannot be called as function. A pointer to a function that returns a void is a `void (*)()`. – Sam Varshavchik Sep 08 '18 at 14:07
  • 1
    @EdwardSeverinsen Final question: what exactly are you trying to do? Writing your own jit is difficult, error prone and extremely time consuming task. Are you sure you don't want to use some existing tool, e.g. LLVM? – freakish Sep 08 '18 at 14:08
  • 1
    @EdwardSeverinsen The answer you linked to is wrong on multiple levels. I downvoted it and wrote a long comment trying to explain what's wrong with it. – melpomene Sep 08 '18 at 14:22
  • 2
    That the answer you linked got 32 upvotes only shows that people will upvote things they don't understand so long as it looks clever enough and already has some upvotes. – molbdnilo Sep 08 '18 at 14:40
  • 3
    Bytecode. You keep using that word. [I don't think it means what you think it means](https://en.wikipedia.org/wiki/Bytecode). – n. m. could be an AI Sep 08 '18 at 14:53
  • @freakish I'm exploiting a buffer overflow in an application and want to create a payload to be executed that accesses the `gs` register to get the PEB/TIB to enumerate the loaded modules to find kernel32 to get the address of certain functions. I haven't hammered out all the details yet obviously but this is the first step in the process. And thank you for suggesting LLVM, I'm looking into it. – Edward Severinsen Sep 08 '18 at 16:35
  • @n.m. I suppose the way I use it kind of ambiguates it's meaning but it still gets the point across. On various websites they refer to buffer overflow payloads with machine code as bytecode. But yes, you're correct, I should be calling it machine code. +1 – Edward Severinsen Sep 08 '18 at 16:47

2 Answers2

2

As far as code validity for compilation goes, in the hope that I understand you question correctly, you need to cast to a callable, which in this case is void(*)(), not simply void*, and you need an extra set of parentheses:

(*reinterpret_cast<void(*)()>(bytecode))();

See here live, but I'm not sure this is anything you actually want to run, even in the context you have provided.

Geezer
  • 5,600
  • 18
  • 31
  • `& [0]` is redundant. Dereferencing the function pointer with `*` is also redundant because the resulting function immediately decays back to a pointer (to be called by `()`). The code can be shortened to `reinterpret_cast(bytecode)()`. – melpomene Sep 08 '18 at 14:49
  • @melpomene a mistype from copying a few attempta. Thanks. Sorry – Geezer Sep 08 '18 at 14:56
  • Sorry, being stupid again. Been programming in C++ for a few years but sometimes I forget the most basic syntax. :P – Edward Severinsen Sep 08 '18 at 16:24
  • 1
    @EdwardSeverinsen wouldn't call it being stupid, it's definitely not *the most basic* syntax. Glad to have helped. – Geezer Sep 08 '18 at 16:32
1

SkepticalEmpiricist's answer was correct and solved the compilation issue so I marked it as correct.

However, what I had to do was create a function to allocate executable memory with VirtualAlloc:

uint8_t* alloc_executable(uint32_t alloc_size)
{
    if(!alloc_size)
        return nullptr;

    return reinterpret_cast<uint8_t*>(VirtualAlloc(NULL, alloc_size, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE));
}

My main function:

int main()
{
    /*
    * nop - 0x90
    * ret - 0xC3
    */
    uint8_t machinecode[] = {0x90, 0xC3};
    uint32_t machinecode_size = ARRAYSIZE(machinecode);

    uint8_t* exec_mem = alloc_executable(machinecode_size);

    memcpy(exec_mem, bytecode, machinecode_size);

    FlushInstructionCache(GetCurrentProcess(), exec_mem, machinecode_size);

    auto func = reinterpret_cast<void(*)()>(exec_mem);

    func();

    return 0;
}

Process returns 0 with no errors.

ALSO: Obviously this is Windows specific. My target platform is x64 Windows 10.