Всем привет,
При работе в IDA мне, да и, наверняка, вам тоже, часто
приходится иметь дело с приложениями, которые имеют достаточно
большой объём кода, не имеют символьной информации и, к тому же,
содержат много библиотечного кода. Зачастую, такой код нужно уметь
отличать от написанного пользователем. И, если на вход
библиотечного кода подаются только int
, void
*
, да const char *
, можно отделаться одними
лишь сигнатурами (созданные с помощью FLAIR-утилит sig-файлы). Но,
если нужны структуры, аргументы, их количество, тут без
дополнительной магии не обойдёшься В качестве примера я буду
работать с игрой для Sony Playstation 1, написанной с
использованием PSYQ v4.7
.
Дополнительная магия
Представим ситуацию: вам попалась прошивка от какой-нибудь железки. Обычный Bare-metal ROM (можно даже с RTOS). Или же ROM игры. В подобных случаях, скорее всего, при компиляции использовался какой-то SDK/DDK, у которого имеется набор LIB/H/OBJ файлов, которые вклеиваются линкером в итоговый файл.
Наш план действий будет примерно таким:
- Взять все lib/obj-файлы, и создать из них сигнатуры (или набор сигнатур). Это поможет нам отделить статически влинкованный библиотечный код.
- Взять все h-файлы и создать из них библиотеку типов. Эта библиотека хранит не только типы данных, но и информацию об именах и типах аргументов функций, в которых объявленные типы используются.
- Применить сигнатуры, чтобы у нас определились библиотечные функции и их имена.
- Применить библиотеки типов, чтобы применить прототипы функций и используемые типы данных.
Создаём sig-файлы
Для создания файла сигнатур необходимо воспользоваться набором FLAIR-утилит, доступных лицензионным пользователям IDA. Список необходимых утилит следующий:
pcf
LIB/OBJ-parser, создаёт PAT-файл из COFF-объектных файловpelf
LIB/OBJ-парсер, создаёт PAT-файл из ELF-файлов (Unix)plb
LIB/OBJ-parser, создаёт PAT-файл из OMF-объектных файловpmacho
MACH-O-парсер, создаёт PAT-файл из MACH-O-файлов (MacOS)ppsx
OBJ-парсер, создаёт PAT-файл из библиотечных файлов PSYQptmobj
OBJ-парсер, создаёт PAT-файл из библиотечных файлов Trimediasigmake
конвертирует ранее созданный PAT-файл в SIG-файл, перевариваемый IDA
В моём случае это ppsx
. Собираю bat-файл, в котором
перечисляю все lib-
и obj-
файлы, и
добавляю к каждой строке вызов ppsx
, чтобы получилось
формирование итогового PAT-файла. Получилось следующее
содержимое:
@echo offppsx -a 2MBYTE.OBJ psyq47.patppsx -a 8MBYTE.OBJ psyq47.patppsx -a LIBAPI.LIB psyq47.patppsx -a LIBC.LIB psyq47.patppsx -a LIBC2.LIB psyq47.patppsx -a LIBCARD.LIB psyq47.patppsx -a LIBCD.LIB psyq47.patppsx -a LIBCOMB.LIB psyq47.patppsx -a LIBDS.LIB psyq47.patppsx -a LIBETC.LIB psyq47.patppsx -a LIBGPU.LIB psyq47.patppsx -a LIBGS.LIB psyq47.patppsx -a LIBGTE.LIB psyq47.patppsx -a LIBGUN.LIB psyq47.patppsx -a LIBHMD.LIB psyq47.patppsx -a LIBMATH.LIB psyq47.patppsx -a LIBMCRD.LIB psyq47.patppsx -a LIBMCX.LIB psyq47.patppsx -a LIBPAD.LIB psyq47.patppsx -a LIBPRESS.LIB psyq47.patppsx -a LIBSIO.LIB psyq47.patppsx -a ashldi3.obj psyq47.patppsx -a ashrdi3.obj psyq47.patppsx -a CACHE.OBJ psyq47.patppsx -a clear_cache.obj psyq47.patppsx -a CLOSE.OBJ psyq47.patppsx -a cmpdi2.obj psyq47.patppsx -a CREAT.OBJ psyq47.patppsx -a ctors.obj psyq47.patppsx -a divdi3.obj psyq47.patppsx -a dummy.obj psyq47.patppsx -a eh.obj psyq47.patppsx -a eh_compat.obj psyq47.patppsx -a exit.obj psyq47.patppsx -a ffsdi2.obj psyq47.patppsx -a fixdfdi.obj psyq47.patppsx -a fixsfdi.obj psyq47.patppsx -a fixtfdi.obj psyq47.patppsx -a fixunsdfdi.obj psyq47.patppsx -a fixunsdfsi.obj psyq47.patppsx -a fixunssfdi.obj psyq47.patppsx -a fixunssfsi.obj psyq47.patppsx -a fixunstfdi.obj psyq47.patppsx -a fixunsxfdi.obj psyq47.patppsx -a fixunsxfsi.obj psyq47.patppsx -a fixxfdi.obj psyq47.patppsx -a floatdidf.obj psyq47.patppsx -a floatdisf.obj psyq47.patppsx -a floatditf.obj psyq47.patppsx -a floatdixf.obj psyq47.patppsx -a FSINIT.OBJ psyq47.patppsx -a gcc_bcmp.obj psyq47.patppsx -a LSEEK.OBJ psyq47.patppsx -a lshrdi3.obj psyq47.patppsx -a moddi3.obj psyq47.patppsx -a muldi3.obj psyq47.patppsx -a negdi2.obj psyq47.patppsx -a new_handler.obj psyq47.patppsx -a op_delete.obj psyq47.patppsx -a op_new.obj psyq47.patppsx -a op_vdel.obj psyq47.patppsx -a op_vnew.obj psyq47.patppsx -a OPEN.OBJ psyq47.patppsx -a PROFILE.OBJ psyq47.patppsx -a pure.obj psyq47.patppsx -a read.obj psyq47.patppsx -a shtab.obj psyq47.patppsx -a snctors.obj psyq47.patppsx -a SNDEF.OBJ psyq47.patppsx -a SNMAIN.OBJ psyq47.patppsx -a SNREAD.OBJ psyq47.patppsx -a SNWRITE.OBJ psyq47.patppsx -a trampoline.obj psyq47.patppsx -a ucmpdi2.obj psyq47.patppsx -a udiv_w_sdiv.obj psyq47.patppsx -a udivdi3.obj psyq47.patppsx -a udivmoddi4.obj psyq47.patppsx -a umoddi3.obj psyq47.patppsx -a varargs.obj psyq47.patppsx -a write.obj psyq47.patppsx -a LIBSND.LIB psyq47.patppsx -a LIBSPU.LIB psyq47.patppsx -a LIBTAP.LIB psyq47.patppsx -a LOW.OBJ psyq47.patppsx -a MCGUI.OBJ psyq47.patppsx -a MCGUI_E.OBJ psyq47.patppsx -a NOHEAP.OBJ psyq47.patppsx -a NONE3.OBJ psyq47.patppsx -a NOPRINT.OBJ psyq47.patppsx -a POWERON.OBJ psyq47.pat
LIBSN.LIB
файл имеет формат, отличный от остальных
библиотек, поэтому пришлось разложить его на OBJ-файлы утилитой
PSYLIB2.EXE
, которая входит в комплект PSYQ. Запускаем
run_47.bat
. Получаем следующий выхлоп:
2MBYTE.OBJ: skipped 0, total 18MBYTE.OBJ: skipped 0, total 1LIBAPI.LIB: skipped 0, total 89LIBC.LIB: skipped 0, total 55LIBC2.LIB: skipped 0, total 50LIBCARD.LIB: skipped 0, total 18LIBCD.LIB: skipped 0, total 51LIBCOMB.LIB: skipped 0, total 3LIBDS.LIB: skipped 0, total 36LIBETC.LIB: skipped 0, total 8LIBGPU.LIB: skipped 0, total 60LIBGS.LIB: skipped 0, total 167LIBGTE.LIB: skipped 0, total 535LIBGUN.LIB: skipped 0, total 2LIBHMD.LIB: skipped 0, total 585LIBMATH.LIB: skipped 0, total 59LIBMCRD.LIB: skipped 0, total 7LIBMCX.LIB: skipped 0, total 31LIBPAD.LIB: skipped 0, total 21LIBPRESS.LIB: skipped 0, total 7LIBSIO.LIB: skipped 0, total 4ashldi3.obj: skipped 0, total 1ashrdi3.obj: skipped 0, total 1CACHE.OBJ: skipped 0, total 1clear_cache.obj: skipped 0, total 1CLOSE.OBJ: skipped 0, total 1cmpdi2.obj: skipped 0, total 1CREAT.OBJ: skipped 0, total 1ctors.obj: skipped 0, total 0divdi3.obj: skipped 0, total 1dummy.obj: skipped 0, total 1Fatal: Illegal relocation information at file pos 0000022Deh_compat.obj: skipped 0, total 1exit.obj: skipped 0, total 1ffsdi2.obj: skipped 0, total 1fixdfdi.obj: skipped 0, total 1fixsfdi.obj: skipped 0, total 1fixtfdi.obj: skipped 0, total 0fixunsdfdi.obj: skipped 0, total 1fixunsdfsi.obj: skipped 0, total 1fixunssfdi.obj: skipped 0, total 1fixunssfsi.obj: skipped 0, total 1fixunstfdi.obj: skipped 0, total 0fixunsxfdi.obj: skipped 0, total 0fixunsxfsi.obj: skipped 0, total 0fixxfdi.obj: skipped 0, total 0floatdidf.obj: skipped 0, total 1floatdisf.obj: skipped 0, total 1floatditf.obj: skipped 0, total 0floatdixf.obj: skipped 0, total 0FSINIT.OBJ: skipped 0, total 1gcc_bcmp.obj: skipped 0, total 1LSEEK.OBJ: skipped 0, total 1lshrdi3.obj: skipped 0, total 1moddi3.obj: skipped 0, total 1muldi3.obj: skipped 0, total 1negdi2.obj: skipped 0, total 1Fatal: Illegal relocation information at file pos 0000013Dop_delete.obj: skipped 0, total 1op_new.obj: skipped 0, total 1op_vdel.obj: skipped 0, total 1op_vnew.obj: skipped 0, total 1OPEN.OBJ: skipped 0, total 1PROFILE.OBJ: skipped 0, total 1pure.obj: skipped 0, total 1Fatal: Unknown record type 60 at 0000015Fshtab.obj: skipped 0, total 0Fatal: Unknown record type 60 at 000000EESNDEF.OBJ: skipped 0, total 0SNMAIN.OBJ: skipped 0, total 1SNREAD.OBJ: skipped 0, total 1SNWRITE.OBJ: skipped 0, total 1trampoline.obj: skipped 0, total 0ucmpdi2.obj: skipped 0, total 1udiv_w_sdiv.obj: skipped 0, total 1udivdi3.obj: skipped 0, total 1udivmoddi4.obj: skipped 0, total 1umoddi3.obj: skipped 0, total 1varargs.obj: skipped 0, total 1Fatal: Unknown record type 60 at 00000160LIBSND.LIB: skipped 0, total 223LIBSPU.LIB: skipped 0, total 126LIBTAP.LIB: skipped 0, total 1LOW.OBJ: skipped 0, total 1Fatal: can't find symbol F003MCGUI_E.OBJ: skipped 0, total 1NOHEAP.OBJ: skipped 0, total 1NONE3.OBJ: skipped 0, total 1NOPRINT.OBJ: skipped 0, total 1POWERON.OBJ: skipped 0, total 1
Видим некоторое количество ошибок парсинга, но, в тех файлах всего 1 сигнатура (total 1), поэтому, думаю, что это не критично. Далее преобразовываем PAT-файл в SIG-файл:
sigmake -n"PsyQ v4.7" psyq47.pat psyq47.sigpsyq47.sig: modules/leaves: 1345/2177, COLLISIONS: 21See the documentation to learn how to resolve collisions.
В итоге получаем следующий список файлов:
psyq47.err
его не трогаемpsyq47.exc
его нужно будет отредактироватьpsyq47.pat
его тоже не трогаем
Открываем на редактирование .exc
-файл. Видим:
;--------- (delete these lines to allow sigmake to read this file); add '+' at the start of a line to select a module; add '-' if you are not sure about the selection; do nothing if you want to exclude all modules
Если удалить ---------
, всё, что содержится в файле
ниже, будет учитываться. Давайте взглянем на то, что там есть. Вот
пример:
CdPosToInt 60 A21C 0000839001008690022903008010050021104500401002000F00633021104300DsPosToInt 60 A21C 0000839001008690022903008010050021104500401002000F00633021104300
Видим, что две функции имеют одну и ту же сигнатуру, и нам нужно
выбрать, какую из них использовать. Для этого слева, рядом с именем
нужной функции ставим +
. Я выбираю первую. То же самое
повторяем с остальными строками.
В итоге, если всё сделано правильно, получаем SIG-файл. Его нужно положить в соответствующую папку в каталоге установка IDA.
Создаём til-файлы
Эти файлы нужны для хранения информации о типах, об аргументах
функций и т.п. По-умолчанию. Создаются они с помощью утилиты
tilib
, которую необходимо положить в каталог с Идой
(ей, почему-то, нужен ida.hlp
файл).
Данной утилите нужно скормить include-файлы вашего SDK/DDK. При том парсинг этой утилитой отличается от такового средством "Parse C header file..." в самой IDA. Вот описание из readme:
Its functionality overlaps with "Parse C header file..." from IDA Pro.
However, this utility is easier to use and provides more control
over the output. Also, it can handle the preprocessor symbols, while
the built-in command ignores them.
У этой утилиты есть один нюанс: она по умолчанию использует
режим, когда символы заманглены, либо имеют стоящее в начале имени
нижнее подчёркивание. В случае работы со статически влинкованным
кодом этот режим нужно выключить флагом -Gn
.
По-умолчанию, данная утилита принимает на вход только один include-файл. Если же файлов много, нужно соорудить include-файл следующего содержания:
#include "header1.h"#include "header2.h"#include "header3.h"// ...
Этот файл передаётся с помощью флага -hFileName.h
.
Далее, передаём путь поиска остальных header-файлов и получаем
следующую командую строку:
tilib -c -Gn -I. -hpsyq47.h psyq47.til
На выходе получаем til-файл, пригодный для использования. Кладём
его в соответствующий каталог IDA: sig\mips
.
Проверяем результат
Закидываем ROM-файл в IDA, дожидаемся окончания анализа. Далее,
необходимо указать компилятор. Для этого заходим в
Options
->Compiler
:
Теперь просто меняем Unknown
на GNU
C++
(в случае PSX). Остальное оставляем как есть:
Теперь жмём Shift+F5
(либо меню
View
->Open
subviews
->Signatures
), жмём
Insert
и выбираем нужный файл сигнатур:
Жмём OK
и ждём, пока применяются сигнатуры (у меня
получилось 482 распознанных функции).
Далее необходимо применить библиотеку типов (til-файл). Для
этого жмём Shift+F11
(либо
View
->Open subviews
->Type
libraries
) и понимаем, что IDA не может определить
компилятор (не смотря на то, что мы его уже указали):
Но это нам всё равно не помешает выбрать til-файл (всё так же,
через Insert
):
Получаем то, что так хотели:
Теперь декомпилятор успешно подхватывает информацию о типах, и выхлоп становится куда лучше:
P.S.
Надеюсь, эта информация окажется для вас полезной. Удачного реверс-инжиниринга!