如何分配一个可执行的内存缓冲区?
我想分配一个缓冲区,我可以在Win32上执行,但我有一个在Visual Studio中的异常cuz malloc函数返回一个不可执行的内存区域。我读到有一个NX标志禁用...我的目标是将字节码转换为asm x86,并牢记性能。如何分配一个可执行的内存缓冲区?
有人可以帮助我吗?
JS
对此,您不使用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代码。
在C++程序中使用malloc的原因是从堆中分配动态内存。非常好,很正常。 –
@KyleSweet:在C++中,您可以使用'new'(或者像'std :: allocator'这样的“普通”或者新的位置)来从免费商店(而不是堆)动态分配。你可以在技术上使用'malloc',但是你有效地写C而不是(惯用的)C++。对于OP的问题,'VirtualAlloc'和'malloc'之间的区别也很重要。 –
我只是回答你提出的问题。如果你想要的只是堆中的一些快速而脏的内存,不要害怕使用malloc! –
如documentation为VirtualAlloc
flProtect [IN]
将要分配给的页区域内的存储器保护说明。如果页面正在提交,您可以指定任何一个内存保护常量。
其中之一是:
PAGE_EXECUTE 0x10的 允许执行访问的页面提交区域。尝试写入已提交的区域会导致访问冲突。 CreateFileMapping函数不支持此标志。
PAGE_EXECUTE_READ 0x20 启用对页面的已提交区域的执行或只读访问。尝试写入已提交的区域会导致访问冲突。 Windows Server 2003和Windows XP:在使用SP2的Windows XP和使用SP1的Windows Server 2003之前,CreateFileMapping函数不支持此属性。
PAGE_EXECUTE_READWRITE 0x40 允许执行,只读或读/写访问页面的承诺区域。 Windows Server 2003和Windows XP:在使用SP2的Windows XP和使用SP1的Windows Server 2003之前,CreateFileMapping函数不支持此属性。
等从here
它看起来像属于*页*的内存。这在这里适合吗? –
是的。如果您需要控制模式,则需要分配_pages_。 – bmargulies
在编译时,链接器将分配的内存为数据段和代码段组织你的程序的内存占用。 CPU将确保程序计数器(硬件CPU寄存器)的值保持在代码段内,否则CPU将因违反内存边界而引发硬件异常。这通过确保您的程序仅执行有效代码来提供一些安全性。 Malloc用于分配数据存储器。您的应用程序有一个堆,堆的大小由链接器确定并标记为数据存储器。所以在运行时,malloc只是从堆中获取一些永远是数据的虚拟内存。
我希望这可以帮助你更好地理解正在发生的事情,尽管它可能不足以让你到达需要的地方。也许你可以为运行时生成的代码预先分配一个“代码堆”或内存池。你可能需要与链接器做文章来完成这个,但我不知道任何细节。
*动态分配内存页面上的读/写/执行权限与链接器无关。如果你想要一些*静态分配的*写入+执行页面,你可以通过链接器脚本或者GNU C'__attribute__'来实现。 –
另外,不,通过检查程序计数器(x86-64上的RIP)是否保持在特定范围内,内存保护不起作用。这就是分段如何工作(使用基准/限制),但x86-64甚至不支持64位模式下的分段限制。内存保护以页为单位,页表中的位(由OS设置)。有关图表,请参阅https://stackoverflow.com/questions/46509152/why-in-64bit-the-virtual-address-are-4-bits-short-48bit-long-compared-with-the-。 –
扩展上面的答案,一个好的做法是:
- 分配内存
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
根据规格如果最后一个参数为NULL,VirtualProtect将失败。使用指向(虚拟)输出变量的指针是强制性的。 – user3042599
@ user3042599:Thx。修复。 – zx485
你可能想看看[asmJit](https://github.com/asmjit/asmjit)这是一个免费代码,它为编写JIT的程序集/内存管理部分完成所有繁重的工作,包括设置内存保护位,以便您可以执行您的代码。 – BeeOnRope