• Уменьшение отступа

    Обратная связь

    (info@ru-sfera.pw)

На заметку Ищем "мёртвое мясо" в физической памяти :)


virt

Просветленный
Просветленный
Регистрация
24.11.2016
Сообщения
706
Репутация
228
0b83751bb3be4ac98f43a02d62af12a9.png

Ко взлому статья неотносится, но если вкурите суть, то ознакомитесь с азами отладки оси...

А также поймёте, как устроена виртуальная память в винде ! :)

Изучение виртуального адресного пространства и алгоритма преобразования адресов заметно упростится, если начать с несложного практического примера. Для этого напишем простую программу, выводящую адрес локальной переменной:
Код:
int main()

{
   unsigned i = 0xDEADBEEF;
   std::cout << "address of i is " << std::hex << &i;
   std::cin.get(); //Чтобы процесс не завершился
   return 0;
}

Затем попробуем найти физический адрес и просмотреть значение по этому адресу.

В моем случае адрес получился равным 0x22FF2C. Вообще, он может различаться при каждом запуске программы (см. ). У других процессов по этому адресу могут находиться какие-то свои значения, потому что это не физический, а виртуальный адрес. Пожалуй, основное предназначение виртуального адресного пространства – возможность предоставлять каждому процессу собственное адресное пространство, в котором он бы не мешал другим. Размер виртуального адресного пространства зависит от платформы. Для x86 теоретический максимальный размер составляет 4 Гб.

По умолчанию, первая половина (0 – 0x7FFFFFFF) является пространством процессов пользователя, в котором располагается образ исполняемого файла текущего процесса, его стек, куча и прочее. Вторая половина (0x80000000 – 0xFFFFFFFF) – системным. С некоторыми оговорками можно считать, что пространство процессов пользователя уникально для каждого процесса, а системное только одно. Адрес 0x22FF2C, очевидно, попал в первую половину.

Виртуальное адресное пространство разбито на 0x100000 (1048576) страниц размером 4096 байт каждая. Физическая память также разбита на страницы такого же размера, называемых страничными блоками. Страницы (не все, конечно) отображаются на страничные блоки, поэтому для каждой страницы нужна информация о ее расположении в физической памяти.

Всем 0x100000 страницам соответствует столько же 4-байтовых записей, называемых PTE (page table entry – запись таблицы страниц). В виртуальном пространстве они расположены в диапазоне адресов 0xC0000000 — 0xC03FFFFF, и занимают 1024 страницы, называемых таблицами страниц. Получить запись просто: k-й странице соответствует k-я запись.


df4c13c9e8c647c8ba55fe229a426f09.png

Оранжевым отмечены таблицы страниц.
Код:
virtual_address = 0x22FF2C
page_index = virtual_address / 4096
pte_addr = 0xC0000000 + page_index * 4
Умножение на 4 потому что PTE – 4-байтовые. Получаем, что в нашем случае pte_addr = 0xC00008BC

Наивная попытка

Адрес PTE есть, пробуем узнать что там:
Код:
std::cout << "PTE is " << std::hex << *(unsigned*)0xC00008BC;
Ну, ой. Аппаратное исключение. А все потому, что мы пытались читать из системного пространства. ReadProcessMemory тоже не поможет. Вызов VirtualQuery скажет нам PAGE_NOACCESS. Получить доступ можно только получив привилегии режима ядра. Пожалуй, самый простой способ для нашей исследовательской задачи — использовать отладчик ядра.

Использование отладчика ядра

Ставим и . LiveKD позволяет запускать отладчики ядра Microsoft Kd и Windbg, входящие в пакет инструментов отладки для Windows, в действующей системе в локальном режиме. По последней ссылке также небольшая справка по установке и справке.

Запускаем наш пример (пусть он называется main.exe). Запускаем LiveKd. Пишем "!process 0 0", чтобы вывести список всех работающих процессов, или сразу "!process 0 0 main.exe"
Код:
0: kd> !process 0 0 main.exe
PROCESS 86530118  SessionId: 1  Cid: 0dcc    Peb: 7ffdd000  ParentCid: 0428
   DirBase: 2402e000  ObjectTable: 8879f430  HandleCount:  16.
   Image: main.exe

Нас интересует адрес после слова PROCESS (это адрес на структуру EPROCESS, содержащей атрибуты процесса). Подключаемся к процессу:
Код:
0: kd> .process 86530118
Implicit process is now 86530118
Проверяем содержимое по адресу 0x22FF2C, чтобы убедиться, что все сделали правильно:
Код:
0: kd> dd 22FF2C L1
0022ff2c  deadbeef
По умолчанию используются шестнадцатеричные числа. Команда dd выводит несколько 4-байтовых значений, начиная с указанного виртуального адреса. L1 – вывод только одного значения.

Чтение PTE
Код:
0: kd> dd C00008BC L1
c00008bc  6612f847

Можно было самим не считать:
Код:
dd C0000000 + (22FF2C >> 0xC) * 4 L1
c00008bc  6612f847
В значении PTE записи 6612f847 первые 20 бит (5 hex-цифр) – индекс страничного блока, остальное – различные флаги. Чтобы получить адрес страничного блока, нужно индекс умножить на размер блока – 4096 байт.
Код:
page_block_index = 0x6612F
page_block_address = page_block_index << 12 = 0x6612F000 //Умножение на 4096
Порядок байтов внутри страницы и страничного блока совпадает, поэтому необходимо рассчитать смещение внутри страницы и прибавить к адресу страничного блока:
Код:
virtual_adress = 0x22FF2C
offset = virtual_adress & 0xFFF = 0xF2C //Последние три hex-цифры
phisycal_address = page_block_address + offset = 0x6612FF2C
Проверяем:
Код:
0: kd> !dd 6612FF2C L1
#6612ff2c deadbeef
Команда !dd аналогична dd, только принимает физические адреса.

Мы выяснили, что наш адрес можно представить так:
Код:
0x22FF2C = b 00000000001000101111 111100101100
            20 бит               12 бит
            page_index           byte_offset

Но еще заметьте, что найденная PTE находится в 0-й таблице страниц с индексом 0x22F внутри нее. И наш адрес может быть представлен так:
Код:
0x22FF2C = b 0000000000 1000101111 111100101100
            10 бит     10 бит     12 бит
            table_idx  PTE_index  byte_offset

We need to go deeper (PDE)

Пользоваться виртуальными адресами PTE неспортивно. Ведь они тоже являются обычными страницами которым нужно найти страничные блоки. А раз так, то просто найдем свои PTE для этих страниц. Всего у нас 1024 таких страниц (называемых таблицами страниц) и все PTE для них помещаются в одной странице. Эту страницу называют каталогом страниц и она содержит 1024 записи (называемых PDE – page directory entry, запись каталога страниц) с адресами на таблицы страниц.


a17c448637c24a4486ce4c4b62f51a48.png

Синим отмечен каталог таблиц, оранжевым – таблицы страниц.


Поступаем точно так же, как уже делали:

Код:
pte_addr = 0xC00008BC
page_index = pte_addr / 4096 = 0xC0000
pde_addr = 0xC0000000 + page_index * 4 = 0xC0300000

Получили адрес PDE = 0xC0300000 (все PDE хранятся в странице по адресу 0xC0300000, мы попали в нулевую PDE). Проверяем содержимое:
Код:
0: kd> dd C0300000 L1
c0300000  0b21d867
Полностью аналогично: PDE, содержащая 0b21d867, дает нам адрес 0x0B21D000 страничного блока с таблицей страниц. Осталось найти в ней нужную PTE. Вспомним, что адресу 0x22FF2C соответствует PTE с индексом 0x22F в 0-й таблице (со смещением 0x22F * 4). Значит, PTE находится по адресу 0x0B21D000 + 0x22F * 4.
Код:
0: kd> !dd 0b21d000 + 0x22f * 4
# b21d8bc 6612f847

С адресом 6612f847 мы уже работали.

Осталось выяснить, где в физической памяти находится каталог (так как мы получали PDE с помощью виртуальной адресации). Адрес был указан в DirBase, когда мы просмотрели информацию о процессе командой "!process 0 0 main.exe". В нашем случае DirBase = 2402e000
Код:
0: kd> !dd 2402e000
#2402e000 0b21d867

Итоговая формула

Код:
0x22FF2C = b 0000000000 1000101111 111100101100
            10 бит     10 бит     12 бит
            PDE_index  PTE_index  byte_offset

pde_addr   = DirBase + PDE_index * 4
pte_addr   = ((*pde_addr) & 0xFFFFF000) | (PTE_index * 4)
value_addr = ((*pte_addr) & 0xFFFFF000) | byte_offset

Ищем в дампе

Думаю, снять дамп с работающей системы несколько проблематично, поэтому снимем с VirtualBox. Для этого нужно запустить в режиме отладки:

VirtualBox.exe --dbg --startvm VM_name

Выбрать в меню "Отладка" -> "Командная строка..." и набрать:

.pgmphystofile "path_to_dump_file"

Открываем файл (я пользуюсь HxD), переходим на 6612ff2c:

5db1be7632414599a0547fe4a22557b9.png



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

Источник:

Таким образом можете исследовать различные программы и т.д. ! :)
 

X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
6 068
Репутация
8 175
Понравилась эта статья на хабре, ну и для тех-кто в "танке" 0xDEADBEEF это не слово, а "магическое" число в шестнадцатиричном виде...

Я в своих программах по всякому прикалываюсь с этими значениями, например магическое число что-бы проверить стек, называю 0xBABADED, ну или там всяко абзываю, эти два примера самые "безобидные" (не ругательные) значения...Dmeh-Smeh-Smeh!!!Dmeh-Smeh-Smeh!!!Dmeh-Smeh-Smeh!!!
 
Верх Низ