В этой статье мы рассмотрим трансляцию виртуальных адресов на примере реального мира. Если хотите, приведенные здесь инструкции должны быть достаточно подробными, чтобы вы могли самостоятельно выполнить этот эксперимент с отладчиком ядра (достаточно локального сеанса отладчика ядра или даже 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....