сначала несколько общих замечаний (не об ошибках)
очень странно с моей стороны смешивать NtQueryVirtualMemory
с VirtualAlloc
.существует смысл или использовать
NtQueryVirtualMemory
с NtAllocateVirtualMemory
или
VirtualQuery
с VirtualAlloc
NtQueryVirtualMemory
не имеет никакой дополнительной функциональности по сравнению с VirtualQueryEx
(NtAllocateVirtualMemory
имеет дополнительную функциональность по сравнению с VirtualAllocEx
с помощью параметра ZeroBits
)
, если уже используется NtQueryVirtualMemory
не нужно GetProcAddress
- вы можете использовать статическое связывание с ntdll.lib или ntdllp.lib с wdk - этот API был, существуют и будут экспортированы из ntdll.dll как VirtualQuery
экспортированы из kernel32.dll , и вы свяжетесь с kernel32.lib , и если вы все равно захотите использоватьGetProcAddress
- существует смысл, делайте это не каждый раз, когда вызывается Allocate2GBRange
, а один раз.и основной результат проверки звонка - может быть GetProcAddress
вернуть 0?если вы уверены, что GetProcAddress
никогда не потерпит неудачу - вы уверены, что NtQueryVirtualMemory
всегда экспортируется из ntdll.dll - поэтому используйте статическую связь с ntdll [p] .lib
тогда INVALID_HANDLE_VALUE
на месте ProcessHandle
выглядят очень не родными, несмотря на правильность.лучше использовать макрос NtCurrentProcess()
здесь или GetCurrentProcess()
.но в любом случае, потому что вы используете api kernel32 - без всякой причины используйте NtQueryVirtualMemory
вместо VirtualQuery
здесь
вам не нужно ноль init 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
Если память резервируется, указанный адрес округляется до ближайшего кратного гранулярности выделения.
это означает, что 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 == 0x00007FF787650000
и mbi.RegionSize == 0x23000
, потому что область занята - вы попытаетесь использовать следующий регион по mbi.BaseAddress + mbi.RegionSize;
- так по адресу 00007FF787673000
.вы получили mbi.State == MEM_FREE
за это, но если вы попытаетесь позвонить VirtualAlloc
с 00007FF787673000
адресом - он округлил этот адрес до 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;
}