at first several general notes (not about erroes)
very strange from my look mix NtQueryVirtualMemory
with VirtualAlloc
. exist sense or use
NtQueryVirtualMemory
with NtAllocateVirtualMemory
or
VirtualQuery
with VirtualAlloc
the NtQueryVirtualMemory
have no any extra functionality compare to VirtualQueryEx
(the NtAllocateVirtualMemory
have extra functionality compare to VirtualAllocEx
via ZeroBits
parameter)
then if already use NtQueryVirtualMemory
not need GetProcAddress
- you can use static linking with ntdll.lib or ntdllp.lib from wdk - this api was, exist and will be exported from ntdll.dll like VirtualQuery
exported from kernel32.dll and you link with kernel32.lib
and if you anyway want use GetProcAddress
- exist sense do this not every time when Allocate2GBRange
is called, but once. and main check result of call - may be GetProcAddress
return 0 ? if you sure that GetProcAddress
never fail - you sure that NtQueryVirtualMemory
always exported from ntdll.dll - so use static linking with ntdll[p].lib
then INVALID_HANDLE_VALUE
in place ProcessHandle
look very not native, despite correct. better use NtCurrentProcess()
macro here or GetCurrentProcess()
. but anyway, because you use kernel32 api - no any reason use NtQueryVirtualMemory
instead VirtualQuery
here
you not need zero init mbi
before calls - this is out only parameter, and because initially always min < max
better use do {} while (min < max)
loop instead while(min < max) {}
now about critical errors in your code:
- use
mbi.AllocationBase
- when mbi.State == MEM_FREE
- in this
case mbi.AllocationBase == 0
- so you tell VirtualAlloc
allocate in any free space.
min += mbi.RegionSize;
- the mbi.RegionSize
is from
mbi.BaseAddress
- so add it to min
incorrect - you need use min
= (UINT_PTR)mbi.BaseAddress + mbi.RegionSize;
- then in call
VirtualAlloc
(when lpAddress != 0) you must use
MEM_COMMIT|MEM_RESERVE
instead MEM_COMMIT
only.
and about address which pass to VirtualAlloc
- pass mbi.AllocationBase
(simply 0) incorrect. but pass mbi.BaseAddress
in case we found mbi.State == MEM_FREE
region also not correct. why ? from VirtualAlloc
If the memory is being reserved, the specified address is rounded down
to the nearest multiple of the allocation granularity.
this mean that VirtualAlloc
really try allocate memory not from passed mbi.BaseAddress
but from mbi.BaseAddress & ~(dwAllocationGranularity - 1)
- smaller address. but this address can be already busy, as result you got STATUS_CONFLICTING_ADDRESSES
(ERROR_INVALID_ADDRESS
win32 error) status.
for concrete example - let you have
[00007FF787650000, 00007FF787673000) busy memory
[00007FF787673000, ****************) free memory
and initially your min
will be in [00007FF787650000, 00007FF787673000)
busy memory region - as result you got mbi.BaseAddress == 0x00007FF787650000
and mbi.RegionSize == 0x23000
, because region is busy - you will try next region at mbi.BaseAddress + mbi.RegionSize;
- so at 00007FF787673000
address. you got mbi.State == MEM_FREE
for it, but if you try call VirtualAlloc
with 00007FF787673000
address - it rounded down this address to the 00007FF787670000
because now allocation granularity is 0x10000
. but 00007FF787670000
is belong to the [00007FF787650000, 00007FF787673000)
busy memory region - al result VirtualAlloc
fail with STATUS_CONFLICTING_ADDRESSES
(ERROR_INVALID_ADDRESS
).
so you need use (BaseAddress + (AllocationGranularity-1)) & ~(AllocationGranularity-1)
really - address rounded up to the nearest multiple of the allocation granularity.
all code can look like:
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;
}