11

I would like to alloc a buffer that I can execute on Win32 but I have an exception in visual studio cuz the malloc function returns a non executable memory zone. I read that there a NX flag to disable... My goal is convert a bytecode to asm x86 on fly with keep in mind performance.

Does somemone can help me?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
John Smith
  • 771
  • 4
  • 11
  • 25
  • 1
    You would probably like to take a look at [asmJit](https://github.com/asmjit/asmjit) which is free code that does all the heavy lifting for the assembly/memory management part of writing a JIT, including setting the memory protection bits so that you can execute your code. – BeeOnRope Dec 02 '16 at 16:39

5 Answers5

26

You don't use malloc for that. Why would you anyway, in a C++ program? You also don't use new for executable memory, however. There's the Windows-specific VirtualAlloc function to reserve memory which you then mark as executable with the VirtualProtect function applying, for instance, the PAGE_EXECUTE_READ flag.

When you have done that, you can cast the pointer to the allocated memory to an appropriate function pointer type and just call the function. Don't forget to call VirtualFree when you are done.

Here is some very basic example code with no error handling or other sanity checks, just to show you how this can be accomplished in modern C++ (the program prints 5):

#include <windows.h>
#include <vector>
#include <iostream>
#include <cstring>

int main()
{
    std::vector<unsigned char> const code =
    {
        0xb8,                   // move the following value to EAX:
        0x05, 0x00, 0x00, 0x00, // 5
        0xc3                    // return what's currently in EAX
    };    

    SYSTEM_INFO system_info;
    GetSystemInfo(&system_info);
    auto const page_size = system_info.dwPageSize;

    // prepare the memory in which the machine code will be put (it's not executable yet):
    auto const buffer = VirtualAlloc(nullptr, page_size, MEM_COMMIT, PAGE_READWRITE);

    // copy the machine code into that memory:
    std::memcpy(buffer, code.data(), code.size());

    // mark the memory as executable:
    DWORD dummy;
    VirtualProtect(buffer, code.size(), PAGE_EXECUTE_READ, &dummy);

    // interpret the beginning of the (now) executable memory as the entry
    // point of a function taking no arguments and returning a 4-byte int:
    auto const function_ptr = reinterpret_cast<std::int32_t(*)()>(buffer);

    // call the function and store the result in a local std::int32_t object:
    auto const result = function_ptr();

    // free the executable memory:
    VirtualFree(buffer, 0, MEM_RELEASE);

    // use your std::int32_t:
    std::cout << result << "\n";
}

It's very unusual compared to normal C++ memory management, but not really rocket science. The hard part is to get the actual machine code right. Note that my example here is just very basic x64 code.

Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
  • 3
    The reason you would use malloc in a C++ program is to allocate dynamic memory from your heap. Perfectly fine and normal. – Kyle Sweet Dec 02 '16 at 18:48
  • 2
    @KyleSweet: In C++, you use `new` (either the "normal" one or placement new like `std::allocator`) to dynamically allocate from the free store (not the heap). You can technically use `malloc`, but then you are effectively writing C and not (idiomatic) C++. For the OP's question, the distinction between `VirtualAlloc` and `malloc` is also important. – Christian Hackl Dec 02 '16 at 19:23
  • 3
    I was just answering the question you posed. If all you want is some quick and dirty memory from the heap, don't be afraid to use malloc! – Kyle Sweet Dec 02 '16 at 22:31
  • 1
    @KyleSweet: The point is that you will hardly find a convincing use case in C++ for "get some quick and dirty memory from the heap". And of course you should be afraid to use memory allocation which disregards basic C++ language rules like constructors and which quickly results in undefined behaviour with casting and memsets if you don't pay a lot of attention. – Christian Hackl Dec 03 '16 at 10:40
  • 1
    Memory pools are a great example, or anything that requires the use of memeory - this is bounded only by your imagination. I agree, you must pay a lot of attention when working with pointers. If you are "afraid" you should probably consider another language. – Kyle Sweet Dec 04 '16 at 14:57
  • 1
    Nitpick: you're missing a `MEM_RESERVE`: https://devblogs.microsoft.com/oldnewthing/20151008-00/?p=91411 – Paul May 05 '19 at 19:00
6

Extending the above answer, a good practice is:

  • Allocate memory with VirtualAlloc and read-write-access.
  • Fill that region with your code
  • Change that region's protection with VirtualProtectto execute-read-access
  • jump to/call the entry point in this region

So it could look like this:

adr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
// write code to the region
ok  = VirtualProtect(adr, size, PAGE_EXECUTE_READ, &oldProtection);
// execute the code in the region
zx485
  • 28,498
  • 28
  • 50
  • 59
4

As stated in documentation for VirtualAlloc

flProtect [in]

The memory protection for the region of pages to be allocated. If the pages are being committed, you can specify any one of the memory protection constants.

one of them is:

PAGE_EXECUTE 0x10 Enables execute access to the committed region of pages. An attempt to write to the committed region results in an access violation. This flag is not supported by the CreateFileMapping function.

PAGE_EXECUTE_READ 0x20 Enables execute or read-only access to the committed region of pages. An attempt to write to the committed region results in an access violation. Windows Server 2003 and Windows XP: This attribute is not supported by the CreateFileMapping function until Windows XP with SP2 and Windows Server 2003 with SP1.

PAGE_EXECUTE_READWRITE 0x40 Enables execute, read-only, or read/write access to the committed region of pages. Windows Server 2003 and Windows XP: This attribute is not supported by the CreateFileMapping function until Windows XP with SP2 and Windows Server 2003 with SP1.

and so on from here

Slava
  • 43,454
  • 1
  • 47
  • 90
1

C version based off of Christian Hackl's answer
I think SIZE_T dwSize of VirtualAlloc should be the size of the code in bytes, not system_info.dwPageSize (what if sizeof code is bigger than system_info.dwPageSize?).
I don't know C enough to know if sizeof(code) is the "correct" way of getting the size of the machine code
this compiles under c++ so I guess it's not off topic lol

#include <Windows.h>
#include <stdio.h>

int main()
{
    // double add(double a, double b) {
    //     return a + b;
    // }
    unsigned char code[] = { //Antonio Cuni - How to write a JIT compiler in 30 minutes: https://www.youtube.com/watch?v=DKns_rH8rrg&t=118s
        0xf2,0x0f,0x58,0xc1, //addsd  %xmm1,%xmm0
        0xc3, //ret
    };

    LPVOID buffer = VirtualAlloc(NULL, sizeof(code), MEM_COMMIT, PAGE_READWRITE);

    memcpy(buffer, code, sizeof(code));

    //protect after write, because protect will prevent writing.
    DWORD oldProtection;
    VirtualProtect(buffer, sizeof(code), PAGE_EXECUTE_READ, &oldProtection);

    double (*function_ptr)(double, double) = (double (*)(double, double))buffer; //is there a cleaner way to write this ?

    // double result = (*function_ptr)(2, 234); //NOT SURE WHY THIS ALSO WORKS
    double result = function_ptr(2, 234);

    VirtualFree(buffer, 0, MEM_RELEASE);

    printf("%f\n", result);
}
Mr. Doge
  • 796
  • 5
  • 11
-3

At compile time, the linker will organize your program's memory footprint by allocating memory into data sections and code sections. The CPU will make sure that the program counter (the hard CPU register) value remains within a code section or the CPU will throw a hardware exception for violating the memory bounds. This provides some security by making sure your program only executes valid code. Malloc is intended for allocating data memory. Your application has a heap and the heap's size is established by the linker and is marked as data memory. So at runtime malloc is just grabbing some of the virtual memory from your heap which will always be data.

I hope this helps you have a better understanding what's going on, though it might not be enough to get you where you need to be. Perhaps you can pre-allocate a "code heap" or memory pool for your runtime-generated code. You will probably need to fuss with the linker to accomplish this but I don't know any of the details.

Kyle Sweet
  • 306
  • 1
  • 3
  • 15
  • The read/write/execute permissions on pages of *dynamically* allocated memory have nothing to do with the linker. If you wanted some *statically allocated* write+execute pages, you could do that with linker scripts or GNU C `__attribute__` stuff on an array. – Peter Cordes Oct 01 '17 at 23:54
  • 1
    Also, no, memory protection doesn't work by checking that the program counter (RIP on x86-64) stays within certain ranges. That's how segmentation worked (with base / limit), but x86-64 doesn't even support segment limits in 64-bit mode. Memory protection is on a per-page basis, with bits in the page table (set by the OS). See https://stackoverflow.com/questions/46509152/why-in-64bit-the-virtual-address-are-4-bits-short-48bit-long-compared-with-the for a diagram. – Peter Cordes Oct 01 '17 at 23:58