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

Дата публикации 13 авг 2017 | Редактировалось 2 сен 2017
Хорошо, чтобы не было скучно, мы будем объединять теорию и некоторые упражнения, в этом случае, это другая программа скомпилированная мной, которая называется TEST_REVERSER.EXE и которая очень простая, но она поможет нам увидеть некоторые новые вещи в статическом реверсинге и которые мы проверим в отладчике.

Если мы запустим программу вне IDA, то мы увидим.

1.png

Нас просят ввести имя пользователя и затем пароль, а потом программа говорит нам, что мы проиграли, и она смеётся над нами.

Давайте откроем её в IDA, чтобы увидеть программу в статическом виде.

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

2.png

Очевидно, она не открывается в функции MAIN, зато открывается в EP, но хорошо, действительность почти всегда такова и мы разберемся с этой проблемой.

Один из способов, который мы уже видели, чтобы попасть в `горячую часть программы` - найти строки, мы уже знаем как это делать, также в этих консольных программах на C++, это один из способов найти MAIN, который почти всегда работает.

Мы знаем, что в функцию передаются аргументы ARGC, ARGV и т.д. Это аргументы консоли.

INT MAIN(INT ARGC, CHAR *ARGV[])

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

3.png

Отсюда и далее, когда я говорю - “На вкладке XXX”, Вы уже должны знать, что это открывается в меню VIEW → OPEN SUBVIEW → XXX, чтобы не повторять это более.

Здесь с помощью комбинации CTRL + F мы фильтруем ввод, введём например ARG и мы увидим записи, давайте сделаем двойной щелчок по _P_ARGC.

4.png

Ища ссылки с помощью X мы находим

5.png

Здесь мы видим как программа вызывает функции _P_ARGV и _P_ARGC и то, что она передает содержимое результата в функцию MAIN, адрес которой в этом случае - 0x401070.

Если я посмотрю ту же самую функцию в моей IDA, в версии с символами.

6.png

И мы видим ссылку.

7.png

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

Давайте переименуем этот вызов в MAIN.

8.png

IDA автоматически переименовывает АРГУМЕНТЫ узнав, что вышеупомянутая функция – это MAIN.

9.png

Сейчас, это больше похоже на версию с символами.

10.png

Мы видим в этом случае, что переменные и аргументы более нумерованы чем в предыдущем примере.

Если мы сделаем двойной щелчок на любой переменной или аргументу, мы увидим статическое представление стека.

11.png

Мы смотрим снизу вверх и видим, что логически первыми идут аргументы функции, которые будут всегда под адресом возврата R, так как они передаются через PUSH и они сохраняются в стек перед вызовом функции с помощью CALL, которая затем сохраняет адрес возврата в стек.

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

12.png

Затем программа помещает ESP в EBP, помещая значение в EBP, которое оно будет иметь в этой функции, чтобы быть БАЗОЙ откуда берутся аргументы, которые ниже БАЗЫ и локальные переменные, которые выше неё, и наконец инструкция SUB ESP, 0x94 сдвигает ESP выделяя место для переменных и локальных буферов, поэтому они выше, в этом случае размер для буфера будет равен 0x94, потому что компилятор вычисляет, размер который ему нужен, чтобы зарезервировать место для переменных и буферов, в соответствии с тем, как мы запрограммировали нашу функцию.

ESP остается со значением выше этого зарезервированного пространства для локальных переменных и EBP указывает на БАЗУ или ГОРИЗОНТ, который делит стек на локальные переменные, которые выше и СОХРАНЕННЫЙ EBP, АДРЕС ВОЗВРАТА и АРГУМЕНТЫ, которые ниже него.

13.png

Вот почему в функциях основанных на EBP, как только программа вызывает функцию, она сохраняет с помощью инструкции PUSH EBP значение EBP функции, которую вызывает программа, затем программа помещает ESP в EBP и это считается теперь как горизонт, вот почему в статическом представлении стека, IDA показывает 000000000 как горизонт, а выше видно знаки минус (-), а ниже знаки плюс (+).

Вот почему VAR_4 имеет значение - 00000004, потому что переменная берет EBP как БАЗУ или как 0 и математическим адресом переменной будет EBP - 4.

И ниже ARGC будет равен EBP + 8, это видно по колонки слева.

14.png

Это можно проверить это в листинге, внутри функции MAIN где программа использует переменную VAR_4, если мы сделаем правый клик на ней.

15.png

Вернемся к статическому представлению стека.

Когда мы видим здесь пустое пространство где нет смежных переменных, возможно это потому что выше есть БУФЕР (позже мы увидим случаи, в котором пустое пространство является структурой). Сейчас давайте немного поднимемся.

16.png

Здесь мы видим переменную BUF, которая является первой переменной над пустой зоной, мы делаем правый клик и выбираем ARRAY.

17.png

Мы видим, что размер МАССИВА равен 120 байт, потому что он состоит из 120 элементов по 1 байту.

18.png

Сейчас представление стека стало лучше.

19.png

Мы видим базу EBP и мы помним, что как только EBP и ESP равны это равносильно инструкции MOV EBP, ESP, затем программа вычитает из ESP значение 0x94 и ESP теперь начинает работать выше зоны для локальных переменных.

20.png

Здесь мы видим область в которой ESP будет после инструкции SUB ESP, 0x94.

Здесь в левой стороне видно значение -00000094 или что также равно ESP = EBP – 094, очевидно затем значение будет продолжать расти по мере работы программы, между другими подпрограммы и т.д., но всегда пока значение находится внутри этой функции MAIN и пока значение не выйдет из этой области, оно будет продолжать работать в области не выше 0x94 зарезервированную часть для переменных, чтобы не наступить на них.

Хорошо, однажды мы уже видели статическое представления стека, давайте отреверсим переменные, поскольку аргументы нам известны (ARGC, ARGV, и т.д.)

21.png

Мы уже видели, что VAR_4 это переменная COOKIE_SEGURIDAD или CANARY, мы видим, что инструкция считывает это значение, затем XORит его с помощью EBP и сохраняет его в стек, чтобы защитить стек от ПЕРЕПОЛНЕНИЯ, поэтому давайте переименуем эту переменную.

22.png

Равно как и в предыдущем примере API функция PRINTF не имеет символов и она не отображается, но я наблюдаю строки для неё, которые она печатает в консоли и мы видим эту функцию по адресу 0x4011B0.

23.png

И здесь внутри по адресу 0x401040 мы видим.

24.png

Так что, давайте переименуем функцию по адресу 0x4011B0 в функцию с именем PRINTF.

25.png

Давайте идти дальше.

26.png

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

27.png

Затем мы видим вызов функции GETS_S, которая является эволюцией функции GETS, но с ограничение по количеству вводимых символов, которые мы можем ввести(это такая защита), в этом случае максимальным значением будет 8, которое помещается в EAX и передается как аргумент с помощью PUSH EAX, а затем LEA получает адрес переменной BUF или BUFFER.

28.png

Конечно, если мы введём больше символом чем 8 и нажмём ENTER, функция также будет обрезать ввод и случится возврат.

Так что мы знаем, что в BUF будет помещаться имя ПОЛЬЗОВАТЕЛЯ, которое мы набрали и что оно будет иметь максимум 8 символов.

29.png

Здесь мы видим, что потом программа передаёт с помощью PUSH EDX адрес буфера снова, как аргумент к API функции STRLEN, чтобы получить длину строки, которая сейчас находится в BUF и соответствует введенному ПОЛЬЗОВАТЕЛЮ, и сохраняет длину в переменной VAR_90 через регистр-результат EAX, так что мы переименовываем VAR_90 в LEN_USER.

30.png

31.png

Синяя стрелка всегда указывает на переход назад, который может быть ЦИКЛОМ, по адресу 0x4010CE программа инициализирует счетчик LOOP VAR_84, мы также видим, что по адресу 0x4010F5 находится условный переход, который оценивает условие выхода из цикла, счетчик начинается с НУЛЯ, и он будет увеличиваться в каждом цикле, и программа выйдет из цикла, когда счетчик будет больше или равен длине, которую мы ввели в LEN_USER.

32.png

Счетчик увеличивается к концу ЦИКЛА здесь.

33.png

Здесь он помещает значение СЧЁТЧИКА в EAX увеличивая его и затем снова сохраняет.

34.png

Здесь программа помещает первый байт БУФЕРА из EBP + EDX + BUF в EAX, поскольку EBP + BUF складывается со СЧЕТЧИКОМ, который сейчас равен нуль, то он будет увеличиваться пока работает ЦИКЛ, мы видим, что здесь будет складываться все значения символов, которые я набираю, поэтому переменная VAR_88 которая начинается с нуля, будет складываться в каждом цикле HEX значения каждого символа строки БУФЕРА.

Мы видим инструкцию, которую мы до этого даже ещё не видели - MOVSX.

MOVSX И MOVZX.

Обе инструкции берут байт и помещают его в регистр, в случае с MOVZX заполняются с помощью нулей старшие байты, в то время как в случае с MOVSX учитывается знак байта, если он положительный или меньше или равен 0x7F он заполняется с помощью нулей и если он отрицательный или равен 0x80 или больше заполняется с помощью 0xFF.

MOVZX EAX, [XXXX]

Если содержимое XXXX будет равно 0x40, EAX будет равен 0x00000040.

Также например существует инструкция MOVZX EAX, CL.

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

MOVSX EAX, CL

Инструкция принимает во внимание знак байта, если CL - например равен 0x40, EAX будет равен 0x00000040 и если бы он был бы равен 0x85, в этом случае, поскольку у него отрицательный знак и это значение отрицательное EAX будет равен 0xFFFFFF85.

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

35.png

Мы видим, что ЦИКЛ - это сложение символов, мы покрасим их тем же цветом.

36.png

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

Есть люди, которые, чтобы убрать блоки с экрана, если они хотят, чтобы они их не беспокоили, группируют их, с помощью нажатия CTRL и делая клик в верхнюю панель каждого блока.

37.png

Делаем правый клик и выбираем GROUP NODES, а затем вводим имя, например LOOP.

38.png

39.png

Последнее, если Вы хотите увидеть блоки, которые спрятаны, то это можно сделать через UNGROUP NODES.

Затем программа выводит слово ПОЛЬЗОВАТЕЛЬ и говорит, что нужно ввести ПАРОЛЬ.

40.png

Затем программа вызовет снова функцию GET_S используя тот же самый буфер и тоже максимальное значение вводимых символов.

41.png

Программа может повторно использовать тот же БУФЕР для ПАРОЛЯ, в любом случае программа полностью рассчитала СУММУ HEX значений символов ПОЛЬЗОВАТЕЛЯ и она больше не будет использовать строку ПОЛЬЗОВАТЕЛЬ.

42.png

Сейчас она возьмёт ПАРОЛЬ и преобразует его в HEX как в предыдущем примере используя ATOI.

43.png

Здесь передаётся значение VALOR_PASSWORD с помощью инструкции PUSH EDX и суммируется с помощью PUSH EAX, это будут два аргумента, которые передаются в функцию по адресу 0x401010, давайте введём их.

44.png

Здесь мы видим два аргумента, очевидно, что тот, который ниже будет VALOR_PASSWORD так как передается с помощью PUSH в стек первым и второй, который кладется следующим, будет суммой, и он будет выше.

45.png

Я переименовываю их согласно этому, и затем, чтобы проверить нормально ли всё, сделайте правый щелчок по адресу SUB_0x401010 и выберите SET_TYPE.

46.png

При этом IDA попытается объявить функцию со своими аргументами, чтобы показать их в ссылке и мы переименуем также это в функцию CHECK.

47.png

И если мы пойдём к ссылке.

48.png

Мы видим, что IDA распространят имена и говорит мне, что у EAX есть СЛОЖЕНИЕ и EDX - это VALOR_PASSWORD.

И что сделает функция CHECK с этими двумя аргументами?

Мы видим, что она сравнивает их, но сначала она берет значение PASSWORD и делает с ним операцию SHL EAX, 1

49.png

Мы знаем, что SHL сдвигает биты влево, заполняя нулями те, которые исчезают на другой стороне, но в частности SHL REG, 1 - это равносильно умножению на 2.

50.png

Программа берет значение пароля, умножает его на 2 и сравнивает его с суммой символов слова ПОЛЬЗОВАТЕЛЬ.

51.png

При этом мы получаем числовое значение символа, мы можем сделать формулу, которая суммирует все символы строки pepe, которую я использую как ПОЛЬЗОВАТЕЛЬ.

52.png

Мы видим, что сумма равна 0x1AA, с другой стороны значение, которые мы ввели как пароль будет умножено на 2 перед сравнением с этим 0x1AA, поэтому правильный пароль должен быть значением, которое при умножении на два даёт нам 0x1AA.

X*2 = 0x1AA

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

Мы очищаем строку и вводим.

X = 0x1AA / 2 и ответ поступает в десятичной системе счисления, очевидно, что с помощью ATOI он переводится из десятичной системы счисления в HEX.

53.png

Если я введу ИМЯ ПОЛЬЗОВАТЕЛЯ как pepe, а ПАРОЛЬ как 213, что произойдёт?

54.png

Конечно, я вижу, когда сравнение одинаково внутри функции проверки.

55.png

Если они не равны, программа идёт в красный блок и возвращает НУЛЬ, а если они равны программа идёт в зелёный блок и возвращает ЕДИНИЦУ, давайте посмотрим, что произойдёт с этим возвращаемым значением.

56.png

Программа сохраняет его здесь, давайте переименуем это значение в FLAG_EXITO.

57.png

Так что, поскольку, если мы видим 0, то тогда пойдём к BAD REVERSER, а если AL равно 1, то идём к GOOD BOY как это и случается у нас здесь.

Мне бы очень понравилось, если бы вы отладили этот крэкми и проверили всё, что мы вместе реверсили, установив BPs и увидев значения в каждом случае до окончательной проверки.

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




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

13 12.962
yashechka

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

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

Комментарии


      1. calleng 19 авг 2024
        Статья очень хорошо написана, ясная и понятная.
        ida является очень загадочным инструментом, большинство людей, большую часть времени только слышать имя громко, но не знаю, как использовать, через текущее обучение 12 уроков, очень ясно, и основные приложения, полностью отделены, ollydbg.
        Полное переосмысление обратной методологии, недавно просочившейся в IDA Pro 9.0.
        Полная версия доступна для macOS, Windows и Linux. Коммерческое программное обеспечение, в конце концов, поддержка есть. Я освоил использование IDA, не ограничиваясь, антиотладкой, написанием плагинов, автоматизацией.
        В полной мере осознаю, что это программное обеспечение является платформой для программирования, которая разрабатывается уже более 30 лет.
        Нет ничего, что он не мог бы сделать, только вы не можете придумать применение. Позвольте мне очень восхититься этим программным обеспечением.
      2. yashechka 3 авг 2018
        Antora нравится это.
      3. Antora 3 авг 2018
        Большое спасибо за статьи! Крайне полезно.
        Скажите, пожалуйста, а нет ли ссылки, откуда скачать можно TEST_REVERSER.EXE?
        yashechka нравится это.
      4. yashechka 9 сен 2017
        Кстати, по поводу донатов. Хотя нет автоматизированной системы, я буду вести список писать, кто и сколько помог.
        DzenGURU нравится это.
      5. yashechka 5 сен 2017
        Как Вам первый ПДФ №12?
      6. yashechka 3 сен 2017
        О, Вы молодец, исправлю, перевыпустим ПДФ, с ноой редакцией.
      7. G0r 3 сен 2017
        "Программа может повторно использовать тот же БУФЕР для ПАРОЛЯ, в любом случае программа полностью рассчитала СУММУ HEX значений символов ПОЛЬЗОВАТЕЛЯ и она больше не будет использовать строку ПОЛЬЗОВАТЕЛЬ." Здесь наверно правильнее записать "HEX-сумму значений символов".
        "Здесь передаётся значение VALOR_PASSWORD с помощью инструкции PUSH EDX и суммируется с помощью PUSH EAX, это будут два аргумента, которые передаются в функцию по адресу 0x401010, давайте введём их." Тут опечатка - вместо "суммируется" надо написать "сумма" или "ASCII-сумма" (ну ASCII-сумма символов имени пользователя).
        "Мы видим, что IDA распространят имена и говорит мне, что у EAX есть СЛОЖЕНИЕ и EDX - это VALOR_PASSWORD." Здесь та же опечатка со словом "СЛОЖЕНИЕ ".
        yashechka нравится это.
      8. yashechka 3 сен 2017
        Азма, Гор, Спасибо.
      9. yashechka 2 сен 2017
        Также жду комментарии по первому пдф. Очень интересно мнение.
      10. yashechka 2 сен 2017
        DzenGURU нравится это.
      11. yashechka 2 сен 2017
        Готово, надеюсь следующая будет быстрее, хе-хе:boast:
        DzenGURU нравится это.
      12. yashechka 1 сен 2017
        Надо выкладывать, но не хочется выкладывать некачественный продукт. Я запутался в длинных предложениях, там куча существительных а они заменены на it it it it it it:hunter:Надо обязательно завтра доделать и выложить!!!
        DzenGURU нравится это.
      13. tfs_cradle 1 сен 2017
        :download:
        yashechka нравится это.