Распаковываем самое простое 5/5 FSG 2.0

😁
Олдфаг
Статус
Оффлайн
Регистрация
27 Ноя 2016
Сообщения
2,091
Реакции[?]
2,025
Поинты[?]
6K
Давайте познакомимся с одним из самых странных и редких упаковщиков для Windows - FSG 2.0. Посмотрим nfo-файл, судя по нему FSG расшифровывается как (F[ast] S[mall] G[ood]). Упаковщик нацелен на обработку маленьких программ размером в 4-64 KB, таких как intro и crack.
Упаковывающая программа занимает всего-лишь 22 килобайта и не предоставляет нам никаких опций. Она без лишних слов сразу предлагает открыть нужный файл, затем показывает степень упаковки и все. Настоящий минимализм. Все 4 опции спрятаны в ini-файле, что очень похоже на UNIX (все опции - в сад). Качество упаковки чуть-чуть превосходит UPX, особенно на маленьких файлах. Из недостатков можно назвать отсутсвие поддержки:
- .NET-файлов
- DLL-библиотек
- TLS-директории
- Отложенного импорта
Но самое главное - содержит несколько подлянок с целью обломать распространенные утилиты, не до конца понимающие PE-формат и заодно усложнить сброс дампа.

- Опции упаковки
Принимают значение 1 или 0 (Да или Нет). В скобках - значение по умолчанию:
strip_overlays (1) - вырезать оверлеи.
strip_exports (0) - вырезать экспорт.
strip_delphi (1) - вырезать неиспользуемые ресурсы из Delphi-программ.
strip_version (0) - вырезать ли информацию о версии
handle_icons (0) - сжимать ли главную иконку приложения (тогда файл останется без иконки).

- Как выглядит?
Он крайне бережно относится к PE-структурам, правильно заполняя даже незначимые поля. Например, code base указывает на точку входа (1000h), а data base - на... начало PE-заголовка (000c). Чего? Да ничего. PE-заголовок расположен в самом начале файла, на смещении 0Ch от его начала. Так получилось, что по смещению 3Ch лежит поле data base, а в нем должно быть записано смещение PE-заголовка. Упаковщик всячески экономит пространство, прижимая структуры ближе друг к другу.
В файле всего 2 секции с пустыми именами:
Код:
"    rva 00001000, vsize 00014000, offset 00000000, psize 00000000
"    rva 00015000, vsize 0000C000, offset 00000200, psize 0000B665
Похоже на UPX - первая секция пуста, а вторая - нет, хотя находится почти в том же месте. Значит, распаковщик находится во 2-ой секции, а в первую - будет вестись распаковка.
Точка входа расположена в PE-заголовке (ep=00000154h, SizeOfHeaders=200h). Это вводит в ступор HTEditor, который считает, что точка входа может быть расположена только в секции. Раньше на это ловились другие программы, но теперь - нет.
В остальном упакованный файл не отличается от остальных. Вторая секция начинается с ресурсов:
Код:
resource directory    rva 00015000, vsize 00006de4
Как всегда - упакованы только текстовые строки (String) и изображения (кроме главной иконки). Информация о версии и манифест остаются нетронутыми. После ресурсов находится фрагмент, явно содержащий упакованный образ. Данные о распаковке находятся здесь же. Конец секции довершает обычная таблица импорта - всего из 2-х ф-ий - GetProcAddress и LoadLibrary. Но в импорте есть одна ловушка. Название библиотеки "KERNEL32.DLL" находится не в секции, а в конце PE-заголовка. Это обламывает почти все знаменитые тулзы - LordPE, PeTools и HIEW считают, что весь импорт обязан быть расположен только в секциях (и это мне еще говорят, что новые статьи по PE-формату не нужны, поскольку все его знают?). Исключением оказывается PeExplorer, воспринимая импорт правильно. Зато ему не нравятся ресурсы!

- Аттрибуты секций
Обе секции имеют неиспользуемые COFF-аттрибуты Code, Initdata и Uninitdata. К сожалению, аттрибута Execute нет, что приведет к неработоспособности на машинах с битом запрета исполнения.

- Метка упаковщика
Вместо Timestamp стоит 'FSG!', в результате дата принимает 01:35:02 11.09.1987 год. Возможно, именно поэтому FSG не поддерживает динамические библиотеки - для них Timestamp очень важен и его нельзя затирать. По этой сигнатуре упаковывающая программа узнает что файл уже упакован FSG. Впрочем и без нее FSG отказывается обрабатывать уже упакованные файлы - это он определяет по низкой степени сжатия.
Метка FSG управляется значением опции pe_tag. Максимальная длина текста - 10 символов. Это значит, что кроме Timestamp метка может затереть неиспользуемые поля PointerToSymbolTable и NumbersOfSymbols. По умолчанию эта опция пустая, что соответсвует тексту 'FSG!'.

- Распаковка
Поверхостный просмотр кода доказывает что упаковщик:
1) Часто использует команду XCHG, меняющей содержимое 2-х регистров.
Это осложняет понимание алгоритма работы
2) Указатель стека используется как обычный регистр:
Код:
xchg [1020628], esp

Из-за этого нельзя пологаться на него в отладчике.
3) В вызове функций пологается на относительную адресацию.
Это означает, что придется трассировать всю программу, следя за изменением регистров.
Но мы ничего не будем анализировать. Мы поступим по-хитрому. Код очень долго выполняется в заголовке, не передавая другому коду управление. Если начать внутреннюю трассировку (Trace into) с условием пока не выйдем из заголовка, отладчик будет трассировать целых 10 секунд. После этого вызывается ф-я LoadLibrary и поэтому выполнение кода вышло за границы заголовка. Сравнение образа файла с доупакованным состоянием показывает, что программа уже распакована. Поэтому теперь можно ставить бряк на OEP и вуала... Вот только точку входа сначала надо найти!
Опять воспользуемся трассировкой. Ставим условие прекращение трассировки когда управление попадет в образ программы, то есть от первой секции до последней (например EIP от 1001000h до 1021000h). Логично что это первое попадание будет точкой входа. Только теперь выбираем внешнюю трассировку (Trace over) - внутренняя будет длиться вечность, перебирая все системные функции после LoadLibrary. И вот, трассировка завершается на адресе 100739Dh - если кто еще не догадался, все экперименты походили над блокнотом.
Есть более быстрый способ найти OEP, основанный на том, что состояние стека должно быть таким же, как до упаковки. Помните мы ставили условие на выход EIP из заголовка? Вот, теперь вместо трассировки внутрь выберите внешнюю трассировку. Так вы пропустите 10 секунд, потраченные на распаковку образа.

- Признаки
Текстовые строки:
1) В поле Timestamp PE-заголовка строка 'FSG!'
2) Все секции имеют пустые имена
3) В конце заголовка
А по структуре PE-файла:
1) Всего 2 секции, первая секция пустая
2) Во второй секции импорт идет после ресурсов
3) Точка входа указывает на заголовок
4) имя библиотеки Kernel32.dll находится в конце заголовка

Существует еще множество менее распространенных упаковщиков - PEcrypt, PEpatch, PEcompact. О них я говорить не буду.
 
Сверху Снизу