Virtual memory allocations on Windows are made with a granularity of 64 kilobytes, the value of SYSTEM_INFO.dwAllocationGranularity. But virtual memory pages are 4096 bytes, the value of SYSTEM_INFO.dwPageSize.
When you allocate virtual memory with VirtualAlloc, you'll always get a chunk back whose BaseAddress equals AllocationBase. But if you then alter the page protection of one or more of the pages within this chunk then you can observe this chunk being subdivided with a different BaseAddress. Best shown with a sample program, run this on MSVC++:
#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
#include <conio.h>
void showmem(void* mem) {
MEMORY_BASIC_INFORMATION info = {};
VirtualQuery(mem, &info, sizeof info);
printf("Alloc = %p, base = %p, size = %d, protect = %d\n",
info.AllocationBase, info.BaseAddress, info.RegionSize, info.Protect);
}
int main() {
BYTE* mem = (BYTE*)VirtualAlloc(0, 65536, MEM_COMMIT, PAGE_READWRITE);
printf("%s", "Initial allocation:\n");
showmem(mem);
DWORD oldprotect;
BOOL ok = VirtualProtect(mem + 4096, 4096, PAGE_NOACCESS, &oldprotect);
printf("%s", "\nAfter protection changes:\n");
showmem(mem);
showmem(mem + 4096);
showmem(mem + 4096 + 4096);
_getch();
return 0;
}
Sample output of this program:
Initial allocation:
Alloc = 00ED0000, base = 00ED0000, size = 65536, protect = 4
After protection changes:
Alloc = 00ED0000, base = 00ED0000, size = 4096, protect = 4
Alloc = 00ED0000, base = 00ED1000, size = 4096, protect = 1
Alloc = 00ED0000, base = 00ED2000, size = 57344, protect = 4
And note how the VirtualProtect() call required the original chunk to be split in 3 regions with different BaseAddress but the same AllocationBase.