В предыдущих статьях мы познакомились с двумя основными типами хакерского инструментария: дизассемблером и отладчиком. Первый служит для статического изучения программного обеспечения, тогда как второй — для динамического. То есть дизассемблер открывает образ файла, хранящегося на носителе, в то время как отладчик раскрывает картину приложения во время выполнения, показывает образ в памяти. В очередной статье цикла мы продолжим изучать глубинное бурение чужого кода.
 

Перемещаемость EXE

В прошлой статье мы остановились на том, что, найдя в памяти с помощью отладчика адреса байтов, которые нужно изменить, мы обнаружили, что в файле на диске таких адресов нет. И какие инструкции в таком случае править?

Другими словами, теперь нам надо разобраться, как процесс проецируется в виртуальное адресное пространство, чтобы соотнести адреса байтов, находящихся в памяти, с их реальным расположением в файле на диске. Конечно, у нас есть дизассемблер, с помощью которого мы нашли эти адреса (см. первую статью). Это удалось во многом благодаря тому, что препарируемая нами программа очень маленькая, и нам не составило труда разобраться в ее дизассемблированном листинге. А если бы исследуемая программа весила сотни мегабайт?

Также мы выяснили, что PE-файл может быть загружен по адресу, отличному от того, для которого он был создан (это свойство называется перемещаемостью), при этом система автоматически корректирует все ссылки на абсолютные адреса, заменяя их новыми значениями. В результате образ файла в памяти не будет соответствовать тому, что записано на диске. И это происходит после каждой перезагрузки системы, а порой даже перезапуска приложения, всякий раз PE-файл размещается по новому адресу. Вдобавок к этому если раньше (до «Висты») системный загрузчик мог перемещать только DLL (в то же время, если ему не удавалось разместить в памяти по заданным адресам EXE, Windows выдавала ошибку загрузки модуля), то теперь исполняемые файлы тоже подвержены перемещению.

Между тем ошибка загрузки модуля происходила довольно редко, потому что, как мы прекрасно знаем, для каждого процесса Windows выделяет независимое виртуальное адресное пространство. Во времена 32-битной Windows это было 2 Гбайт ядерного пространства и 2 Гбайт пользовательского. То есть по факту для процесса выделялось только 2 Гбайт, а 2 Гбайт ядерного пространства были общими для всех процессов, к которым код из пользовательского режима доступа не имел. При включении режима PAE пользовательскому пространству доставалось 3 Гбайт и, соответственно, 1 Гбайт — ядерному. PAE в x86-процессорах стал нужен для работы DEP, препятствующей выполнению кода в секции данных. Он автоматически включен во всех более поздних процессорах. Если пользовательское пространство обособлено для конкретного процесса, то пространство ядра общее для всех привилегированных механизмов, выполняющихся в 0-м кольце.

Для x64 картина в целом аналогична. Адресное пространство заметно увеличилось, теоретически до 16 Эбайт. Но, так как современные процессоры фактически используют только 48 бит для адресации пространства, реально используется только малая часть: 8 Тбайт для пользовательского режима и 248 Тбайт для ядерного. Конечно, пока эти размеры кажутся заоблачными — примерно как 4 Гбайт в конце 1980-х. 🙂

Теперь, когда в общих чертах картина обрисована, можно двигаться дальше. Наше приложение passCompare1 откомпилировано 32-битным компилятором. Это позволит нам избавиться от лишних циферок, сохранив при этом смысл происходящего. Итак, чтобы найти адрес нужной инструкции на диске, вкратце повторим последовательность действий из предыдущей статьи, так как за прошедшее время ты наверняка перезагрузил компьютер, поэтому адреса в памяти изменились.

Сначала воспользуемся утилитой dumpbin из штатной поставки Visual Studio, на этот раз с ее помощью найдем базовый адрес модуля — тот, с которым работают HIEW (или другой шестнадцатеричный редактор) и дизассемблер:

 >dumpbin /headers passcompare1.exe
 
 OPTIONAL HEADER VALUES
 ...
 400000 image base (00400000 to 00405FFF)
 ...

Натравим отладчик на подопытную программу. Определим адрес загрузки модуля приложения в памяти (в твоем случае результаты будут другими):

 0:004> lmf m passcompare1
 start end module name
 00d30000 00d36000 passCompare1 passCompare1.exe

Далее нам нужно найти адрес инструкции, которую требуется изменить. Для этого первым делом надо найти расположение эталонного пароля (он находится в секции .rdata), поэтому воспользуемся командой !dh passCompare1, которая выведет сведения о секциях. Сложим адрес загрузки модуля и виртуальный адрес секции .rdata.

Таким образом, в моем случае секция .rdata начинается с адреса 0xD32000. Немного прокрутив вывод отладчика вниз, я вижу, что пароль располагается по адресу 0xD32108. Теперь нам нужен адрес расположения инструкции в памяти. Не напрягая мозг, легким движением рук поставим бряк на пароль: ba r4 d32108. Продолжим отладку и введем любой пароль, после всплытия отладчика по команде t сделаем шаг вперед. И двумя строками выше в дизассемблерном листинге отладчика мы видим сравнивающую инструкцию test eax, eax, которую нам надо отломать, а слева в первом столбце — ее адрес: 0xD310A7.

Если попробовать найти его в файле, то HIEW скажет, что такой адрес отсутствует. Но теперь, когда есть все необходимые значения, нетрудно посчитать, что адрес 0xD310A7 будет соответствовать адресу

адрес инструкции в файле на диске == адрес инструкции в памяти – (адрес загрузки модуля – базовый адрес модуля):
0xD310A7 – (0xD30000 – 0x400000) == D310A7 – 0x930000 == 0x4010A7

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

 004010A7: 85 C0 test eax,eax
 004010A9: 74 63 je 0040110E

Все верно, посмотри, как хорошо это совпадает с дампом отладчика:

 00d310a7 85c0 test eax, eax
 00d310a9 7463 je passCompare1!main+0xce (00d3110e)

Следующим действием отломаем программу. Это мы уже проходили в третьем шаге первой статьи. Ничего нового непосредственно во взломе не появилось, мы нашли адрес, а процедура кряка такая же: запускаем HIEW — и в бой.

 

Перемещаемость DLL

Под занавес прошлой статьи мы упомянули, что в старых версиях Windows можно было загрузить один и тот же exe-модуль два раза, представив его в виде DLL. Однако сейчас этот трюк не прокатывает, собственно, он и не нужен, поскольку, как мы увидели в предыдущем разделе, Windows свободно перемещает в памяти загруженный exe-модуль относительно заранее определенных адресов. Теперь давай разберемся, как обстоят дела с динамическими библиотеками.

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

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», увеличит личную накопительную скидку и позволит накапливать профессиональный рейтинг Xakep Score! Подробнее

Вариант 2. Открой один материал

Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.


Check Also

Через уязвимость в vBulletin взломаны форумы для секс-работников

Болгарский хакер скомпрометировал несколько форумов, включая итальянские и нидерландские р…

2 комментария

  1. Аватар

    enc

    07.03.2019 at 04:57

    Спасибо за статью, люблю как практические статьи. За разжовывание WinDbg отдельный +. С высчетом адреса в DLL так и не смог разобраться. Какие бы значения не подставлял выходит 7А6С2000. Базовый адрес загрузки чего — fixupdemo.dll или fixupload.exe?

Оставить мнение