Функция VMQuery
Начиная изучать архитектуру памяти в Windows, я пользовался функцией VirtualQuery как "поводырем". Если Вы читали первое издание моей книги, то заметите, что программа VMMap была гораздо проще ее нынешней версии, представленной в следующем разделе. Прежняя была построена на очень простом цикле, из которого периодически вызывалась функция VirtualQuery, и для каждого вызова я формировал одпу строку, содержавшую элементы структуры MEMORY_BASIC_INFORMATION. Изучая полученные дампы и сверяясь с документацией из SDK (в то время весьма неудачной), я пытался разобраться в архитектуре подсистемы управления памятью. Что ж, с тех пор я многому научился и теперь знаю, что функция VirtualQuery и структура MEMORY_BASIC_INFORMATION не дают полной картины.
Проблема в том, что в MEMORY_BASIC_INFORMATION возвращается отнюдь не вся информация, имеющаяся в распоряжении системы. Если Вам нужны простейшие данные о состоянии памяти по конкретному адресу, VirtualQuery действительно незаменима. Она отлично работает, если Вас интересует, передана ли по этому адресу физическая память и доступен ли он для операций чтения или записи. Но попробуйте с её помощью узнать общий размер зарезервированного региона и количество блоков в нем или выяснить, не содержит ли этот регион стек потока, — ничего не выйдет.
Чтобы получать более полную информацию о памяти, я создал собственную функцию и назвал ее VMQuery.
BOOL VMQuery( HANDLE hProcess, PVOID pvAddress, PVMQUERY pVMQ);
По аналогии с VirtualQueryEx она принимает в hProcess описатель процесса, в pvAddress - адрес памяти, а в pVMQ — указатель на структуру, заполняемую самой функцией. Структура VMQUERY (тоже определенная мной) представляет собой вот что.
typedef struct
{
// информация о регионе
PVOID pvRgnBaseAddress;
DWORD dwRgnProtection;
// PAGE_*
SIZE_T RgnSize;
DWORD dwRgnStorage;
// MEM_* Free. Irnage, Mapped, Private
DWORD dwRgnBlocks;
DWORD dwRgnGuardBlks; // если > 0, регион содержит стек потока
BOOL tRqnlsAStack; // TRUE, если регион содержит стек потока
// информация о блоке
PVOID pvBlkBaseAddress;
DWORD dwBlkProtection;
// PAGE_*
SIZE_T BlkSize;
DWORD dwBlkStorage;
// MEM_* Free, Reserve, Image, Mapped, Private
} VMQUERY, *PVMQUERY;
С первого взгляда заметно, что моя структура VMQUERY содержит куда больше информации, чем MEMORY_BASIC_INFORMATION. Она разбита (условно, конечно) на две части: в одной — информация и регионе, в другой — информация о блоке (адрес которого указан в параметре pvAddress). Элементы этой структуры описываются в следующей таблице.
Элемент | Описание |
pvRgnBaseAddress | Идентифицирует базовый адрес региона виртуального адресного про странства, включающего адрес, указанный в параметре pvAddress |
dwRgnProtection | Сообщает атрибут защиты, присвоенный региону при его резервиро вании. |
RgnSize | Указывает размер (в байтах) зарезернириванного о региона. |
dwRgnStorage | Идентифицирует тип физической памяти, используемой группой бло ков данного peгиона: MEM_FREE, MEM_IMAGE, MEM_MAPPED или MEM PRIVATE. Поскольку Windows 98 не различает типы памяти, в этой операционной системе данный элемент содержит либо MEM_FREE, либо MEM_PRIVATE |
dwRgnBlocks | Содержит значение — число блоков в указанном регионе |
dwRgnGuardBlks | Указывает число блоков с установленным флагом атрибутов защиты PAGE GUARD. Обычно это значение либо 0, либо 1. Если оно равно 1, то регион скорее всего зарезервирован под стек потока В Windows 98 этот элемент всегда равен 0 |
fRgnIsAStack | Сообщает, есть ли в данном регионе стек потока Результат определяется на основе взвешенной оценки, так как невозможно дать стопроцентной гарантии тому, что в регионе содержится стек. |
pvBlkBaseAddress | Идентифицирует базовый адрес блока, включающего адрес, указанный в параметре pvAddress, |
dwBlkProtection | Идентифицирует атрибут защиты блока, включающего адрес, указанный в параметре pvAddress. |
BlkSize | Содержит значение — размер блока (в байтах), включающего адрес, указанный в параметре pvAddress. |
dwBlkStorage | Идентифицирует содержимое блока, включающего адрес, указанный в параметре pvAddress. Принимает одно из значений: MEM_FREE, MEM_RESERVE, MEM_IMAGE, MEM_MAPPED или MEM_PRIVATE. В Windows 98 этот элемент никогда не содержит значения MEM_IMAGE и MEM_MAPPED |
Чтобы получить всю эту информацию, VMQuery, естественно, приходится выполнять гораздо болыше операций (в том числе многократно вызывать VirtualQueryEx), а потому она работает значительно медленнее VirtualQueryEx. Так что Вы должны все тщательно взвесить, прежде чем остановить свой выбор на одной из этих функций. Если Вам не нужна дополнительная информация, возвращаемая VMQuery, используйте VirtualQuery или VirtualQueryEx.
Листинг файла VMQuery.cpp (рис. 14-3) показывает, как я получаю и обрабатываю данные, необходимые для инициализации элементов структуры VMQUERY. (Файлы VMQuery.cpp и VMQueryh содержатся в каталоге 14-VMMap на компакт-диске, прилагаемом к книге.) Чтобы не объяснять подробности обработки данных "на пальцах", я снабдил тексты программ массой комментариев, вольно разбросанных по всему коду.
VMQuery