Программы на Си часто собираются вместе с 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 F.L.I.R.T. Technology: In-Depth

  • IDA_SDK/flair77/pat.txt

  • IDA_SDK/flair77/sigmake.txt

Комментарии (0)


  1. vilgeforce
    22.09.2025 09:19

    Lumina вроде использует просто хэши тела функций, а не формат FLIRT...


    1. gr0grig Автор
      22.09.2025 09:19

      Вы правы, убрал эту часть