В этой части, мы будем решать упражнение, которое мы оставили для решения в предыдущей части. Оно называется IDA_STRUCT.7Z. Если Вы не решили это упражнение или у Вас его нет, загрузите его отсюда.
http://ricardo.crver.net/WEB/INTRODUCCION AL REVERSING CON IDA PRO DESDE CERO/EJERCICIOS/IDA_STRUCT_RESOLVER DESPUES DE LA PARTE 26.7z
Исполняемый файл называется CONSOLEAPPLICATION4.EXE и в том же каталоге находятся символы CONSOLEAPPLICATION4.PDB.
Когда IDA загрузит файл, то сообщит Вам, что она пытается найти символы. Вы можете указать на этот файл и загрузить символы, с которыми программу станет немного яснее понимать. Но для нашего примера, я буду удалять или переименовывать файл PDB, чтобы загрузить программу без символов, что соответствует тому, с чем мы обычно имеем дело. Хотя это и выглядит сложнее, зато приближено к реальности.
Очевидно, что без символов, функция не будет определяться как MAIN. Мы можем добраться до неё, так как это консольное приложение, которое ищет аргументы ARGV или ARGC, а если их нет, то её, обычно, можно найти через поиск строк.
Если мы запустим программу, то увидим, что первое, что она сделает - это попросит нас ввести число. Здесь Вы видите строки, которые могут быть хорошим и плохим сообщением.
Щелкните на строке PLEASE ENTER YOUR NUMBER OF CHOICE:
Мы видим, что строка имеет ссылки. Помещая курсор над стрелкой или нажимая клавишу X на адресе, мы видим, что строка имеет ссылку.
Давайте перейдём по этому адресу.
Видим, что мы находимся внутри функции. Далее видно вывод строки и вызов функции для печати этой строки. Поскольку у нас нет символом, IDA не говорит нам, что функция по адресу 0x401220 - это функция PRINTF. Если мы посмотрим во внутрь этой функции.
Вы можете посмотреть во внутрь этой функции и увидеть, что внутри есть ещё несколько функций.
В PROXIMITY VIEW, в который можно войти нажав клавишу - и выйти, нажав +, мы видим, что функция 0x401220 вызывает те же самые три функции, но две функции 0x401000 и ACRT_IOB_FUNC - это функции, которые что-то делают и возвращаются назад. Они не идут к другим дочерним функциям.
Здесь, где есть стрелки, которые я добавил, видно, что блоки не идут к другим функциям. Только одна функция, которая вызывает другие функции - это функция по адресу 0x4010F0, которая вызывает две функции и первая из вызываемых, это функция VFPRINTF. Функция после своего выполнения возвращается обратно и ниже уже нет никаких других функций.
Это также можно увидеть в дизассемблерном списке. Если я посмотрю во внутрь каждой функции, я увижу то же самое.
Видно, что функция 0x401000 никуда не ведёт. Функция просто исполняет ерунду и возвращает выполнение.
И _ACRT_IOB_FUNC - это API функция. Поэтому функция не будет продолжаться??? Функция будет только инициализировать поток STDOUT, чтобы затем быть готовой печатать текст.
Другими словам, передача аргумента 1, как в нашем случае, будет инициализировать поток STDOUT.
И третья функция, которая будет вызываться, это функция по адресу 4010F0.
Функция заканчивается вызовом VFPRINTF, т.е. мы пришли к тому же, что видно и в PROXIMITY VIEW, но это занимает больше времени.
Так что давайте переименуем функцию по адресу 0x401220 в PRINTF.
Те блоки, которые заканчиваются вызовом API, как в этом случае PRINTF, я буду закрашивать в небесный цвет. Каждый будет делать это по своему вкусу.
Следующая функция по адресу 0x40109D – это конечно же функция SCANF. Если мы перейдём в неё и посмотрим в PROXIMITY VIEW, то увидим:
Здесь я нажимаю на +, чтобы увидеть спрятанные блоки.
Мы видим, что это функция SCANF.
И в этом случае, функция _ACRT_IOB_FUNC с аргументом 0 инициализирует поток STDIN.
Поэтому, мы переименовываем эту функцию в SCANF.
Мы видим что-то похожее на структуру, потому что когда Вы передаёте адрес как аргумент и затем извлекаете его и прибавляете к нему смещение для доступа к полям в каждом месте, в котором они используются, возможно это является адресом структуры.
Давайте посмотрим на ссылки для следующую функции.
Мы видим, что есть две ссылки. Если я зайду в первую.
Я вижу, что аргумент в обоих случаях является адресом, который даёт представление о структурах.
Поскольку два разных адреса производят впечатление, что они были двумя структурами одного и того же типа, мы начнем создавать единую структуру, не зная размера, не зная полей или чего-то еще. Мы будем понемногу реверсить её внутреннюю структуру.
Мы видим, что максимальное смещение, которое я нахожу до настоящего времени равно 0x14. Поэтому я буду создавать структуру этой длины. Если структура станет больше, я буду увеличивать её размер.
Для этого, я иду во вкладку STRUCTURES. Это один из способов её создания. Другой способ - это перейти в LOCAL TYPES и создать её как код в стиле C. Давайте так и сделаем.
Это немного раздражает и это не очень интуитивно. Но хорошо когда мы находимся в месте где определена структура. Здесь мы можем сделать CREATE STRUCT FROM SELECTION. Обычно мы будем создавать структуру в некоторой функции где она не определена, ничего не зная о ней.
Я знаю, что если проанализировать представление стека функции MAIN, я мог бы здесь использовать опцию CREATE STRUCT FROM SELECTION и это облегчило бы мне жизнь, но давайте возьмём наихудший случай. Представим, что мы находимся в функции очень большой программы и что мы далеко от того места, где она была определена, так что мы должны исправить их как только можем.
Здесь мы видим, что для создания структуры Вы должны нажать клавишу INS. Сделаем же это.
Я могу назначить имя, какое захочу. Назовём нашу структуру MYSTRUCT.
Здесь она была создана мной с размером 0. Сейчас я буду делать трюк. Когда я всё ещё не знаю полей или чего-то ещё и я хочу дать полю размер, сначала я нажимаю D на слове ENDS, чтобы добавить одно поле.
Здесь я добавляю поле длиной 1 байт DB. Если бы я снова нажал D, я бы переключился на слово DW и затем на DD.
Но здесь, поскольку мы не знаем, что нам нужно, мы оставляем всё так и делаем правый щелчок на структуре.
Так как я видел поле со смещением 0x14.
Таким образом, чтобы заполнить это поле с помощью DWORD, структуре нужно ещё 4 байта, поэтому я буду создавать структуру из 0x18 байт. Я буду добавлять ещё 0x17 байт к байту, который был у структуры.
Я вижу, что размер стал равен 0x18. Теперь мы оставим структуру такой.
Поскольку эта функция вызывается дважды, первый раз с адресом первой структуры типа MYSTRUCT, которую мы будем произвольно называть PEPE и второй раз с адресом второй структуры того же типа MYSTRUCT, которую мы будем называть JUAN. Внутри функции, мы будем давать общее имя, которое будет обслуживать оба случая.
В исходном коде, это выглядит так. Чтобы уточнить две переменные типа MYSTRUCT первая называется PEPE, а другая JUAN. Обе передают свой адрес как аргумент в функцию.
Так как одна и та же функция сначала будет иметь адрес первой структуры или PEPE в аргументе ARG0 и во второй раз, когда она будет вызвана она будет иметь адрес структуры JUAN, то я буду давать структуре общее имя для обоих случаев, например _STRUCT.
Если я декомпилирую функцию с помощью F5, я вижу, что результат неверный.
Я вижу, что определение переменной является простым типом INT, а не как в исходном коде как адрес структуры. Я могу это исправить так.
Это позволяет выбрать адрес структуры, в котором она находится, и здесь мы будем выбирать тип MYSTRUCT.
Очевидно, что BUF - это PEPE и здесь он получает свой адрес и передаёт его как аргумент. Давайте посмотрим BUF в представлении стека.
Поэтому структура необязательна, чтобы создать её, потому что она уже существует. Я просто должен сказать Вам, что BUF – это переменная типа MYSTRUCT. Для этого нажмите сочетание ALT + Q на переменной BUF.
и IDA назначит переменной BUF тип MYSTRUCT. Если мы поместим размер ниже, некоторые поля будут опущены, но затем можно будет увеличить структуру MYSTRUCT и она будет исправлена только здесь.(Если она не сломается конечно)
Мы переименовываем BUF в PEPE.
Мы видим здесь, что адрес PEPE передан и во втором вызове передаётся адрес VAR_44, которая также будет переменной JUAN типа MYSTRUCT, поэтому мы пойдём в представление стека и на переменной VAR_44 мы также нажмём ALT + Q.
У нас уже есть две структуры типа MYSTRUCT.
Я возвращаюсь в функцию.
Мы видим, что поле 0x10 является DWORD где оно передаётся функции SCANF, поэтому мы переходим в MYSTRUCT и в поле 0x10 мы нажимаем D до тех пор пока оно не будет иметь тип DD.
Я переименую это поле в NUMERO.
Другая запись - это поле по смещению 0x14, которое используется в цикле для удаления символа 0xA. Я назову его C.
Давайте пойдём в поле по смещению 0x14 структуры MYSTRUCT и будем нажимать D до тех пор пока не появится DWORD и давайте назовём это поле именем C.
Введение в реверсинг с нуля, используя IDA PRO. Часть 27-1.
Дата публикации 25 дек 2017
| Редактировалось 4 янв 2018