Функции по управлению памятью
Задание 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).