如何分配可执行内存缓冲区?
How to alloc a executable memory buffer?
我想分配一个可以在 Win32 上执行的缓冲区,但我在 visual studio 中有一个异常,因为 malloc 函数 returns 是一个不可执行的内存区域。我读到有一个 NX 标志可以禁用...我的目标是将字节码动态转换为 asm x86,同时牢记性能。
有人可以帮助我吗?
JS
如 documentation 所述 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.
其中之一是:
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.
等等 here
在编译时,链接器将通过将内存分配到数据段和代码段来组织程序的内存占用。 CPU 将确保程序计数器(硬 CPU 寄存器)的值保留在代码段内,否则 CPU 将因违反内存边界而抛出硬件异常。这通过确保您的程序只执行有效代码来提供一些安全性。 Malloc 用于分配数据内存。您的应用程序有一个堆,堆的大小由链接器确定并标记为数据内存。所以在运行时 malloc 只是从你的堆中获取一些虚拟内存,这将永远是数据。
我希望这可以帮助您更好地了解正在发生的事情,尽管这可能不足以让您到达需要的位置。也许您可以为运行时生成的代码预先分配一个 "code heap" 或内存池。您可能需要对链接器大惊小怪才能完成此操作,但我不知道任何细节。
扩展上述答案,一个好的做法是:
- 使用
VirtualAlloc
和读写访问分配内存。
- 用您的代码填写该区域
- 将该区域的保护更改为
VirtualProtect
执行读取访问权限
- 跳转to/call本区域入口点
所以它可能看起来像这样:
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
你不会为此使用 malloc
。无论如何,你为什么要在 C++ 程序中?但是,您也不要将 new
用于可执行内存。有 Windows 特定的 VirtualAlloc
function to reserve memory which you then mark as executable with the 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 代码。
我想分配一个可以在 Win32 上执行的缓冲区,但我在 visual studio 中有一个异常,因为 malloc 函数 returns 是一个不可执行的内存区域。我读到有一个 NX 标志可以禁用...我的目标是将字节码动态转换为 asm x86,同时牢记性能。
有人可以帮助我吗?
JS
如 documentation 所述 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.
其中之一是:
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.
等等 here
在编译时,链接器将通过将内存分配到数据段和代码段来组织程序的内存占用。 CPU 将确保程序计数器(硬 CPU 寄存器)的值保留在代码段内,否则 CPU 将因违反内存边界而抛出硬件异常。这通过确保您的程序只执行有效代码来提供一些安全性。 Malloc 用于分配数据内存。您的应用程序有一个堆,堆的大小由链接器确定并标记为数据内存。所以在运行时 malloc 只是从你的堆中获取一些虚拟内存,这将永远是数据。
我希望这可以帮助您更好地了解正在发生的事情,尽管这可能不足以让您到达需要的位置。也许您可以为运行时生成的代码预先分配一个 "code heap" 或内存池。您可能需要对链接器大惊小怪才能完成此操作,但我不知道任何细节。
扩展上述答案,一个好的做法是:
- 使用
VirtualAlloc
和读写访问分配内存。 - 用您的代码填写该区域
- 将该区域的保护更改为
VirtualProtect
执行读取访问权限 - 跳转to/call本区域入口点
所以它可能看起来像这样:
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
你不会为此使用 malloc
。无论如何,你为什么要在 C++ 程序中?但是,您也不要将 new
用于可执行内存。有 Windows 特定的 VirtualAlloc
function to reserve memory which you then mark as executable with the 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 代码。