Решаем простой CrackMe с помощью angr

Не так давно мне попался на глаза занимательный Python-фреймворк под названием angr. Он предназначен для анализа всевозможных исполняемых файлов под различные платформы. И, несмотря на незнание мной Python'а и нелюбовь к паскаледобному синтаксису, я все же решил потратить некоторое время на его изучение, так как на одном CTF'е увидел занятный вариант решения задания по реверс-инжинирингу с его использованием.
Перейду сразу к сути: с помощью него можно, например, закейгенить произвольный CrackMe почти не занимаясь разбором алгоритма проверки ключа (конечно, речь не идет о ситуациях, когда для генерации ключа используются лучшие практики ассиметричной криптографии и прочие штуки).

Итак, для тестов я нашел в интернете небольшой CrackMe с названием crackme01_x64.exe и логотипом Лаборатории Касперского. Давайте проведем начальный анализ и посмотрим, что он из себя представляет. Для начала просто запустим его:

crackme

Что ж, ничего необычного, нужно подобрать серийный номер, также при нажатии мы можем наблюдать сообщение: Fail, Serial is invalid !!!. Теперь откроем файл в каком-нибудь дизассемблере, который поддерживает 64-битные приложения, я для этой цели воспользуюсь x64dbg.

x64dbg_init

Мы видим стандартный пролог и вызов функции DialogBoxParam, который создает диалог с окном, которое мы наблюдали при запуске программы, по адреу 140001110 расположена процедура, в которой реализована логика работы диалога, перейдем к ней.

dlgmain

Человек, который хотя бы немного знаком с WinAPI и конвенцией вызовов fastcall, сразу увидит, что тут нет ничего интересного, кроме вызова функции по адресу 140001000, которой в качестве аргументов передается строка, полученная предшествующим вызовом GetDlgItemText, и ее длина:

Далее идет проверка, что вернул вызов функции 140001000, и если это 1, то серийный номер корректный. Рассмотрим функцию проверки серийного номера:

check_func

Мы наблюдаем большой фрагмент кода, который не поместился у меня на экране и который мне чрезвычайно лень анализировать, хотя я все же отмечу несколько особенностей, которые нам понадобятся в дальнейшем:

1. Почти в самом начале мы видим строку:

Как вы помните, в edx была передана длина строки серийного номера. Это значит, что искомый серийный номер должен состоять из 19 символов (0x13 = 19).

2. Почему собственно 19 символов? Странный размер, но обратите внимание на код чуть ниже:

Это может быть не слишком очевидно для человека, который не привык копаться в ассемблере, но тут проверяется каждый 5 символ серийного номера, и он должен быть равен 0x2D или символу "-" (без кавычек). Вообще, эту логику можно увидеть, просто введя произвольный серийный номер и немного потрассировав в отладчике.

3. Самый простой пункт. В нем я просто отмечу, что функция возвращает 1 (ее мы и хотим) только при возвращении (инструкция ret) по адресу 00000001400010FC, а адреса 0000000140001012 и 0000000140001108, по которым тоже расположена инструкция ret, нас не интересуют, так как им предшествует код xor eax,eax, а это означает, что функция вернет 0.

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

Как вы можете видеть, в коде я использую немного другие адреса в качестве параметров find и avoid. Поясню: это адреса, куда ведут условные переходы, за которыми в дальнейшем следуют инструкции ret, адреса которых я упоминал выше.
Итак, запускаем скрипт:

И за какие-нибудь 5-10 секунд получаем:

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

good

Номер подходит! Классно же?

CrackMe + скрипт: скачать

Решаем простой CrackMe с помощью angr: 11 комментариев

    1. В следующий раз напишу про z3, который требует изучения алгоритма и решает систему закономерностей, которым подчиняется серийный номер

  1. Frida говорят что хороший Фреймворк для реверса, может напишешь про него и особенно анализ моб приложений

  2. Ух ты, питон не люблю (потому что я тупой), но штука забавная. Правда область применения, наверное, только подобные задачки.

    То есть можно сформулировать примерно такое правило: если крякми можно решить исправив один условный переход (или заставив возвращать "1" нужную процедуру), значит и фреймворк вероятно поможет.

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

      1. Если алгоритм вычисления размазан по всей программе, ключ проверяется в разных местах программы по частям, напичкан ловушками, в этом случае прилаживание фреймворка само по себе может стать вызовом.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *