Введение в реверсинг с нуля используя 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

10 566
yashechka

yashechka
Ростовский фанат Нарвахи
Команда форума

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

Комментарии


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