gpt4 book ai didi

c - 在 2GB 范围内分配内存

转载 作者:可可西里 更新时间:2023-11-01 09:32:10 25 4
gpt4 key购买 nike

我正在编写一个函数,允许用户在指定地址的 2GB +/- 范围内分配内存。我正在查询内存以找到一个空闲页面,并在那里分配。这是x64 trampoline hooking ,因为我使用的是相对 jmp 指令。

我的问题是 NtQueryVirtualMemorySTATUS_ACCESS_VIOLATION 错误而失败,因此总是返回 0。我很困惑为什么会发生这种情况,因为 min(可能的最低地址)在我 checkin Process Explorer 时似乎是免费的。

LPVOID Allocate2GBRange(UINT_PTR address, SIZE_T dwSize)
{
NtQueryVirtualMemory_t NtQueryVirtualMemory = (NtQueryVirtualMemory_t)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryVirtualMemory");

UINT_PTR min, max;
min = address >= 0x80000000 ? address - 0x80000000 : 0;
max = address < (UINTPTR_MAX - 0x80000000) ? address + 0x80000000 : UINTPTR_MAX;

MEMORY_BASIC_INFORMATION mbi = { 0 };
while (min < max)
{
NTSTATUS a = NtQueryVirtualMemory(INVALID_HANDLE_VALUE, min, MemoryBasicInformation, &mbi, sizeof(MEMORY_BASIC_INFORMATION), NULL);
if (a)
return 0;

if (mbi.State == MEM_FREE)
{
LPVOID addr = VirtualAlloc(mbi.AllocationBase, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (addr)
return addr;
}

min += mbi.RegionSize;
}
}

最佳答案

首先是几个一般性的注释(不是关于错误的)

从我的搭配来看很奇怪NtQueryVirtualMemoryVirtualAlloc .存在意义或使用

  • NtQueryVirtualMemoryNtAllocateVirtualMemory

  • VirtualQueryVirtualAlloc

NtQueryVirtualMemoryVirtualQueryEx 相比没有任何额外功能(与 NtAllocateVirtualMemory 相比,VirtualAllocEx 通过 ZeroBits 参数具有额外的功能)

那么如果已经使用 NtQueryVirtualMemory 不需要GetProcAddress - 您可以使用来自 wdkntdll.libntdllp.lib 的静态链接 - 这个 api 曾经存在,并将从 ntdll.dll 喜欢VirtualQuerykernel32.dll 导出,然后链接到 kernel32.lib如果你想使用 GetProcAddress - 存在意义不是每次都这样做 Allocate2GBRange被调用,但是一次。和调用的主要检查结果 - 可能是 GetProcAddress返回 0 ?如果你确定 GetProcAddress永远不会失败 - 你确定 NtQueryVirtualMemory总是从 ntdll.dll 导出 - 所以使用 ntdll[p].lib 的静态链接

然后 INVALID_HANDLE_VALUE就位ProcessHandle尽管正确,但看起来很不自然。更好用NtCurrentProcess()此处宏或 GetCurrentProcess() .但无论如何,因为你使用 kernel32 api - 没有任何理由使用 NtQueryVirtualMemory相反 VirtualQuery这里

你不需要零初始化 mbi在调用之前 - 这只是参数,因为最初总是 min < max更好用do {} while (min < max)改为循环 while(min < max) {}


现在关于代码中的严重错误:

  • 使用mbi.AllocationBase - 当mbi.State == MEM_FREE - 在这个案例mbi.AllocationBase == 0 - 所以你告诉 VirtualAlloc 任何可用空间中分配。
  • min += mbi.RegionSize; - mbi.RegionSize来自 mbi.BaseAddress - 所以将其添加到 min不正确 - 你需要使用 min
    = (UINT_PTR)mbi.BaseAddress + mbi.RegionSize;
  • 然后打电话VirtualAlloc (当 lpAddress != 0 时)您必须使用 MEM_COMMIT|MEM_RESERVE相反 MEM_COMMIT仅。

以及关于传递给 VirtualAlloc 的地址- 通过 mbi.AllocationBase (只是 0)不正确。但通过 mbi.BaseAddress万一我们找到mbi.State == MEM_FREE区域也不正确。为什么 ?来自 VirtualAlloc

If the memory is being reserved, the specified address is rounded down to the nearest multiple of the allocation granularity.

这意味着VirtualAlloc真的尝试分配内存而不是通过 mbi.BaseAddress但来自 mbi.BaseAddress & ~(dwAllocationGranularity - 1) - 较小的地址。但是这个地址可能已经很忙,结果你得到了STATUS_CONFLICTING_ADDRESSES (ERROR_INVALID_ADDRESS win32 错误)状态。

举个具体的例子——让你有

[00007FF787650000, 00007FF787673000) busy memory
[00007FF787673000, ****************) free memory

最初是你的 min将在[00007FF787650000, 00007FF787673000)繁忙的内存区域 - 结果你得到了 mbi.BaseAddress == 0x00007FF787650000mbi.RegionSize == 0x23000 ,因为区域繁忙 - 您将在 mbi.BaseAddress + mbi.RegionSize; 尝试下一个区域- 所以在 00007FF787673000地址。你有mbi.State == MEM_FREE为此,但如果您尝试调用 VirtualAlloc00007FF787673000地址 - 它将此地址向下舍入为 00007FF787670000因为现在分配粒度是0x10000 .但是00007FF787670000属于[00007FF787650000, 00007FF787673000)繁忙的内存区域 - 结果 VirtualAlloc失败 STATUS_CONFLICTING_ADDRESSES (ERROR_INVALID_ADDRESS)。

所以你需要使用(BaseAddress + (AllocationGranularity-1)) & ~(AllocationGranularity-1)真的 - 地址向上舍入到最接近分配粒度的倍数。

所有代码都可以是这样的:

PVOID Allocate2GBRange(UINT_PTR address, SIZE_T dwSize)
{
static ULONG dwAllocationGranularity;

if (!dwAllocationGranularity)
{
SYSTEM_INFO si;
GetSystemInfo(&si);
dwAllocationGranularity = si.dwAllocationGranularity;
}

UINT_PTR min, max, addr, add = dwAllocationGranularity - 1, mask = ~add;

min = address >= 0x80000000 ? (address - 0x80000000 + add) & mask : 0;
max = address < (UINTPTR_MAX - 0x80000000) ? (address + 0x80000000) & mask : UINTPTR_MAX;

::MEMORY_BASIC_INFORMATION mbi;
do
{
if (!VirtualQuery((void*)min, &mbi, sizeof(mbi))) return NULL;

min = (UINT_PTR)mbi.BaseAddress + mbi.RegionSize;

if (mbi.State == MEM_FREE)
{
addr = ((UINT_PTR)mbi.BaseAddress + add) & mask;

if (addr < min && dwSize <= (min - addr))
{
if (addr = (UINT_PTR)VirtualAlloc((PVOID)addr, dwSize, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE))
return (PVOID)addr;
}
}


} while (min < max);

return NULL;
}

关于c - 在 2GB 范围内分配内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54729401/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com