IDA Pro: работа с библиотечным кодом (не WinAPI)

Всем привет,

hecld4kn_msnzxx5gh28gljqdoe.png

При работе в IDA мне, да и, наверняка, вам тоже, часто приходится иметь дело с приложениями, которые имеют достаточно большой объём кода, не имеют символьной информации и, к тому же, содержат много библиотечного кода. Зачастую такой код нужно уметь отличать от написанного пользователем. И, если на вход библиотечного кода подаются только int, void * да const char *, можно отделаться одними лишь сигнатурами (созданные с помощью FLAIR-утилит sig-файлы). Но, если нужны структуры, аргументы, их количество, тут без дополнительной магии не обойдёшься… В качестве примера я буду работать с игрой для Sony Playstation 1, написанной с использованием PSYQ v4.7.

Представим ситуацию: вам попалась прошивка от какой-нибудь железки. Обычный Bare-metal ROM (можно даже с RTOS). Или же ROM игры. В подобных случаях, скорее всего, при компиляции использовался какой-то SDK/DDK, у которого имеется набор LIB/H/OBJ файлов, которые вклеиваются линкером в итоговый файл.

Наш план действий будет примерно таким:


  1. Взять все lib/obj-файлы, и создать из них сигнатуры (или набор сигнатур). Это поможет нам отделить статически влинкованный библиотечный код.
  2. Взять все h-файлы и создать из них библиотеку типов. Эта библиотека хранит не только типы данных, но и информацию об именах и типах аргументов функций, в которых объявленные типы используются.
  3. Применить сигнатуры, чтобы у нас определились библиотечные функции и их имена.
  4. Применить библиотеки типов, чтобы применить прототипы функций и используемые типы данных.


Создаём 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-файл из библиотечных файлов PSYQ
  • ptmobj — OBJ-парсер, создаёт PAT-файл из библиотечных файлов Trimedia
  • sigmake — конвертирует ранее созданный PAT-файл в SIG-файл, перевариваемый IDA

В моём случае это ppsx. Собираю bat-файл, в котором перечисляю все lib- и obj-файлы, и добавляю к каждой строке вызов ppsx, чтобы получилось формирование итогового PAT-файла. Получилось следующее содержимое:


run_47.bat
@echo off

ppsx -a 2MBYTE.OBJ psyq47.pat
ppsx -a 8MBYTE.OBJ psyq47.pat
ppsx -a LIBAPI.LIB psyq47.pat
ppsx -a LIBC.LIB psyq47.pat
ppsx -a LIBC2.LIB psyq47.pat
ppsx -a LIBCARD.LIB psyq47.pat
ppsx -a LIBCD.LIB psyq47.pat
ppsx -a LIBCOMB.LIB psyq47.pat
ppsx -a LIBDS.LIB psyq47.pat
ppsx -a LIBETC.LIB psyq47.pat
ppsx -a LIBGPU.LIB psyq47.pat
ppsx -a LIBGS.LIB psyq47.pat
ppsx -a LIBGTE.LIB psyq47.pat
ppsx -a LIBGUN.LIB psyq47.pat
ppsx -a LIBHMD.LIB psyq47.pat
ppsx -a LIBMATH.LIB psyq47.pat
ppsx -a LIBMCRD.LIB psyq47.pat
ppsx -a LIBMCX.LIB psyq47.pat
ppsx -a LIBPAD.LIB psyq47.pat
ppsx -a LIBPRESS.LIB psyq47.pat
ppsx -a LIBSIO.LIB psyq47.pat
ppsx -a ashldi3.obj psyq47.pat
ppsx -a ashrdi3.obj psyq47.pat
ppsx -a CACHE.OBJ psyq47.pat
ppsx -a clear_cache.obj psyq47.pat
ppsx -a CLOSE.OBJ psyq47.pat
ppsx -a cmpdi2.obj psyq47.pat
ppsx -a CREAT.OBJ psyq47.pat
ppsx -a ctors.obj psyq47.pat
ppsx -a divdi3.obj psyq47.pat
ppsx -a dummy.obj psyq47.pat
ppsx -a eh.obj psyq47.pat
ppsx -a eh_compat.obj psyq47.pat
ppsx -a exit.obj psyq47.pat
ppsx -a ffsdi2.obj psyq47.pat
ppsx -a fixdfdi.obj psyq47.pat
ppsx -a fixsfdi.obj psyq47.pat
ppsx -a fixtfdi.obj psyq47.pat
ppsx -a fixunsdfdi.obj psyq47.pat
ppsx -a fixunsdfsi.obj psyq47.pat
ppsx -a fixunssfdi.obj psyq47.pat
ppsx -a fixunssfsi.obj psyq47.pat
ppsx -a fixunstfdi.obj psyq47.pat
ppsx -a fixunsxfdi.obj psyq47.pat
ppsx -a fixunsxfsi.obj psyq47.pat
ppsx -a fixxfdi.obj psyq47.pat
ppsx -a floatdidf.obj psyq47.pat
ppsx -a floatdisf.obj psyq47.pat
ppsx -a floatditf.obj psyq47.pat
ppsx -a floatdixf.obj psyq47.pat
ppsx -a FSINIT.OBJ psyq47.pat
ppsx -a gcc_bcmp.obj psyq47.pat
ppsx -a LSEEK.OBJ psyq47.pat
ppsx -a lshrdi3.obj psyq47.pat
ppsx -a moddi3.obj psyq47.pat
ppsx -a muldi3.obj psyq47.pat
ppsx -a negdi2.obj psyq47.pat
ppsx -a new_handler.obj psyq47.pat
ppsx -a op_delete.obj psyq47.pat
ppsx -a op_new.obj psyq47.pat
ppsx -a op_vdel.obj psyq47.pat
ppsx -a op_vnew.obj psyq47.pat
ppsx -a OPEN.OBJ psyq47.pat
ppsx -a PROFILE.OBJ psyq47.pat
ppsx -a pure.obj psyq47.pat
ppsx -a read.obj psyq47.pat
ppsx -a shtab.obj psyq47.pat
ppsx -a snctors.obj psyq47.pat
ppsx -a SNDEF.OBJ psyq47.pat
ppsx -a SNMAIN.OBJ psyq47.pat
ppsx -a SNREAD.OBJ psyq47.pat
ppsx -a SNWRITE.OBJ psyq47.pat
ppsx -a trampoline.obj psyq47.pat
ppsx -a ucmpdi2.obj psyq47.pat
ppsx -a udiv_w_sdiv.obj psyq47.pat
ppsx -a udivdi3.obj psyq47.pat
ppsx -a udivmoddi4.obj psyq47.pat
ppsx -a umoddi3.obj psyq47.pat
ppsx -a varargs.obj psyq47.pat
ppsx -a write.obj psyq47.pat
ppsx -a LIBSND.LIB psyq47.pat
ppsx -a LIBSPU.LIB psyq47.pat
ppsx -a LIBTAP.LIB psyq47.pat
ppsx -a LOW.OBJ psyq47.pat
ppsx -a MCGUI.OBJ psyq47.pat
ppsx -a MCGUI_E.OBJ psyq47.pat
ppsx -a NOHEAP.OBJ psyq47.pat
ppsx -a NONE3.OBJ psyq47.pat
ppsx -a NOPRINT.OBJ psyq47.pat
ppsx -a POWERON.OBJ psyq47.pat

LIBSN.LIB файл имеет формат, отличный от остальных библиотек, поэтому пришлось разложить его на OBJ-файлы утилитой PSYLIB2.EXE, которая входит в комплект PSYQ. Запускаем run_47.bat. Получаем следующий выхлоп:


run_47.bat output
2MBYTE.OBJ: skipped 0, total 1
8MBYTE.OBJ: skipped 0, total 1
LIBAPI.LIB: skipped 0, total 89
LIBC.LIB: skipped 0, total 55
LIBC2.LIB: skipped 0, total 50
LIBCARD.LIB: skipped 0, total 18
LIBCD.LIB: skipped 0, total 51
LIBCOMB.LIB: skipped 0, total 3
LIBDS.LIB: skipped 0, total 36
LIBETC.LIB: skipped 0, total 8
LIBGPU.LIB: skipped 0, total 60
LIBGS.LIB: skipped 0, total 167
LIBGTE.LIB: skipped 0, total 535
LIBGUN.LIB: skipped 0, total 2
LIBHMD.LIB: skipped 0, total 585
LIBMATH.LIB: skipped 0, total 59
LIBMCRD.LIB: skipped 0, total 7
LIBMCX.LIB: skipped 0, total 31
LIBPAD.LIB: skipped 0, total 21
LIBPRESS.LIB: skipped 0, total 7
LIBSIO.LIB: skipped 0, total 4
ashldi3.obj: skipped 0, total 1
ashrdi3.obj: skipped 0, total 1
CACHE.OBJ: skipped 0, total 1
clear_cache.obj: skipped 0, total 1
CLOSE.OBJ: skipped 0, total 1
cmpdi2.obj: skipped 0, total 1
CREAT.OBJ: skipped 0, total 1
ctors.obj: skipped 0, total 0
divdi3.obj: skipped 0, total 1
dummy.obj: skipped 0, total 1
Fatal: Illegal relocation information at file pos 0000022D
eh_compat.obj: skipped 0, total 1
exit.obj: skipped 0, total 1
ffsdi2.obj: skipped 0, total 1
fixdfdi.obj: skipped 0, total 1
fixsfdi.obj: skipped 0, total 1
fixtfdi.obj: skipped 0, total 0
fixunsdfdi.obj: skipped 0, total 1
fixunsdfsi.obj: skipped 0, total 1
fixunssfdi.obj: skipped 0, total 1
fixunssfsi.obj: skipped 0, total 1
fixunstfdi.obj: skipped 0, total 0
fixunsxfdi.obj: skipped 0, total 0
fixunsxfsi.obj: skipped 0, total 0
fixxfdi.obj: skipped 0, total 0
floatdidf.obj: skipped 0, total 1
floatdisf.obj: skipped 0, total 1
floatditf.obj: skipped 0, total 0
floatdixf.obj: skipped 0, total 0
FSINIT.OBJ: skipped 0, total 1
gcc_bcmp.obj: skipped 0, total 1
LSEEK.OBJ: skipped 0, total 1
lshrdi3.obj: skipped 0, total 1
moddi3.obj: skipped 0, total 1
muldi3.obj: skipped 0, total 1
negdi2.obj: skipped 0, total 1
Fatal: Illegal relocation information at file pos 0000013D
op_delete.obj: skipped 0, total 1
op_new.obj: skipped 0, total 1
op_vdel.obj: skipped 0, total 1
op_vnew.obj: skipped 0, total 1
OPEN.OBJ: skipped 0, total 1
PROFILE.OBJ: skipped 0, total 1
pure.obj: skipped 0, total 1
Fatal: Unknown record type 60 at 0000015F
shtab.obj: skipped 0, total 0
Fatal: Unknown record type 60 at 000000EE
SNDEF.OBJ: skipped 0, total 0
SNMAIN.OBJ: skipped 0, total 1
SNREAD.OBJ: skipped 0, total 1
SNWRITE.OBJ: skipped 0, total 1
trampoline.obj: skipped 0, total 0
ucmpdi2.obj: skipped 0, total 1
udiv_w_sdiv.obj: skipped 0, total 1
udivdi3.obj: skipped 0, total 1
udivmoddi4.obj: skipped 0, total 1
umoddi3.obj: skipped 0, total 1
varargs.obj: skipped 0, total 1
Fatal: Unknown record type 60 at 00000160
LIBSND.LIB: skipped 0, total 223
LIBSPU.LIB: skipped 0, total 126
LIBTAP.LIB: skipped 0, total 1
LOW.OBJ: skipped 0, total 1
Fatal: can't find symbol F003
MCGUI_E.OBJ: skipped 0, total 1
NOHEAP.OBJ: skipped 0, total 1
NONE3.OBJ: skipped 0, total 1
NOPRINT.OBJ: skipped 0, total 1
POWERON.OBJ: skipped 0, total 1

Видим некоторое количество ошибок парсинга, но в тех файлах всего 1 сигнатура (total 1), поэтому думаю, что это не критично. Далее преобразовываем PAT-файл в SIG-файл:

sigmake -n"PsyQ v4.7" psyq47.pat psyq47.sig
psyq47.sig: modules/leaves: 1345/2177, COLLISIONS: 21
See 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 0000839001008690022903008010050021104500401002000F00633021104300
DsPosToInt                                          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, дожидаемся окончания анализа. Далее, необходимо указать компилятор. Для этого заходим в OptionsCompiler:

t-jfp65zrit1i_wbxx7bbw78oqu.png

Теперь просто меняем Unknown на GNU C++ (в случае PSX). Остальное оставляем как есть:

6cwg-wexjqpdhcvrv1hrac7ezrc.png

Теперь жмём Shift+F5 (либо меню ViewOpen subviewsSignatures), жмём Insert и выбираем нужный файл сигнатур:

fi4dyz4zgsly2w8cdvpsano2zwu.png

Жмём OK и ждём, пока применяются сигнатуры (у меня получилось 482 распознанных функции).

ndu9cqvhbv5dfi007gh_kl1b6w8.png

Далее, необходимо применить библиотеку типов (til-файл). Для этого жмём Shift+F11 (либо ViewOpen subviewsType libraries) и понимаем, что IDA не может определить компилятор (несмотря на то, что мы его уже указали):

727o6mcoljgopyivafqot8sgsnw.png

Но это нам всё равно не помешает выбрать til-файл (всё так же, через Insert):

3hx-5ypuusvkjzan-3onlydma0k.png

Получаем то, что так хотели:

hecld4kn_msnzxx5gh28gljqdoe.png

Теперь декомпилятор успешно подхватывает информацию о типах, и выхлоп становится куда лучше:

z0ifcqkb-u5l_rulv61qie3q6va.png


P.S.

Надеюсь, эта информация окажется для вас полезной. Удачного реверс-инжиниринга!

© Habrahabr.ru