I'm writing a function that will allow the user to allocate memory within 2GB +/- of a specified address. I'm querying the memory to find a free page, and allocating there. This is for x64 trampoline hooking, since I'm using a relative jmp instruction.
My issue is that NtQueryVirtualMemory fails with a STATUS_ACCESS_VIOLATION error, thus always returns 0. I'm confused on why this is happening, because min(the lowest possible address) appears to be free when I check in 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;
    }
}
at first several general notes (not about erroes)
very strange from my look mix NtQueryVirtualMemory with VirtualAlloc. exist sense or use 
NtQueryVirtualMemory with NtAllocateVirtualMemoryor
VirtualQuery with VirtualAllocthe 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:
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;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;
}
 RbMm
 RbMmUser contributions licensed under CC BY-SA 3.0