Статьи

Упражнение в переводе виртуальной памяти в физическую

В этой статье мы рассмотрим трансляцию виртуальных адресов на примере реального мира. Если хотите, приведенные здесь инструкции должны быть достаточно подробными, чтобы вы могли самостоятельно выполнить этот эксперимент с отладчиком ядра (достаточно локального сеанса отладчика ядра или даже LiveKD).

Давайте начнем с основ. Чтобы лучше понять, как работает перевод памяти в системах x86-64 и x86, вы должны прочитать сообщение в блоге Люка Хатчинсона . Кроме того, Windows Internals (6-е издание) содержит еще более подробное описание преобразования адресов в главе «Диспетчер памяти» (том 2).

Теперь давайте продолжим и выполним упражнение, чтобы увидеть, как на самом деле работает перевод памяти. (Это адаптированная версия реального упражнения, которое мы даем в нашем курсе Windows Internals .)

Начните с того, с какой комбинацией аппаратного и программного обеспечения вы работаете. В частности, определите, используете ли вы x86 без PAE (очень маловероятно), x86 с PAE или x64. Если вы действительно параноик и хотите убедиться, что используете PAE, посмотрите этот отличный ответ на SuperUser .

Затем запустите калькулятор Windows (calc.exe) и отладчик ядра. Найдите процесс calc.exe в отладчике ядра с помощью команды ! Process 0 0 calc.exe . Запомните , ПЭБ виртуальный адрес — это виртуальный адрес , который мы будем переводить. Также запишите физический адрес DirBase — это отправная точка для процесса преобразования адресов.

С этого момента процесс зависит от используемого аппаратного и программного обеспечения (x86 без PAE, x86 с PAE или x64). Как правило, вам необходимо знать структуру записей таблицы страниц на всех уровнях таблиц страниц, а также знать структуру виртуального адреса — как виртуальный адрес разбивается на индексы таблицы страниц. Вооружившись этими знаниями, вы можете переводить адреса так же, как это делает процессор!

В 64-разрядных системах 64-разрядный виртуальный адрес разделен на пять подразделов следующим образом:

Селектор четвертого уровня карты страниц

Селектор указателя каталога страницы

Селектор таблицы страниц

Селектор записей таблицы страниц

Байт на странице

9 бит

9 бит

9 бит

9 бит

12 бит

Запись таблицы страниц в системах x64 содержит номер кадра страницы в битах с 12 по 39 (где младший бит — это бит 0, а самый старший бит — бит 63). Размер страницы для маленьких страниц составляет четыре КБ. Каждая запись в таблице страниц имеет длину восемь байтов.

В системах x86 с PAE 32-битный виртуальный адрес делится на четыре подраздела:

Индекс указателя каталога страницы

Индекс каталога страницы

Индекс таблицы страниц

Байт на странице

2 бита

9 бит

9 бит

12 бит

Запись таблицы страниц в системах x86 с PAE содержит номер кадра страницы в битах от 12 до 31. Размер страницы для маленьких страниц составляет четыре КБ. Каждая запись в таблице страниц имеет длину восемь байтов.

В системах x86 без PAE 32-битный виртуальный адрес делится на три подраздела:

Индекс каталога страницы

Индекс таблицы страниц

Байт на странице

10 бит

10 бит

12 бит

Запись таблицы страниц в системах x86 без PAE содержит номер кадра страницы в битах от 12 до 31. Размер страницы для маленьких страниц составляет четыре КБ. Каждая запись в таблице страниц имеет длину четыре байта.

Чтобы просмотреть содержимое физической памяти в отладчике, используйте команды ! Dd и ! Dq . Их параметры идентичны командам dd и dq , но они работают непосредственно с физической памятью.

Мы рассмотрим пример на реальной системе x64. Виртуальный адрес, который мы хотели бы перевести, — 7fffffdf000, а база каталогов (регистр CR3) указывает на физический адрес 26994000.

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

Селектор четвертого уровня карты страниц

Селектор указателя каталога страницы

Селектор таблицы страниц

Селектор записей таблицы страниц

Байт на странице

9 бит

9 бит

9 бит

9 бит

12 бит

00f

1 и далее

1 и далее

1DF

000

Далее мы начинаем перевод с карты-селектора четвертого уровня, который равен 00f. База каталогов находится по физическому адресу 26994000, и нам нужно сместить его (напомним, что каждая запись в каталоге страниц составляет восемь байтов):

lkd> !dq 26994000+f*8 L1
#26994078 00800000`05b48867

Теперь у нас есть запись таблицы страниц первого уровня. Биты с 12 по 39 соответствуют номеру фрейма страницы, который говорит нам, где находится таблица страниц следующего уровня. Номер фрейма страницы в этом случае 5b48. Номер фрейма страницы необходимо умножить на размер страницы (4 КБ = 0x1000), чтобы получить фактический физический адрес таблицы страниц следующего уровня.

Индекс в таблице страниц следующего уровня равен 1ff, поэтому нам действительно нужно значение по физическому адресу 5b48000 + 1ff * 8, где мы находим следующую запись таблицы страниц для расшифровки:

lkd> !dq 5b48000 + 1ff*8 L1
# 5b48ff8 00900000`05c49867

Аналогичным образом мы можем определить, что номер фрейма страницы в этом случае равен 5c49. Индекс в таблице страниц следующего уровня снова равен 1ff, поэтому мы переходим к записи таблицы страниц следующего уровня:

lkd> !dq 5c49000+1ff*8 L1
# 5c49ff8 00a00000`05bca867

Мы почти закончили. Индекс таблицы страниц следующего уровня — 1df, а номер фрейма страницы в последнем выводе — 5bca. Обратимся к записи таблицы страниц последнего уровня:

lkd> !dq 5bca000+1df*8 L1
# 5bcaef8 82a00000`056cb847

Номер фрейма страницы в этом случае составляет 56 КБ, и наши данные имеют нулевое смещение байта на соответствующей странице. Другими словами, виртуальный адрес, с которого мы начали, отображается на физический адрес 56cb000.

Чтобы убедиться в этом, мы можем попросить отладчик отобразить данные по этому физическому адресу, а также данные по виртуальному адресу, который у нас был в первую очередь. К счастью, данные идентичны.

lkd> !db 56cb000 L40
# 56cb000 00 00 00 08 00 00 00 00-ff ff ff ff ff ff ff ff ................
# 56cb010 00 00 06 ff 00 00 00 00-40 26 f3 76 00 00 00 00 ........@&.v....
# 56cb020 e0 1d 38 00 00 00 00 00-00 00 00 00 00 00 00 00 ..8.............
# 56cb030 00 00 38 00 00 00 00 00-00 a9 f3 76 00 00 00 00 ..8........v....

lkd> db 7fffffdf000 L40
000007ff`fffdf000 00 00 00 08 00 00 00 00-ff ff ff ff ff ff ff ff ................
000007ff`fffdf010 00 00 06 ff 00 00 00 00-40 26 f3 76 00 00 00 00 ........@&.v....
000007ff`fffdf020 e0 1d 38 00 00 00 00 00-00 00 00 00 00 00 00 00 ..8.............
000007ff`fffdf030 00 00 38 00 00 00 00 00-00 a9 f3 76 00 00 00 00 ..8........v....