Введение в реверсинг с нуля, используя IDA PRO. Часть 18.

Дата публикации 17 окт 2017 | Редактировалось 18 окт 2017
В предыдущей части, мы распаковали исполняемый файл упражнения и сделали его рабочим. В этой части, мы будем его реверсить, чтобы увидеть, можно ли сделать для него кейген в PYTHON.

Хорошо помнить, что для статического анализа нет необходимости распаковывать файл. Нам просто нужно получить OEP и сделать TAKE MEMORY SNAPSHOT. Затем, нужно скопировать файл .IDB в другое место и открыть его там. Этого бы хватило, чтобы анализировать его статически, но хорошо иметь распакованный файл, это позволит отлаживать файл и может иногда помогать.

Я открываю распакованный файл в IDA, и первое, на что я обращаю внимание - это строки.

1.png

Хорошо, мы знаем, что первое, что делаем программа после запуска - это печатает строку “Pone un user”.

2.png

Поэтому, я делаю двойной щелчок на этой строке в IDA и попадаю сюда.

3.png

И ищу перекрёстную ссылку с помощью клавиши X.

4.png

Видно, что ссылка нашлась. Теперь можно перейти по ней.

5.png

Давайте будем статически реверсить начинаю отсюда.

В функциях, основанных на EBP, мы сказали, что сначала, в стек, с помощью инструкции PUSH EBP сохраняется EBP функции, которую я вызвал, и, затем, исполняется инструкция MOV EBP, ESP для установки EBP как опорного значения для этой функции, откуда будут вычисляться позиции изменяемых аргументов и буферов.

Видно, что программа резервирует 0x94 байта для локальных переменных и буферов, начиная с базового значения EBP.

6.png

Хорошо, делая двойной щелчок на любой переменной или аргументе, ЗАГРУЗЧИК показывает статическое представление стека.

7.png

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

8.png

Это тот же самый случай, как и в прошлый раз. Это функция MAIN и она имеет такие аргументы: ARGV и ARGC и т.д. Но, так как она не использует их внутри функции, то IDA не учитывает эти аргументы.

9.png

10.png

Давайте переименуем эту функцию в MAIN, и IDA добавит мне автоматически три аргумента.

11.png

Также, если мы нажмём клавишу X на любом из трёх аргументов.

12.png

Увидим, что аргументы не используются, так что они не очень важны для нас.

13.png

Возвращаясь к статическому представлению стека, видим, что ниже адреса возврата, как и положено, есть аргументы. Затем идёт буква S, что означает STORED EBP. Как мы сказали, это значение хранит EBP предыдущей функции, которое помещается в стек с помощью инструкции PUSH EBP и выше есть место для переменных, которое обычно имеет переменную VAR_4, которая нужна для защиты стека от переполнения буфера.

14.png

Эта переменная имеет две перекрёстные ссылки. Одну в начале функции, когда программа сохраняет значение SECURITY COOKIE(Печеньки безопасности. Прим. Яши) в стек.

15.png

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

16.png

Где программа восстанавливает исходное сохраненное значение и XORит его с помощью EBP для восстановления исходного значение в ECX, и внутри этого CALL, программа проверяет это значение.

17.png

Если всё нормально, программа будет возвращаться, но если ECX не имеет первоначального значения __SECURITY_COOKIE, программа перейдёт в JMP, который ведёт на ВЫХОД и не позволит Вам достигнуть RET функции.

Плохой вариант может случиться и программа пойдёт на ВЫХОД, если произойдёт ПЕРЕПОЛНЕНИЕ, которое перезаписывает значение VAR_4 внутри функции. Сейчас давай переименуем VAR_4 в CANARY(КАНАРЕЙКА) или SECURITY COOKIE.

18.png

Сейчас, листинг выглядит более красиво и читаемо.

19.png

Затем, видим две переменные, о которых мы ещё ничего не знаем и о которых мы ещё не говорили, как они используются. Они инициализируются нулями. Также есть переменная, которая уже имеет имя SIZE и инициализируется числом 8.

Если посмотрим перекрёстные ссылки для переменной VAR_7D, увидим, что она используется здесь.

20.png

Программа сохраняет значение AL в эту переменную при возврате из CALL и затем перемещает этот байт в регистр EDX, для того, чтобы проверить равен он нулю или нет, чтобы сделать вывод о том, хорошие мы реверсеры или плохие. Так что, это переменная одного байта или флаг. Следовательно, мы можем переименовать её в FLAG_EXITO.

Мы проверяем в статическом представлении стека, что IDA определила переменную как байт.

21.png

Нужно поменять ей имя с помощью клавиши N.

22.png

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

Очевидно, если бы нам нужно было просто пропатчить этот переход JZ, это было бы то место, где нужно было это сделать, но мы будем стараться решить этот крекми правильным способом.

23.png

Мы видим, что другая переменная VAR_90, которая тоже обнуляется в начале, складывает байты, которые читаются из буфера BUF один за другим и помещает их в регистр EDX по адресу 0x231109, а затем прибавляет его к нулю в первом цикле, и EDX всегда накапливает сумму всех байтов. Мы увидим, что он содержит буфера BUF, который он читает. Давайте продолжать расследование.

24.png

Видно, что VAR_84 - это счетчик этого ЦИКЛА, который складывает значения. Но видно, что цикл складывает только первые четыре байта, потому что он выходит, когда это значение больше или равно 4.

Здесь видно этот СЧЁТЧИК и как он увеличивается.

25.png

Очевидно, этот счётчик также присутствует по адресу 0x231109 для чтения буфера BUF с самого начала и нужен для сложения его следующих байтов.

26.png

Хорошо, мы уже видели, что этот ЦИКЛ читает байты из буфера BUF, затем суммирует их и сохраняет эту сумму в SUMATORIA. Теперь давайте посмотрим, что находится в BUF.

27.png

Видно, что размер BUF равен 8 байтами и это максимальная длина для имени пользователя. Функция GETS_S используется для получения данных с клавиатуры.

Нам нужно поменять функцию по адресу 0x002310A0 на PRINTF.

28.png

Готово.

29.png

Также, в статическом представлении стека мы видим длину буфера BUF с помощью правого щелчка и выбора пункта ARRAY.

30.png

Размер совпадает с исходным кодом.

31.png

32.png

Представление стек становится для нас более ясным.

После получения данных в буфер BUF, программа передаёт их в функцию STRLEN, чтобы узнать длину данных, которые были введены.

33.png

Следовательно, VAR_88 это количество байт, которые мы вводим.

34.png

И если длина буфера меньше чем 4, программа идет на ВЫХОД.

Из всего этого, уже можно сделать вывод, что этот ЦИКЛ складывает первые четыре байта пользователя, которые мы ввели. Поэтому давайте перегруппируем блоки, для того чтобы они не мешались и смотрелись лучше. Щелкаем в панели каждого блока зажав CTRL.

35.png

Сейчас выглядит намного лучше. С помощью правого щелчка GROUP NODES и выбора пункта UNGROUP я могу разгруппировать блоки, если нам это понадобится.

36.png

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

37.png

Снова программа использует функцию STRLEN, чтобы узнать размер буфера и если он меньше чем 4, программа отправляет нас на ВЫХОД.

38.png

Если размер буфера равен или больше 4, программа продолжит выполнение с зеленого блока.

39.png

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

40.png

41.png

Здесь видно, что пароль XORится в HEX представление с помощью ключа 0x1234 и программа сохраняет его снова в ту же переменную.

42.png

Мы видим, что программа будет сравнивать сумму первых 4 байт пользователя и HEX значение пароля обработанное операцией XOR с ключом 0x1234 в этой функции, которую мы назовём CHEQUEO_EXITO. Результат функции определит переход программы в хорошее сообщение или плохое.

Здесь, мы видим два аргумента. ARG_4 будет тем, который помещается первым.

43.png

Так что давайте переименуем внутри функции оба этих аргумента.

44.png

Пришло время правильно настроить аргументы для этой функции, для этого делаем правый щелчок и выбираем SET TYPE.

45.png

Смотрим на результат после проделанной операции.

46.png

Смотрим, распространились ли теперь ссылки?

47.png

Видно, что синие сообщения, которые появились при распространении имён, соответствуют именам в ссылке, так что всё сработало правильно.

48.png

Я вижу, что перед сравнением значений, программа делает SHL EAX, 1 что равносильно умножению аргумента на 2.

Значит, если они равны, программа перейдёт в зеленый блок, где она поместит 1 в регистр AL, и она будет возвращать значение как флаг FLAG_EXITO, который должен определить, хорошие мы реверсеры или плохие.

Обобщим всё это.

Программа берет первые 4 байта имени ПОЛЬЗОВАТЕЛЯ и складывает их.

ПАРОЛЬ переводится в HEX и он XORится с помощью ключа 0x1234 и затем умножается на 2.

Теперь будем делать формулу, предполагая, что мы знаем имя ПОЛЬЗОВАТЕЛЯ, так как кейген основан на знании этого имени. С помощью определённого имени ПОЛЬЗОВАТЕЛЯ, кейген будет находить соответствующий пароль.

X = ПАРОЛЬ конвертируется в HEX

(X
^ 0x1234) * 2 = СУММА

Если поделим на 2

X ^ 0x1234= (СУММА/2),
то получится так

X = (СУММА/2) ^ 0x1234

Функция XOR обратима и она работает с членами так:

A ^ B = C

A = B ^ C

Хорошо, значение X, которое нужно найти, рассчитывается по следующей формуле

X = (СУММА/2) ^ 0x1234

Если моё имя было бы например pepe, которое действительно, потому что оно меньше чем 8 байт, сумма байтов рассчитывалась бы так.

49.png

Здесь, мы получили сумму для моего пользователя pepe.

50.png

Но мы помним, что не все байты суммируются, а только первые четыре. Подправим наш скрипт.

51.png

Скрипт теперь выглядит лучше, потому что он проверяет, что имя больше или равно 4 как того просит программа.

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

52.png

Видно, что используя RAW_INPUT(Считывает и возвращает строку входных данных. Прим. Яши) мы получаем всё, что мы печатаем через консоль.

Результат для pepe схожий. Сумма равна 0x1AA. Но я могу получить её и для любого пользователя, например fiaca.

53.png

Мы получаем такую формулу:

X = (СУММА/2) ^ 0x1234

Поэтому сумма должна делиться на 2 и XORиться с помощью ключа 0x1234, чтобы находить пароль в HEX виде.

54.png

Если я пробую это значение, которое подсчитал нам скрипт.

55.png

Теперь у нас есть кейген. Сейчас, нам не нужно делать преобразование пароля из HEX в десятичное значение, потому что PYTHON всегда печатает в десятичном формате по умолчанию.

56.png

Мы видим, что скрипт складывает только первые 4 символа имени пользователя. Имя не имеет значения, если пароль больше, но 4 начальных символа похожи.

Поэтому приложение падает при вводе 8 символов, так как имя должно состоять из 8 символов, включая завершающий нуль в конце строки.

57.png

До 7 символов программа функционирует хорошо.

Только в ней есть одна проблема, когда сумма получается нечётной.

58.png

Уравнение не имеет решения, так как пароль заканчивается умножением на 2 и является умножением целочисленных целых чисел, он никогда не будет нечетным, так что мы добавляем эту проверку в скрипт.

59.png

Здесь, мы проверяем остаток от деления на два. Если он равен нулю, это пара не имеет решения.

60.png

Я думаю, что наш кейген уже очень хорош. Так что мы можем закончить эту часть и увидимся теперь в следующей.

До встрече в 19-той части.


Автор текста: Рикардо Нарваха - Ricardo Narvaja (@ricnar456)
Перевод на английский: IvinsonCLS (@IvinsonCLS)
Перевод на русский с испанского+английского: Яша_Добрый_Хакер(Ростовский фанат Нарвахи).
Перевод специально для форума системного и низкоуровневого программирования — WASM.IN
15.10.2017
Версия 1.0

8 8.380
yashechka

yashechka
Ростовский фанат Нарвахи

Регистрация:
2 янв 2012
Публикаций:
90

Комментарии


      1. DrVirus 4 дек 2022
        This is not full tutorial or am i missing something. There is a keygen also prepared in original tutorial.
      2. CrawlUp 25 дек 2018
        Хотел keygen сделанный добавить, но в коментарии вставлять файлы нельзя, как я понял.
        p.s.
        yashechka спасибо за ваш труд, но большая просьба не лайкайте мои комментарии. Может прозвучит странно, но по колличеству лайков у профиля, можно, хоть и косвенно, оценить полезность того или иного человека на этом форуме. И не хотелось бы получать лайки не заслуженно :)
        yashechka нравится это.
      3. yashechka 19 окт 2017
        Хочу, чтобы Инди плюсанул, тут дело принципа ;)
      4. tfs_cradle 19 окт 2017
        буду первым :)
        yashechka нравится это.
      5. yashechka 19 окт 2017
        Инди, если Вы не лайкните перевод, то всё ..............
      6. yashechka 19 окт 2017
        Просмотров - нет, лайков - нет. Я понял, все скачали ПДФ версию =)
      7. yashechka 18 окт 2017
        Вот и мы:acute::acute::acute::derisive::derisive::derisive: