Статья [Reverse-Engineering] Разбор, решение Virtual Machine (BabyVM)

  • 112
  • 92
Контакты для связи отсутствуют.
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Всем привет. Сегодня у нас на разборе crackme, суть которого - это найти пароль, но он отличается от обычных crackme,т.к в нем присутствует симуляция виртуальной машины(не знаю как это правильно называется.). Что же, приступим.
Картинок здесь уж очень много, но мне кажется так легче объяснить)

Я пытался объяснить максимально понятно и не растягивая, по этому объяснять некоторые функции по типу mov from register я не стал, да и незачем. Там все довольно просто(4 ассемблерные строки)

1) Как найти main функцию.

Наша главная задача заключается в поиске main функции. Когда я только начинал, я не мог ее так просто найти. Мне приходилось залазить в строки и искать в строках что-то типа"Enter the pass:", без него же я не мог его найти. Всего есть 2 варианта(возможно еще какие нибудь, но я пользуюсь только этими).
Вариант 1: Если вы уже реверсили довольно немного программ, написанные например на С++, вы можете найти main по памяти.
Вариант 2: Если же вы новичок или не встречали программ, например написанных на Pascal и вы не понимаете где может быть main функция, а строк,которые могли бы подсказать нет, тогда вы просто жмете Trace Over(F8) пока вы не увидите, что черная строка пропала,а в статусе написано Running. Например как тут:
1619361608374.png

1619361388027.png
2) Анализ main функции.

Вот мы и находимся в main функции. Здесь мы видим довольно несложные действия

1619361836249.png

1619362082754.png

*Адресу. Я ошибся)
Содержатся*
1619362293932.png

Вкратце про init_vm. Она просто инициализирует адрес(D09090) значениями, и также переносит наш байт код в "регистр"(помечено 1). Инициализирует просто выделением памяти и записи туда этих значений
1619362461835.png

Все эти значения - это регистры. Красным отмечена область,которая не входит в регистры. Я сделал кривой скриншот, а переделывать было лень(((. В основном, они хранят разные EIP, чтобы при помощи этого сделать "цикл". Это понадобится нам при чтения пароля и его проверки и при расшифровки нашего сообщения о введении пароля.
1619362630562.png

1619363229135.png

1619363467478.png

3) Разбор байтов.
Здесь очень много байт кодов и разбор каждого занял бы довольно много времени, да и вы бы наверное устали читать).
По этому я разберу только самые важные.
1) 0x1С байт код.
1619364437857.png

Далее идет запись "p" в регистр. Вы этого не видите, т.к в скрин не влез, но там есть такая же функция как и на скрине выше.
2) 0x3С байт код.
У нас идет вывод этой 1 буквы, в нашем случае "p". Функция mov from register, перемещает значение с нашего виртуального регистра в регистр дебаггера сбоку. Вы можете посмотреть на него на скрине выше. Далее идет печать с помощью putchar функции.
1619364613170.png

3) 0x13 байт код. Тут идет уменьшение числа. Это число отвечает за длину пароля. Функция длинная, но там ее основная цель это уменьшение числа в виртуальном регистре
1619364868071.png

Кстати, забыл упомянуть в начале, что вот этот участок в нашем виртуальном регистре это ZF(zero flag). Он будет использоваться при сравнении пароля, длинны пароля, длинны текста, где нас просят ввести пароль. Этим самым действием, автор реализовал CMP инструкцию .Если кто-то не понял вот пример.
У нас есть 2 числа. 0x80 и 0x79. Нам нужно их сравнить, но как? Это делается при помощи отнимания от 0x80 - 0x79. Если результат не равен 0, тогда ZF = 0, иначе ZF = 1.
1619365055793.png

4) 0x3A байт код. Этот байт код вызывает функцию,которая перемещает некое значение в виртуальный EIP. В нашем случае, это
1619366718788.png

То есть,мы имеем некое число записанное в r14. Это число - это указатель на то, какую инструкцию нам нужно выполнить следующую. Судя по числу ,которое хранит r14 регистр, мы можем сделать вывод, что он хранит указатель назад. То есть,представим такую картину.
Я сейчас напишу рандомные байты, это никак не относится к нашему крякми, просто для понимания.
Мы имеем вот такую последовательность байт кода.
Адрес Байт код
0 0xA 0xB 0xC 0xD
4 0xE 0xF 0x10 0x11
Наш регистр r14 содержит адрес 0, и это равно 0xA. А наш виртуальный регистр EIP имеет адрес 0x4 ,что соответствует 0xE.
Наша функция ,которая изображена выше, делает такие вещи. Она перемещает регистр r14 в регистр eip. Следуя примеру,
до перемещения наш виртуальный регистр EIP имел адрес 0x4 ,что соответствует 0xE, после применения перемещения, регистру eip присвоено АДРЕС r14, что соответствует адресу 0 и соответствует значению 0xA. То есть, теперь наш EIP имеет адрес регистра R14(0 адрес). Это используется для создания цикла. С помощью 0x13 байт кода, мы сокращаем количество повторений в цикле. Думаю,суть вы уловили.
Далее мы повторяем расшифровку и вывод по символу 10 раз.
В итоге мы получаем password->
1619368434900.png


Финальная часть

Все основные байт коды мы разобрали. Теперь осталось просто понять генерацию пароля. Скажу сразу, что она несложная. Сама суть,я думаю, заключалась в выяснении за что отвечает каждый байт код.
Все начинается с того, что у нас считывают пароль с помощью байт кода 0x3D.
1619368812696.png

Далее мы переходим в самую главную функцию, мы ее уже встречали, и я говорил вам,что она разделена на несколько частей, для разных нужд, но выполняет похожие задачи.
Ее байт код 0x1C
Выглядит она так:
1619369045911.png

Константа всегда статическая, то есть она не меняется и она равна 0xAC.
Результат XORING записывается в то место, где мы хранили наш символ , в моем случае в 16 виде 0x31 в ascii представлении это "1".
Мой результат 0x9D(что неправильно,смотрите дальше)
Идем дальше.
Далее мы попадаем в функцию с байт кодом 0x2E. Она нам уже известна. Тут просто сверяются результаты xoring путем отнимания.
Если мой результат xoring равен константе 0xC5 , тогда ZF = 1 и программа продолжает сверять,иначе программа завершается.
Вот это место с объяснениями
1619369407777.png

1619369422482.png

Тут я думаю,все понятно. Первая mov функция присваивает eax ту самую константу 0xC5 ,а вторая дает нам результат xoring,в моем случае 0x9D

Внизу ошибка. Если при отнимании результат получается 0, тогда ZF = 1, иначе ZF = 0.
1619369601861.png

Вот эта наша функция. Мы имеем константу,которая должна получиться 0xC5 и которая получилась 0x9D. По логике, мы должны xor 0xC5 на 0xAC(это статическая константа, смотри выше,мы уже говорил про нее). Что и равно "i" или "69" в hex.
Все,первый символ у нас "i".
Следующая константу, мы получаем в функции с байт кодом 0x0A. Это она выглядит внутри
1619370494207.png


Там будет 2 или 3 функции, входим в нее и находим
1619370558398.png

У вас не будет подписано, т.к я ее подписал сам. Заходим внутрь.
Видим такое дело
1619370680867.png

В конечном итоге, мы получаем свою константу. В моем случае это 0xC1. Опять xor ее с 0xAC. Это равно 0x6D hex и в ascii "_"
Делаем так с каждым символом и получаем.

Конечный результат:
i_4m_t0p_cr4ck3r!
1619370895919.png


Всем спасибо. Это конец.
Я старался объяснить как можно понятнее и самые важные места, чтобы это заняло не так много времени, но в итоге это все равно заняло 3 часа.
Если бы я все разбирал, боюсь представить сколько бы времени я потратил)))


p.s извините за ошибки в словах на скринах.
 

Вложения

  • 1619368417150.png
    1619368417150.png
    6.4 KB · Просмотры: 76
Последнее редактирование:
  • 112
  • 92
Контакты для связи отсутствуют.
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
спс братан залил свежак на форум

Посмотреть вложение 20900
ты угараешь с меня? Во-первых, это крякми ShockByte,а ты мне скинул не пойми кого + чекни логику того кода и ту,что я разобрал

UPD
Специально для тебя нашел его крякми на гитхабе.
Сверь код и логику то, что на гитхабе и то,что я разобрал.
Вообще крякми брался отсюда
Тем более, при чем здесь свежак? Главное это понять логику, я попытался разъяснить логику виртуальной машины. Вот и все, от того,что я возьму виртуальную машину посвежее ничего не изменится, никаких особо новых фич ты не увидишь. Я уже не одну разобрал виртуальную машину, и все +- одинаковые.
 
Последнее редактирование:
Сверху Снизу