Функции по управлению памятью
Задание 1. Определить значения системных переменных, отвечающих за границы областей виртуального адресного пространства (ВАП).
Указания к выполнению.
1. Узнаем значения 4 х системных переменных:
- MmHighestUserAddress – наибольший адрес пользовательского ВАП;
- MmSystemRangeStart – начальный адрес системного ВАП;
- MiSystemCacheEndExtra – конечный адрес области системного кэша или начальный адрес области таблицы страниц;
- MmNonPagedSystemStart – начальный адрес системных PTE.
2. Чтобы узнать значение переменной MmHighestUserAddress, введите следующую команду:
dd MmHighestUserAddress L1
Команда dd означает отображение 4 байтовых значений, а L1 указывает, что нужно отобразить одно такое значение.
Таким образом, переменная MmHighestUserAddress = 7FFE FFFF.
Заметьте, что верхняя граница пользовательского ВАП не достигает стартового адреса системного ВАП 8000 0000. Область 64 КБ (8000 0000 – 7FFE FFFF = 64 КБ) зарезервирована системой и недоступна пользовательским процессам.
3. Аналогичным образом можно посмотреть значения других системных переменных.
Задание 2. Создать программу, которая выделяет область памяти.
Указания к выполнению.
1. Создайте пустой проект с названием, например, MemoryAlloc, в Visual Studio и добавьте файл исходного кода main.cpp (см. лабораторную работу 2 "Процессы и потоки", задание 3).
Сохраните проект в папке c:\Programs\MemoryAlloc.
2. Вставьте в main.cpp следующий исходный код:
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
// Функция пытается записать по адресу lpvPointer 1 байт
VOID WriteCharToMemory(LPVOID lpvPointer, char Symbol)
{
__try
{
*(LPTSTR)lpvPointer = Symbol;
_tprintf ("First byte in memory area = '%c' (hex code = %x)\n\n", Symbol, Symbol);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
_tprintf ("Write to memory area failed\n\n");
}
}
// Вывод сообщения об ошибке
VOID ErrorExit(LPTSTR lpMsg)
{
_tprintf("Error! %s with error code of %ld\n", lpMsg, GetLastError ());
exit (0);
}
VOID _tmain(VOID)
{
LPVOID lpvReserved; // Адрес зарезервированной области памяти
LPVOID lpvCommit; // Адрес переданной области памяти
BOOL bSuccess; // Признак успешного освобождения памяти
SYSTEM_INFO sSysInfo; // Информация о системе
DWORD dwPageSize; // Размер страницы
GetSystemInfo(&sSysInfo); // Получаем информацию о системе
_tprintf ("This computer has page size %d\n\n", sSysInfo.dwPageSize);
dwPageSize = sSysInfo.dwPageSize; // Определяем размер страницы
// Точка останова
//_asm int 3
// Резервируем 1 страницу в памяти
lpvReserved = VirtualAlloc(
NULL, // Адрес размещения выбирает система
dwPageSize, // Размер области памяти
MEM_RESERVE, // Резервируем, не передаем
PAGE_READWRITE); // Память доступна для чтения-записи
if (lpvReserved == NULL )
ErrorExit("VirtualAlloc reserve failed");
_tprintf ("Reserve succeeded, address = %x\n\n", lpvReserved);
// Пытаемся записывать в память
WriteCharToMemory(lpvReserved, 'a');
// Точка останова
//_asm int 3
// Передаем зарезервированную страницу
lpvCommit = VirtualAlloc(
lpvReserved, // Адрес зарезервированной области
dwPageSize, // Размер области памяти
MEM_COMMIT, // Передаем память
PAGE_READWRITE); // Память доступна для чтения-записи
if (lpvCommit == NULL )
ErrorExit(TEXT("VirtualAlloc commit failed"));
_tprintf ("Commit succeeded, address = %x\n\n", lpvCommit);
// Пытаемся записывать в память
WriteCharToMemory(lpvCommit, 'a');
// Точка останова
//_asm int 3
// Освобождаем область памяти
bSuccess = VirtualFree(
lpvReserved, // Адрес освобождаемой области
0, // Параметр должен быть равен 0
MEM_RELEASE); // Освобождаем память
if (bSuccess)
_tprintf ("Release succeeded\n\n");
else
_tprintf ("Release failed\n\n");
system("pause");
}Данная программа сначала резервирует (но не передает) страницу в памяти, пытается записать в эту область данные, затем передает (commit) зарезервированную область и снова пытается записать в неё данные.
3. Установите свойства проекта (см. лабораторную работу 2 "Процессы и потоки", задание 3):
- конфигурацию Release;
- Библиотека времени выполнения – Многопоточная (/MT).
4. Проверьте работоспособность созданного приложения (клавиша F5):
Задание 3. В приложении расставить точки останова.
Указания к выполнению.
1. В созданном проекте уберите комментарии (//) перед операторами:
_asm int 3
Требуется убрать 3 комментария (т. е. установить 3 точки останова).
__asm – ключевое слово, которое указывает компилятору, что следующая команда будет командой встроенного Ассемблера.
int 3 – команда Ассемблера, обозначающая точку останова.
2. Запустите проект. Программа должна прерваться в первой точке останова, причем её выполнение можно продолжить, нажав кнопку Продолжить:
Задание 4. Исследовать функцию NtAllocateVirtualMemory.
Указания к выполнению.
1. Исполняемый файл созданного в предыдущем пункте приложения MemoryAlloc.exe после компиляции проекта должен находиться в следующей папке:
c:\Programs\MemoryAlloc\Release.
Скопируйте исполняемый файл на виртуальную машину.
2. Запустите исполняемый файл. Выполнение программы должно прерываться, а управление перейти к отладчику WinDbg.
3. Установите в отладчике точку останова на функции NtAllocateVirtualMemory. Для этого введите команду:
bp NtAllocateVirtualMemory
Если бы мы не вставили точку останова в само приложение, а до запуска приложения установили бы точку останова на функции NtAllocateVirtualMemory, то было бы трудно отследить запуск этой функции для выделения памяти в приложении, поскольку до этого момента функция NtAllocateVirtualMemory запускается несколько раз (например, для выделения памяти самому приложению).
4. Продолжите выполнение, нажав F5 в отладчике. Должна сработать точка останова на функции NtAllocateVirtualMemory и откроется исходный код этой функции.
Замечание. В отладчике WinDbg номер текущей строки исходного кода отображается в окне отладчика внизу в информационной строке (Ln – Line):
5. Выполните трассировку функции NtAllocateVirtualMemory, обращая внимание на следующие участки кода.
Строка 344 – формирование маски защиты ProtectionMask. В нашем приложении указано значение PAGE_READWRITE = 0*04 (см. MSDN1MSDN – Memory Protection Constants: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366786(v=vs.85).aspx), поэтому маска защиты также будет равна 0*04.
Строка 353 – определение текущего процесса CurrentProcess. Проверьте, что имя исполняемого файла процесса MemoryAlloc.exe (воспользуйтесь окном Locals отладчика, нажав Alt+3; проверьте значение поля ImageFileName переменной CurrentProcess).
Строка 380 – определение размера области памяти (CapturedRegionSize). В нашем случае эта переменная должна быть равна 0x1000 (4096 байт, 1 страница).
Строка 491 – определение величины выравнивания (гранулярности памяти) Alignment. Как указывалось в лекции 8 "Управление памятью", это значение составляет 64 КБ.
Строка 504 – округление размера области памяти CapturedRegionSize до целого числа страниц.
Строка 545 – определение числа страниц NumberOfPages.
Строка 647 – выделение памяти под новый дескриптор виртуального адреса (VAD) при помощи функции ExAllocatePoolWithTag.
Строки 660–668 – заполнение поля VadFlags в дескрипторе виртуального адреса.
Строка 853 – вычисление стартового адреса StartingAddress области памяти при помощи функции MiFindEmptyAddressRange. Эта функция определяет свободные места подходящего размера в памяти, просматривая АВЛ дерево дескрипторов виртуальных адресов.
Строка 867 – вычисление конечного адреса EndingAddress области памяти по простой формуле с округлением до последнего адреса последней страницы, входящей в область.
Строки 899 и 900 – вычисление полей StartingVpn и EndingVpn дескриптора VAD (структура MMVAD) – начального и конечного номеров виртуальных страниц.
Строка 930 – вставка сформированного дескриптора виртуального адреса в АВЛ дерево и его балансировка при помощи функции MiInsertVad.
Строка 1049 – вычисление реального размера зарезервированной области памяти (CapturedRegionSize).
Строка 1050 – увеличение размера виртуальной памяти процесса на величину зарезервированной области (Process->VirtualSize).
Строка 1052 – изменение при необходимости пикового размера виртуальной памяти процесса (Process->PeakVirtualSize).
Строки 1078 и 1079 – возвращение указателя на область памяти (BaseAddress) и реального размера области памяти (RegionSize).



