Сергей Попов

Администратор
30.12.2015
6 184
6 946
Специализация
  1. OSINT
  2. Веб-безопасность
Статус верификации
  1. ✓ Verified
Латунно-стальной сортировочный механизм с тремя каналами на тёмной матовой поверхности. Полупрозрачный блок с микросхемами скользит по желобу в конусе тёплого света.


За последний квартал через пайплайн триажа одной TI-команды прошло больше 12 тысяч PE-файлов. Ручного реверса потребовали 340. Остальное отсеялось автоматически: легитимные инсталляторы, известные семейства по imphash, дубликаты. Без автоматизированного анализа PE-файлов этот объём убивает аналитика за пару недель - проверено на собственной шкуре. Ниже - конкретный pipeline, который собирается за день из открытых инструментов, работает офлайн и масштабируется от десятка семплов до потока в тысячи бинарников.

Место триажа в цепочке malware-анализа​

Триаж вредоносных файлов - сортировка, а не анализ. Задача - за миллисекунды рассортировать бинарник: чистый, подозрительный, вредоносный, требует ручного разбора. В цепочке DFIR и Threat Intelligence триаж PE-бинарников стоит между получением семпла и глубоким реверсом в дизассемблере. Подробнее - в нашем подробном разборе бинарный анализ уязвимостей.

Полная цепочка:
  1. Получение семпла - из CAPE Sandbox, EDR-детекта, почтового шлюза, ручного сбора с хоста
  2. Автоматизированный триаж - PE header analysis, энтропия секций, YARA, imphash-lookup
  3. Решение - drop (чистый), escalate (подозрительный), analyze (требует ручного реверса)
  4. Глубокий анализ - дизассемблер, дебаггер, декомпилятор, динамическое исполнение
Триаж экономит 80–90% времени аналитика. Из 200 файлов в очереди 150 - NSIS-инсталляторы, ещё 30 - известные UPX-packed майнеры с совпадающим imphash. Ручного реверса требуют только оставшиеся 20.

Требования к окружению​

Для воспроизведения описанных шагов:
  • ОС: Linux (Ubuntu 22.04+) или Windows 10/11 - скрипты кроссплатформенные
  • Python: 3.9+
  • Библиотеки: pefile (2023.2.7+), lief (0.14+), yara-python (4.3+)
  • RAM: 4 ГБ минимум, 8 ГБ рекомендуется при обработке >1000 файлов за сессию
  • Сеть: не требуется - весь pipeline работает offline
  • Дополнительно: Detect-It-Easy (DiE) 3.09+ для визуальной верификации

PE header analysis: автоматизированный разбор заголовков​

PE-заголовок - первое, что читает triage-скрипт. Из заголовка за миллисекунды вытягиваются десятки параметров, каждый из которых может быть маркером аномалии. Статический анализ PE-заголовков не требует выполнения бинарника и безопасен на аналитической станции без песочницы.

DOS Header и поле e_lfanew​

Значение e_lfanew по смещению 0x3C указывает на начало PE-сигнатуры. В нормальном бинарнике оно лежит в диапазоне 0x40–0x200. Если e_lfanew > 0x400 - между DOS-заглушкой и PE-заголовком спрятан аномально большой блок данных. Малварь использует это пространство для хранения зашифрованного payload или шеллкода. Значение e_lfanew < 0x40 физически невозможно - DOS header занимает 64 байта. Такие файлы либо побиты, либо crafted для эксплуатации парсеров.

Проверка через pefile - обращение к pe.DOS_HEADER.e_lfanew. Выходит за пределы допустимого диапазона - файл получает повышенный score подозрительности.

COFF File Header: маркеры аномалий​

Ключевые поля для триажа бинарников:

Machine - 0x14C (i386) или 0x8664 (AMD64). Экзотика вроде MIPS или ARM в десктопной малвари - маркер нестандартного компилятора или целенаправленной обфускации.

TimeDateStamp - метка времени компиляции. Значение из будущего или из 1970-х - типичный признак тамперинга. Многие билдеры малвари зануляют или рандомизируют это поле, чтобы затруднить атрибуцию.

NumberOfSections - Windows PE loader исторически поддерживает до 96 секций (точный лимит зависит от версии ОС и размера SizeOfOptionalHeader). Файл с одной-двумя секциями при размере >1 МБ подозрителен: вероятна упаковка. Файл с >20 секциями - возможно артефакт обфускатора или протектора.

Characteristics - флаг [URL='https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#characteristics']IMAGE_FILE_DLL[/URL] (0x2000) в файле с расширением .exe - аномалия. Отсутствие IMAGE_FILE_EXECUTABLE_IMAGE - файл не является валидным PE для выполнения.

Optional Header и точка входа​

Magic - 0x10B для PE32, 0x20B для PE32+. Несоответствие Magic и Machine - явная аномалия.

AddressOfEntryPoint - точка входа, указывающая внутрь секции с данными (не .text) или в последнюю секцию, - классический маркер пакера. UPX ставит entry point в секцию .UPX1. Кастомные пакеры часто лепят новую секцию в конец и перенаправляют EP туда.

SizeOfImage - значение, не выровненное по SectionAlignment, нарушает спецификацию PE-формата. Загрузчик Windows в ряде версий это проглатывает, но аналитически это маркер манипуляции.

Subsystem - IMAGE_SUBSYSTEM_WINDOWS_CUI (консольное приложение) у файла без консольного вывода может указывать на утилиту, запускаемую скрытно.

Библиотека pefile (активно поддерживается, последний релиз 2023, 1.8k+ звёзд на GitHub) даёт доступ ко всему перечисленному: pe.FILE_HEADER.TimeDateStamp возвращает Unix-timestamp, pe.OPTIONAL_HEADER.AddressOfEntryPoint - RVA точки входа. Чтобы определить, в какой секции находится EP, достаточно пройтись по pe.sections и сверить VirtualAddress + Misc_VirtualSize каждой секции с RVA.

LIEF работает быстрее на больших объёмах и умеет парсить не только PE, но и ELF с Mach-O. Для чистого PE-триажа библиотеки взаимозаменяемы, но LIEF удобнее, когда нужно залезть в overlay и ресурсы.

Энтропия секций и обнаружение упаковщиков PE​

Энтропия Шеннона - главный числовой индикатор упаковки или шифрования. Для каждой секции PE-файла считается значение от 0 (все байты одинаковые) до 8 (максимальная случайность - зашифрованные или сжатые данные).

Пороговые значения энтропии​

  • < 1.0 - секция забита нулями или повторяющимися данными (.bss, неинициализированные данные)
  • 4.0–6.5 - нормальный скомпилированный код (.text) или данные (.rdata, .data)
  • 6.8–7.2 - серая зона: может быть сжатый ресурс (PNG, ZIP внутри PE) или упакованный код
  • > 7.2 - с высокой вероятностью шифрование или сильная компрессия. UPX-packed секции дают ~7.0–7.4, кастомные пакеры с AES - 7.8–7.99
В pefile метод section.get_entropy() считает энтропию Шеннона) по байтам секции. Но одна только энтропия для вердикта недостаточна - сжатые ресурсы (иконки, манифесты) тоже дают высокие значения. Энтропию надо комбинировать с анализом имени секции, её флагов и числа импортов.

Имена секций и сигнатуры пакеров​

Стандартные имена секций MSVC-бинарника: .text, .rdata, .data, .rsrc, .reloc. Отклонения:
  • .UPX0, .UPX1 - UPX
  • .nsp0, .nsp1 - NsPack
  • .aspack, .adata - ASPack
  • .themida - Themida/WinLicense
  • Произвольные имена из случайных символов - кастомный пакер
  • Пустое имя секции (8 нулевых байт) - частый артефакт обфускаторов
Detect-It-Easy (DiE, активно поддерживается, стабильный релиз 3.09, 7k+ звёзд на GitHub) решает задачу обнаружения упаковщиков PE комплексно: база сигнатур покрывает более 2000 компиляторов, пакеров и протекторов. В автоматизированном pipeline DiE вызывается из командной строки - diec --json sample.exe - и отдаёт структурированный JSON с детектами.

Сравнение инструментов реверс-инженера для обнаружения упаковщиков​

КритерийDetect-It-Easypefile + entropyYARA PE module
База сигнатур пакеров2000+ сигнатурНет (только метрики)Пользовательские правила
Скорость на 1 файл~200 мс~50 мс~30 мс
Batch-обработкаCLI с JSON-выводомНативно из PythonНативно из Python
КастомизацияLua-скриптыПолная (Python API)Полная (YARA DSL)
Когда использоватьВизуальная верификация, единичный анализPipeline, массовый скринингPipeline, matching по паттернам
Когда НЕ использоватьПоток >10k файлов (медленно)Нужен детект конкретного пакераНет готовых правил под задачу

YARA-правила для автоматизированного анализа PE-файлов​

YARA (поддерживается VirusTotal, стабильный релиз 4.5, 8k+ звёзд на GitHub) - индустриальный стандарт для pattern matching. Модуль pe даёт доступ к структурированным данным PE-файла: заголовкам, секциям, импортам, экспортам, ресурсам и overlay.

Аномальная точка входа - один из самых надёжных маркеров модификации бинарника. Правило, детектирующее EP вне первой секции:
Код:
import "pe"

rule suspicious_entrypoint {
  condition:
    pe.entry_point < pe.sections[0].raw_data_offset or
    pe.entry_point > pe.sections[0].raw_data_offset +
      pe.sections[0].raw_data_size
}
Срабатывает, когда точка входа (как raw file offset) находится за пределами raw-данных первой секции. Нюанс: pe.entry_point в YARA - это смещение в файле, а не RVA. Правило может давать false positive, если .text не первая секция. Для более точного детекта пакеров стоит проверять попадание EP в последнюю секцию у файлов с >2 секциями.

Минимальный набор импортов - ещё один маркер. Если весь Import Address Table сводится к LoadLibraryA + GetProcAddress, бинарник разрешает остальные API динамически. Типичная картина для пакеров и малвари:
Код:
import "pe"

rule dynamic_api_resolution {
  condition:
    pe.number_of_imported_functions < 5 and
    pe.imports("kernel32.dll", "GetProcAddress") and
    pe.imports("kernel32.dll", "LoadLibraryA")
}
Комбинированные условия работают ещё точнее: высокая энтропия + малое число импортов. Модуль math в YARA позволяет считать энтропию прямо в правиле через math.entropy(offset, size), связывая её с данными из PE-модуля.

Ограничения YARA в статическом триаже​

YARA работает с файлом как с набором байтов и структур - она не выполняет бинарник:
  • Динамическая распаковка невидима для YARA
  • Self-modifying код не анализируется
  • Многослойная упаковка (пакер поверх пакера) детектируется только по внешнему слою
  • False positive на легитимных пакованных приложениях (игры, DRM-защищённый софт)
Для дальнейшего разбора семплов, прошедших YARA-фильтр как подозрительные, нужен динамический анализ - CAPE Sandbox или ручной дебаг. YARA - фильтр первого эшелона, не замена песочнице.

Импорты как поведенческий отпечаток бинарника​

Таблица импортов - профиль поведения программы до её запуска. По импортам без выполнения кода видно: работает ли файл с сетью, создаёт ли процессы, лезет ли в реестр, инжектит ли код в чужие процессы.

Imphash и кластеризация семплов​

Import hash (imphash) - MD5-хеш нормализованного списка импортированных функций. Два бинарника с одинаковым imphash с высокой вероятностью собраны из одного исходника или одним билдером. Это позволяет группировать семплы одного семейства, находить связи между кампаниями и фильтровать дубликаты в потоке.

В pefile вычисление - вызов pe.get_imphash(). Результат сравнивается с базой известных хешей (собственной или публичной, из репозиториев MISP). Совпадение с known-bad - немедленный escalation. Совпадение с known-good (imphash легитимного notepad.exe, calc.exe) - понижение score.

Подозрительные паттерны API​

Набор импортов, указывающий на конкретное вредоносное поведение:

Паттерн импортовВероятное поведение
[URL='https://attack.mitre.org/techniques/T1055/']VirtualAllocEx[/URL] + WriteProcessMemory + CreateRemoteThreadProcess injection (T1055, Defense Evasion / Privilege Escalation)
CreateToolhelp32Snapshot + Process32First/NextПеречисление процессов перед injection
RegSetValueExA/W с ключами RunPersistence через автозагрузку
InternetOpenA + HttpSendRequestAHTTP-based C2 коммуникация
CryptEncrypt + FindFirstFile + MoveFileExШифрование файлов (ransomware-паттерн)
NtUnmapViewOfSection + VirtualAllocExProcess hollowing (T1055.012)

Одно наличие подозрительного импорта - не вердикт. VirtualAllocEx используют легитимные программы: дебаггеры, антивирусы. Triage-скрипт должен учитывать комбинации и контекст: VirtualAllocEx + WriteProcessMemory + CreateRemoteThread в бинарнике с высокой энтропией, малым числом секций и аномальным EP - это escalation, а не drop.

Rich Header​

Rich header - недокументированная структура, которую добавляет линкер Microsoft. Содержит информацию о версиях компилятора и линкера, использованных при сборке. Малварь, собранная на MinGW, Go или Rust, Rich header не содержит. Легитимные бинарники Microsoft - содержат практически всегда.

Аномалии Rich header: обнулённый или отсутствующий Rich header в файле, который позиционирует себя как продукт Microsoft. Или наоборот - Rich header с версиями MSVC 2008 в файле с timestamp 2025 года. Через pefile доступно обращение к pe.RICH_HEADER, где каждая запись содержит compid - пару (product ID, version, object count).

Installer framework detection: фильтрация шума в потоке​

Значительная доля PE-файлов в потоке - легитимные инсталляторы: NSIS, InnoSetup, InstallShield, WiX. Их детектирование на этапе триажа экономит десятки минут аналитического времени.

Маркеры основных фреймворков:
  • NSIS - строки Nullsoft и NSIS в overlay, секция .ndata, ресурс с типом NSIS
  • InnoSetup - строка Inno Setup в overlay, характерная структура setup.bin внутри
  • InstallShield - строки InstallShield, ресурсы с типом RCData определённой структуры
  • WiX - встроенный MSI-файл, строки Windows Installer XML
  • PyInstaller - строка MEI в overlay, ресурс с иконкой Python
DiE детектирует все перечисленные фреймворки из коробки. В кастомном pipeline задача решается через overlay-анализ: данные после последней секции PE-файла часто содержат сигнатуры инсталлятора. Overlay вычисляется так: конец последней секции = max(section.PointerToRawData + section.SizeOfRawData) для каждой секции. Всё, что после, - overlay. У нормального бинарника overlay отсутствует или минимален. У инсталлятора overlay составляет 90%+ размера файла - там хранится сжатый payload.

Тут есть подвох: малварь тоже использует NSIS и InnoSetup как обёртки для доставки. Детект инсталлятора - не причина для автоматического drop. Это причина для отдельной ветки триажа: извлечь вложенные файлы из инсталлятора и прогнать их через pipeline рекурсивно. Потренировавшись «на кошках», быстро понимаешь, что инсталлятор-обёртка - один из самых частых трюков доставки.

Автоматизация malware triage: сборка пайплайна​

Decision tree: логика принятия решений​

Финальный pipeline объединяет все проверки в последовательный decision tree:
  1. Парсинг PE-заголовков - если файл не парсится как PE, передать другому анализатору (ELF, Mach-O, документ)
  2. Installer detection - если инсталлятор, извлечь вложенные файлы, прогнать рекурсивно
  3. Imphash lookup - сравнение с базой known-bad и known-good
  4. Энтропия секций - если максимальная энтропия > 7.2 и число импортов < 10, пометить как packed
  5. YARA scan - если есть срабатывания, классифицировать по метаданным правила
  6. Анализ импортов - если подозрительные комбинации API, повысить severity
  7. Финальный вердикт: clean / suspicious / malicious / needs_manual_review
На Python это реализуется как цепочка функций с суммированием score. Скелет pipeline:
Python:
import pefile

def triage(path):
    try:
        pe = pefile.PE(path)
    except pefile.PEFormatError:
        return {"verdict": "not_pe"}
    score = sum(20 for s in pe.sections if s.get_entropy() > 7.2)
    ep = pe.OPTIONAL_HEADER.AddressOfEntryPoint
    ep_section = next((s for s in pe.sections if s.VirtualAddress <= ep < s.VirtualAddress + s.Misc_VirtualSize), None)
    if ep_section and ep_section != pe.sections[0]:  # EP не в первой секции
        score += 30
    if pe.FILE_HEADER.TimeDateStamp == 0:
        score += 10
    return {"verdict": "suspicious" if score > 40 else "review", "score": score}
Это демонстрация концепции - в проде добавляются YARA-сканирование через yara.compile(), imphash-lookup по словарю, installer detection через overlay analysis и логирование результатов в JSON для последующей агрегации.

Ограничения статического триажа​

Статический бинарный анализ без выполнения принципиально ограничен:

Многослойная упаковка - payload зашифрован внутри пакера, который упакован другим пакером. Статика видит только внешний слой. Та самая матрёшка, которую не развернёшь без запуска.

.NET и managed code - для .NET-сборок триаж по нативным импортам бесполезен: единственный импорт - mscoree.dll!_CorExeMain. Нужен отдельный pipeline с парсингом метаданных CLR через dnSpy, de4dot или YARA-модуль dotnet.

Fileless-стадии - PE может быть дроппером, а основная логика загружается в память из C2. Триаж дроппера покажет минимальные импорты и маленький размер, но payload останется за кадром.

Go и Rust бинарники - статически слинкованные, с огромной таблицей символов и нестандартной структурой секций. Стандартные YARA-правила для C/C++ PE дают false negative. Нужны отдельные правила: для Go - строки go.buildid и runtime-паттерны, для Rust - паникующие строки и манглинг символов.

Антианализ - малварь может содержать мусорные импорты для обмана триажа (import table stuffing) или поддельный Rich header.

Статический триаж - фильтр первого эшелона. Он сокращает очередь на порядок, но не заменяет динамический анализ для семплов, прошедших порог подозрительности.

Я строю подобные pipeline третий год, и главное наблюдение - не в инструментах, а в пороговых значениях. Каждая команда калибрует thresholds под свой поток: у SOC банка и у вендора антивируса распределение чистых и вредоносных PE кардинально различается. Начинал с энтропии > 7.0 как порога для packed - и утонул в false positive от легитимных UPX-бинарников и .NET-сборок с ConfuserEx. Поднял до 7.2 и добавил условие на число импортов < 10 - точность выросла, но пропустил один семпл с кастомным XOR-пакером, у которого энтропия была 6.9. Абсолютных порогов не существует - есть порог, оптимальный для конкретного потока, и его нужно пересматривать каждый квартал.

Индустрия переоценивает сложность автоматизации malware triage. Пайплайн из 200 строк на Python с pefile + YARA + imphash-база покрывает большинство задач. Оставшиеся 15% - .NET, Go, Rust и кастомно упакованные семплы, для которых нужны специализированные ветки. Вместо построения универсального анализатора эффективнее держать набор специализированных pipeline с общим оркестратором. Обратная сторона: такой подход требует дисциплины. Каждый новый билдер малвари, каждый новый пакер - это новое YARA-правило и, возможно, новая ветка в decision tree. Кто перестаёт обновлять правила - через полгода получает pipeline, пропускающий половину входящего потока. Если хочется потренироваться на разборе packed PE руками - на HackerLab.pro в RE-категории есть задачи, где payload нужно извлечь через несколько слоёв обфускации без подсказок.
 
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

Похожие темы

🚀 Первый раз на Codeby?
Гайд для новичков: что делать в первые 15 минут, ключевые разделы, правила
Начать здесь →
🧭 Навигатор · ИБ 2026
Не знаешь, какой трек твой?
5 направлений ИБ, реальные зарплаты и точка входа для каждого — в одном треде.
JuniorSenior+
100K → 600K+ ₽ /мес
Открыть навигатор →
🔴 Свежие CVE, 0-day и инциденты
То, о чём ChatGPT ещё не знает — обсуждаем в реальном времени
Threat Intel →
💼 Вакансии и заказы в ИБ
Pentest, SOC, DevSecOps, bug bounty — работа и проекты от проверенных компаний
Карьера в ИБ →

HackerLab