Введение во внутреннее устройство Windows

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

Показывать лекцию целиком

Задание 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 (см. MSDNMSDN – 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).

    Задание 5. Просмотреть содержимое памяти, изменяемое приложением.

    Указания к выполнению.

    1. Удалите точку останова с функции NtAllocateVirtualMemory: в меню Edit отладчика выберите Breakpoints… и нажмите кнопку Remove All.

    2. Возобновите выполнение виртуальной машины, пройдя все точки останова приложение MemoryAlloc.exe (несколько раз нажмите в отладчике F5).

    3. Запустите приложение MemoryAlloc.exe ещё раз.

    4. Когда сработает первая точка останова, продолжите в отладчике выполнение (нажмите F5 один раз).

    5. На второй точке останова перейдите в окно виртуальной машины и посмотрите, по какому адресу оказалась зарезервирована область памяти:

    6. Перейдите в отладчик и просмотрите содержимое памяти по этому адресу, введя команду:

    db 340000 L1
    

    Команда db отображает однобайтовое значение, параметр L1 указывает, что необходимо показать одно такое значение.

    Как и подсказывает сообщение нашего приложения ("Write to memory area failed"), попытка записи в зарезервированную область память оказалась неудачна, значение в данной ячейке не определено:

    Чтобы работать с памятью, её нужно передать (commit).

    7. Нажмите в отладчике F5 – программа дойдет до третьей точки останова (после передачи памяти):

    Снова просмотрите содержимое памяти по адресу 340000. Сейчас в этой ячейке должен оказаться символ ‘a’ (с шестнадцатеричным кодом 61):

    Задание 6. Исследовать дескрипторы виртуальных адресов VAD.

    Указания к выполнению.

    1. Запустите приложение MemoryAlloc.exe. Когда сработает первая точка останова и управление перейдет к отладчику, требуется узнать адрес процесса MemoryAlloc.exe в памяти. Для этого введите команду:

    !process 0 0

    и найдите процесс с именем MemoryAlloc.exe:

    В данном примере адрес процесса в памяти (адрес объекта EPROCESS): 81F9E320.

    2. Определите адрес корня АВЛ дерева дескрипторов виртуальных адресов VadRoot.

    Для этого введите команду:

    !process 81F9E320 1
    

    Адрес корня VadRoot равен 82251498.

    3. Выведите на экран дерево дескрипторов виртуальных адресов процесса MemoryAlloc.exe.

    Введите команду:

    !vad 82251498
    

    На экран выводится таблица со следующими столбцами:

  • VAD – адрес дескриптора виртуального адреса – узла дерева;
  • level – уровень в дереве, на котором находится узел;
  • start – номер начальной виртуальной страницы (VPN) в блоке, за который отвечает данный VAD;
  • end – номер последней виртуальной страницы в блоке;
  • commit – счетчик переданных страниц в блоке;
  • тип памяти блока;
  • параметры защиты блока;
  • файлы, отображаемые на блок памяти.
  • После таблицы показывается общее количество узлов в дереве, средний уровень дерева и его максимальная глубина.

    4. Продолжите выполнение процесса MemoryAlloc.exe, нажав F5 в отладчике. Должна сработать вторая точка останова.

    Снова выведите на экран дерево дескрипторов виртуальных адресов:

    Вы должны заметить, что общее количество дескрипторов увеличилось на единицу, т. е. после резервирования области памяти в приложении в дереве появился новый дескриптор по адресу 81F454E8.

    Обратите внимание на номер стартовой виртуальной страницы для этого дескриптора: 340. Сравните это значение с адресом зарезервированной области памяти в приложении:

    Как и отмечалось в лекции 8 "Управление памятью" номер виртуальной страницы в VAD получается путем отбрасыванием младших 12 бит (или 3 шестнадцатеричных цифр).

    5. Продолжите выполнение приложения (F5). Сработает третья точка останова.

    Снова выведите дерево дескрипторов виртуальных адресов: на экран:

    Обратите внимание, что после передачи (commit) зарезервированной области памяти счетчик переданных страниц нового дескриптора увеличился на единицу.

    Задания для самостоятельного выполнения

    Задание 1. Исследуйте действие различных значений параметров защиты памяти функции VirtualAlloc на примере приложения MemoryAlloc.

    Указания к выполнению.

    1. На странице MSDN "Memory Protection Constants" приведено описание значений параметра flProtect функции VirtualAlloc. Попробуйте запустить приложение MemoryAlloc с разными значениями данного параметра и оцените результат.

    Желательно также исследовать влияние параметра защиты на дескрипторы виртуальных адресов VAD.

    Задание 2. Продолжить исследование функции NtAllocateVirtualMemory.

    Указания к выполнению.

    1. В основной части лабораторной работы (задание 4) осуществлялась трассировка функции NtAllocateVirtualMemory при резервировании области памяти в приложении MemoryAlloc.

    Выполните трассировку данной функции при передаче (commit) области памяти (поставьте точку останова на функции NtAllocateVirtualMemory после срабатывания второй точки останова в программе MemoryAlloc.

    Задание 3. Исследовать процесс трансляции виртуальных адресов.

    Указания к выполнению.

    1. Выполните процесс трансляции виртуального адреса из диапазона адресов области памяти, выделенной в приложении MemoryAlloc. Воспользуйтесь описанием трансляции из лекции 8 "Управление памятью" и примером из [5, стр. 456].

    Задание 4. Исследовать утилиту для работы с виртуальной памятью VMMap от Sysinternals.

    Указания к выполнению.

    1. Скачайте утилиту VMMap по адресу:

    http://technet.microsoft.com/en-us/sysinternals/dd535533

    2. Исследуйте возможности утилиты на примере приложения MemoryAlloc.

    Вернуться к учебному плану