Вятский государственный гуманитарный университет
Опубликован: 27.05.2013 | Доступ: свободный | Студентов: 2741 / 614 | Длительность: 09:18:00
Самостоятельная работа 4:

Функции по управлению памятью

< Лекция 8 || Самостоятельная работа 4: 12 || Лекция 9 >
Аннотация: Цель работы: исследовать структуры данных и функции WRK, используемые при управлении памятью.

Задание 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).

< Лекция 8 || Самостоятельная работа 4: 12 || Лекция 9 >
Егор Исаев
Егор Исаев
Россия, Г.Починок
Елена Пискунова
Елена Пискунова
Россия