Программы на Си часто собираются вместе с LIB-файлами — архивами заранее скомпилированных объектных модулей. Во время линковки их код не меняется, за исключением абсолютных адресов, используемых в нём. Код прилинкованных библиотек способен занимать значительную часть объёма файла, а разбирать его вручную — значит впустую тратить время.
FLIRT расшифровывается как Fast Library Identification and Recognition Technology. Это технология распознавания кода статически линкованных библиотек по специальным сигнатурам. Она ищет совпадения в базе и присваивает библиотечным функциям, по своему мнению, корректные имена. Это заметно упрощает работу реверсеров — при условии, что применены подходящие сигнатуры.
IDA поставляется с набором часто используемых сигнатур. Все они лежат в папке \IDA Professional 9.1\sig
. Туда же мы будем добавлять новые сигнатуры. Дополнительные можно найти на GitHub или создать самостоятельно.
Создание сигнатур
Всё начинается с выбора конкретной библиотеки: внешней (вроде OpenSSL) или стандартной, входящей в комплект компилятора. Версию компилятора удобно выяснять с помощью Detect It Easy — там же можно посмотреть используемые строки, где нередко встречаются копирайты нужной библиотеки.
Определив предполагаемую версию, начинаем квест по поиску LIB-файлов. Старые компиляторы можно найти на Рутрекере, библиотеки нужных версий — на GitHub. Так или иначе, заполучив LIB-файлы, можно приступать к созданию сигнатур.
Для этого понадобится папка flair из IDA SDK. Аббревиатура расшифровывается как Fast Library Acquisition for Identification and Recognition — это набор инструментов для создания сигнатур. В нём есть утилиты для разных входных форматов, но мы будем использовать pcf.exe
(parsecoff), работающий только с Common Object File Format.
Для примера создадим сигнатуры для восьмой версии Visual Studio.
"C:\Program Files\IDA Professional 9.1\tools\flair\pcf.exe" LIBCMT.LIB
LIBCMT.LIB: skipped 49, total 1207
Рядом с LIB должен появиться LIBCMT.pat
, содержащий patterns — набор шаблонов. Посмотрим, что внутри:
833D........017E126803010000FF742408E8........5959EB12A1........ 1D A0AA 003D :0000 ___iscsymf ^0002 ___mb_cur_max ^0013 __isctype ^001C __pctype
833D........017E116803010000FF742408E8........5959C38B4424048B0D 00 0000 002E :0000 _isalpha ^0002 ___mb_cur_max ^0013 __isctype ^0020 __pctype ........668B04412503010000C3
Каждая строка описывает свой шаблон. Вначале идут первые 32 байта выбранной функции. Точками помечены «вариативные» байты — те, что изменяются на финальной сборке.
Далее следует 1D A0AA
— значение CRC16 для 29 (1D) байт. Оно считается от 32-го байта до первого встреченного вариативного байта. Это сделано для ускоренного сравнения функций с нашими сигнатурами. Число 003D
представляет полный размер функции.
Следующий участок задаёт ссылки на имена: :0000 ___iscsymf ^0002 ___mb_cur_max
. Начало функции (то есть она сама) должно называться ___iscsymf
, а на втором байте должна быть ссылка на ___mb_cur_max
. Этот фрагмент тоже используется при сопоставлении: если ранее успешно применена сигнатура к ___mb_cur_max
, ссылка на неё внутри ___iscsymf
подтвердит корректность этой сигнатуры.
Если функция большая, в самом конце приводится шаблон оставшейся после CRC16 части.
С паттернами разобрались — остаётся преобразовать их в формат сигнатур IDA. Для этого воспользуемся sigmake
из той же папки.
"C:\Program Files\IDA Professional 9.1\tools\flair\sigmake.exe" -n"VS8 LIBCMT" LIBCMT.pat LIBCMT.sig
Ключ -n
задаёт имя выходной сигнатуры. На самом деле SIG-файл содержит много метаданных, задаваемых разными ключами; их можно посмотреть в sigmake.txt
из старого SDK.
На выходе получаем два файла — LIBCMT.err
и LIBCMT.exc
. Оба сообщают о коллизиях, обнаруженных при сборке: ситуациях, когда одинаковые сигнатуры указывают на разные имена. Такое случается довольно часто, особенно для маленьких функций.
В начале LIBCMT.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
_memcpy 18 91E3 558BEC57568B750C8B4D108B7D088BC18BD103C63BFE76083BF80F8278010000
_memmove 18 91E3 558BEC57568B750C8B4D108B7D088BC18BD103C63BFE76083BF80F8278010000
Утилита sigmake
просит вручную разрешить конфликт: функции _memcpy
и _memmove
имеют одинаковую сигнатуру, а с учётом особенностей C++-кода у них, скорее всего, и одинаковый машинный код.
Можно выбрать одно из имён, поставив перед ним «+», или оставить всё как есть и вовсе не включать эти функции.
Когда мы закончили с расстановкой плюсов (или проигнорировали все конфликты), удаляем комментарии в начале файла и повторно запускаем sigmake
. Теперь он отрабатывает корректно и создаёт LIBCMT.sig
.
С этим файлом уже можно работать в IDA. Посмотрим, что у него внутри — воспользуемся dumpsig
:
"C:\Program Files\IDA Professional 9.1\tools\flair\dumpsig.exe" LIBCMT.sig LIBCMT.sigdump
IDA signature file dumper by Ilfak Guilfanov. Version 1.23
Signature : VS8 LIBCMT, 953 modules
Version : 10
Features : 32-bytes patterns
Processor id : 0 Intel 80x86
OS types : FFFF MSDOS WIN OS/2 NETWARE UNIX
App types : FFFF CONSOLE GRAPHICS EXE DLL DRV SINGLE-THREADED MULTI-THREADED 16BIT 32BIT 64BIT
File types : FFFFFFFF DOSEXE(OLD) DOSCOM(OLD) BIN DOSDRV NE INTELHEX MOSHEX LX LE NLM COFF PE OMF SREC ZIP OMFLIB AR LOADER ELF W32RUN AOUT PILOT DOSEXE DOSCOM AIXAR
Flags : 0000
Видим массу метаданных, которые мы не задавали при создании SIG. Заглянем в LIBCMT.sigdump
:
33:
C0:
394424087506506A0450EB0A680002000050FF742410FF742410E8:
0. 00 0000 0025 0000:_setbuf
50506A03506A0368000000:
4068........FF15........A3........C3:
0. 00 0000 001F 0000:___initconout
C068........FF15........A3........C3:
0. 00 0000 001F 0000:___initconin
Перед нами предстаёт бинарное древо, созданное ради оптимизации: в него включено сразу несколько паттернов. Любая база сигнатур работает схожим образом. Первый байт функции должен быть 33
, следующий — C0
. Дальше начинаются развилки: одна ветвь указывает на _setbuf
, две другие — на ___initconout
и ___initconin
.
Иными словами, sigmake
оптимизировал сигнатуры под быстрый поиск: взял только минимально возможный набор байт, отличающийся в наборе паттернов, и построил из них бинарное древо.
Зачастую паттерны из разных LIB объединяют в один большой PAT-файл, чтобы охватить все файлы конкретной библиотеки. Это несложно сделать скриптами, которые я здесь приводить не буду, чтобы не раздувать статью.
Отдельно отмечу возможность создавать сигнатуры из текущего IDB-файла: меню File → Produce file → Create SIG file
. Так легко переносить имена распознанных функций между двумя базами «Иды».
Заключение
Из-за ограничений памяти и копирайтов сигнатуры не могут содержать полный код оригинальных функций. Ограниченный размер сигнатуры зачастую приводит к ошибкам распознавания. Тем не менее, FLIRT зарекомендовал себя как крайне полезный инструмент. Жаль, что публичных баз для него не так много.
Источники
The IDA Pro book: The Unofficial Guide to the World’s Most Popular Disassembler (Chris Eagle)
IDA_SDK/flair77/pat.txt
IDA_SDK/flair77/sigmake.txt
vilgeforce
Lumina вроде использует просто хэши тела функций, а не формат FLIRT...
gr0grig Автор
Вы правы, убрал эту часть