如何分配可执行内存缓冲区?

新手上路,请多包涵

我想分配一个可以在 Win32 上执行的缓冲区,但我在 Visual Studio 中有一个异常,因为 malloc 函数返回一个不可执行的内存区域。我读到有一个 NX 标志要禁用…我的目标是将字节码即时转换为 asm x86,同时牢记性能。

有人可以帮助我吗?

JS

原文由 John Smith 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 379
2 个回答

您不要为此使用 malloc 。你为什么要在 C++ 程序中呢?但是,您也不 new 用于可执行内存。有特定于 Windows 的 VirtualAlloc 函数来保留内存,然后您可以使用 VirtualProtect 函数将其标记为可执行文件,例如应用 PAGE_EXECUTE_READ 标志。

完成后,您可以将指向已分配内存的指针转换为适当的函数指针类型,然后调用该函数。完成后不要忘记致电 VirtualFree

这是一些非常基本的示例代码,没有错误处理或其他完整性检查,只是为了向您展示如何在现代 C++ 中实现这一点(程序打印 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";
}

与普通的 C++ 内存管理相比,这是非常不寻常的,但并不是真正的火箭科学。困难的部分是正确获取实际的机器代码。请注意,我在这里的示例只是非常基本的 x64 代码。

原文由 Christian Hackl 发布,翻译遵循 CC BY-SA 3.0 许可协议

扩展上述答案,一个好的做法是:

  • 使用 VirtualAlloc 和读写访问分配内存。
  • 用您的代码填写该区域
  • 使用 VirtualProtect 更改该区域的保护以执行读取访问
  • 跳转到/调用该区域的入口点

所以它可能看起来像这样:

 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 发布,翻译遵循 CC BY-SA 3.0 许可协议

推荐问题