Хакер - Малварь на просвет. Учимся быстро искать признаки вредоносного кода
hacker_frei
Евгений Дроботун
Содержание статьи
- Инструментарий
- HEX-редакторы
- Детекторы упаковщиков
- Специализированные утилиты для исследования исполняемых файлов Windows
- Python-модуль pefile
- Yara
- Меры предосторожности
- Определение типа файла
- Поиск в VirusTotal по хешу
- Поиск и анализ строк
- Анализ информации PE-заголовка
- Анализ таблицы импорта
- Анализ таблицы экспорта
- Анализ таблицы секций
- Временная метка компиляции
- Анализ ресурсов исполняемого файла
- Rich-сигнатура
- Заключение
Представь, что на твоем компьютере возник неизвестный исполняемый файл, но отправлять его на проверку в VirusTotal ты почему-то не хочешь. Например, потому что он может уйти в исследовательскую лабораторию, где его пристально изучат. В этой статье я покажу тебе, как провести такое исследование самостоятельно.
В целом, если говорить про анализ исполняемых файлов, можно выделить два подхода — это статический анализ и динамический анализ.

Статический анализ предполагает анализ файла без его запуска на выполнение. Он может быть базовым — в этом случае мы не анализируем непосредственно инструкции процессора в файле, а производим поиск нетипичных для обычных файлов артефактов (например, таких как строки или названия и последовательности API-функций), либо расширенным — в этом случае файл дизассемблируется и производится исследование инструкций, поиск их характерных для вредоносных программ последовательностей и определение того, что именно делала программа.
Динамический анализ заключается в исследовании файла с его запуском в системе. Он тоже может быть базовым и расширенным. Базовый динамический анализ — это исследование файла с его запуском без использования средств отладки, он заключается в отслеживании событий, связанных с этим файлом (например, обращение к реестру, дисковые операции, взаимодействие с сетью и т. п.). Расширенный динамический анализ заключается в исследовании поведения запущенного файла с применением средств отладки.
В этой статье я расскажу о базовых техниках статического анализа. Его преимущества:
- позволяет получить результат достаточно быстро;
- безопасен для системы при соблюдении минимальных мер предосторожности;
- не требует подготовки специальной среды.
Основной недостаток базового статического анализа — это его низкая эффективность при анализе и распознавании сложных вредоносных программ, например упакованных неизвестным упаковщиком или использующих полное либо частичное шифрование файла с применением продвинутых алгоритмов.
Инструментарий
HEX-редакторы
Один из основных инструментов статического базового анализа — это HEX-редактор. Их много, но в первую очередь необходимо отметить Hiew. Это безусловный лидер и бестселлер. Помимо непосредственно функций HEX-редактора, в нем реализовано еще много дополнительных возможностей, связанных с анализом файла: это и дизассемблер, и просмотрщик секций импорта и экспорта, и анализатор заголовка исполняемых файлов. Главный недостаток — все это не бесплатно (хотя и весьма недорого — от 555 рублей).

Если не хочется тратить деньги, то можно обратить внимание, например, на Hex Editor Neo (есть бесплатный вариант) или на HxD Hex Editor.
Детекторы упаковщиков
Если есть подозрение, что файл упакован, то с помощью детектора упаковщиков можно попытаться определить, какой упаковщик при этом использовался, и попробовать распаковать исследуемый файл. Долгое время безусловным лидером здесь была программа PEiD, и в принципе можно пользоваться и ей, однако поддержка давно прекращена и новых сигнатур для определения типов упаковщика уже никто не выпускает. Альтернатива — Exeinfo PE.

Эта программа, помимо детекта упаковщиков, имеет еще много других функций для анализа исполняемых файлов Windows, и во многих случаях можно обойтись ей одной.
Специализированные утилиты для исследования исполняемых файлов Windows
Программа CFF Explorer из пакета Explorer Suite — это настоящий швейцарский нож для исследователя PE-файлов. Позволяет получить огромное количество разнообразной информации обо всех компонентах структуры PE-файла и, помимо прочего, может служить HEX-редактором.

Так что настоятельно рекомендую CFF Explorer, тем более что программа бесплатная.
Python-модуль pefile
Python-модуль pefile позволит обойтись при анализе PE-файлов исключительно интерпретатором Python. С ним практически все операции по базовому статическому анализу можно реализовать путем написания небольших скриптов. Прелесть всего этого в том, что заниматься исследованием PE-файлов можно в Linux.
Модуль присутствует в PyPi, и установить его можно через pip:
pip install pefile
Yara
Ну и в завершение всего списка весьма популярный и востребованный инструмент, ставший своеобразным стандартом в среде антивирусной индустрии, — проект Yara. Разработчики позиционируют его как инструмент, который помогает исследователям малвари идентифицировать и классифицировать вредоносные сэмплы. Исследователь может создать описания для разного типа малвари в виде так называемых правил, используя текстовые или бинарные паттерны.
WWW
- Актуальная версия Yara
- Англоязычная справка по написанию правил
- Перевод справки на русский (обрати внимание — он слегка устарел)
Меры предосторожности
Чтобы обезопасить систему при проведении базового статического анализа подозрительных файлов, необходимо:
- установить запрет на операцию чтения и выполнения анализируемого файла (вкладка «Безопасность» в контекстном меню «Свойства»);
- сменить разрешение файла с .exe на какое-нибудь другое (или вообще убрать расширение анализируемого файла);
- не пытаться открыть файл текстовыми процессорами и браузерами.
Можно обойтись этими мерами и не использовать виртуальную среду, хотя для полной безопасности можешь установить, например, Virtual Box и проводить анализ в нем (тем более что при динамическом анализе без виртуалки, как правило, не обойтись).
Определение типа файла
Я думаю, тебе известно, что признак PE-файла в Windows — это не только расширение .exe, .dll, .drv или .sys. Внутри него содержатся и другие отличительные черты. Первая из них — это сигнатура из байт вида MZ (или 0x4d, 0x5a в шестнадцатеричном представлении) в самом начале файла. Вторая — сигнатура также из двух байт PE и двух нулевых байтов следом (или 0x50, 0x45, 0x00, 0x00 в шестнадцатеричном представлении).
Смещение этой сигнатуры относительно начала файла записано в так называемом DOS-заголовке в поле e_lfanew, которое находится по смещению 0x3c от начала файла.
WWW
По большому счету наличие этих двух сигнатур в файле и подходящее расширение свидетельствует о том, что перед нами именно PE-файл, однако при желании можно посмотреть еще значение поля Magic опционального заголовка (Optional Header). Это значение находится по смещению 0x18 относительно начала сигнатуры PE. Значение этого поля определяет разрядность исполняемого файла:
- значение 0x010b говорит о том, что файл 32-разрядный (помни, что в памяти числа располагаются с обратной последовательностью байтов, сначала младший байт и далее старшие байты, то есть число 0x010b будет представлено последовательностью 0x0b, 0x01);
- значение 0x020b говорит о том, что файл 64-разрядный.
Посмотреть это все можно несколькими способами. Первый — с помощью HEX-редактора.

Второй — используя CFF Explorer или Exeinfo PE. Они наглядно показывают значения указанных сигнатур.
Третий способ — использовать возможности Python, запустив такой скрипт:
with open(<путь к файлу>, 'rb') as file: # прочитаем первые 1000 байт файла (больше и не надо) buffer = file.read(1000) e_ifanew = int.from_bytes(buffer[0x3c:0x40], byteorder='little') mz_signature = buffer[0x0:0x2] pe_signature = buffer[e_ifanew:e_ifanew + 0x4] magic = buffer[e_ifanew + 0x18:e_ifanew + 0x1a] if mz_signature == b'MZ' and pe_signature == b'PE\x00\x00': if magic == b'\x0b\x01': print('Файл', sys.argv[1], 'является исполнимым PE32 файлом Windows.') elif magic == b'\x0b\x02': print('Файл', sys.argv[1], 'является исполнимым PE64 файлом Windows.') else: print('Файл', sys.argv[1],'не является PE файлом Windows.')
Или можешь использовать вот такое правило для Yara:
import "pe" //импортируем Yara-модуль pe rule is_pe_file { strings: $MZ_signature = "MZ" condition: ($MZ_signature at 0) and (pe.is_32bit() or pe.is_64bit()) }
Поиск в VirusTotal по хешу
Отправить на VirusTotal для проверки можно не только сам файл, но и его хеш (md5, sha1 или sha256). В этом случае, если такой же файл уже анализировался, VirusTotal покажет результаты этого анализа, при этом сам файл на VirusTotal мы не засветим.
Думаю, как узнать хеш файла, ты прекрасно знаешь. В крайнем случае можно написать небольшой скрипт на Python:
import hashlib with open(<путь к файлу>, 'rb') as file: buffer = file.read() print('md5 =', hashlib.md5(buffer).hexdigest()) print('sha1 =', hashlib.sha1(buffer).hexdigest()) print('sha256 =', hashlib.sha256(buffer).hexdigest())
Результат подсчета хеша шлем на VirusTotal либо применяем мои рекомендации из статьи «Тотальная проверка. Используем API VirusTotal в своих проектах» и автоматизируем этот процесс с помощью небольшого скрипта на Python.
import sys import requests ## будем использовать 2-ю версию API VirusTotal api_url = 'https://www.virustotal.com/vtapi/v2/file/report' ## не забудь про ключ доступа к функциям VirusTotal params = dict(apikey=<ключ доступа к API VirusTotal>, resource=str(sys.argv[1])) response = requests.get(api_url, params=params) if response.status_code == 200: result = response.json() if result['response_code'] == 1: print('Обнаружено:', result['positives'], '/', result['total']) print('Результаты сканирования:') for key in result['scans']: print('\t' + key, '==>', result['scans'][key]['result']) elif result['response_code'] == -2: print('Запрашиваемый объект находится в очереди на анализ.') elif result['response_code'] == 0: print('Запрашиваемый объект отсутствует в базе VirusTotal.') else: print('Ошибка ответа VirusTotal.') else: print('Ошибка ответа VirusTotal.')
Как видишь, скрипт получает значение хеша, переданного в виде аргумента командной строки, формирует все нужные запросы для VirusTotal и выводит результаты анализа.
Если VirusTotal выдал в ответ какие-нибудь результаты анализа, это значит, что исследуемый файл уже кто-то загружал для анализа и его можно загрузить туда повторно и получить более актуальные результаты, на чем анализ можно и завершать. Но вот если VirusTotal не найдет файла в базах, тогда есть смысл идти дальше.
Поиск и анализ строк
Некоторые строки внутри файла могут явно свидетельствовать о его злонамеренности, а иногда по ним даже понятно, что конкретно он делает. Например, URL или IP-адреса внутри файла подскажут, что она взаимодействует с какими-то серверами (их, кстати, тоже можно пробить через VirusTotal на вредоносность).
Могут попадаться и строки с ключами реестра. Например, вот такие пути будут явно указывать на желание файла закрепиться в системе надолго:
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run
Скорее всего, вместе с такими строками в секции импорта найдутся и запросы к функциям API, которые манипулируют ключами реестра.
Строки можно напрямую поискать с помощью HEX-редактора, а можно использовать консольную утилиту Strings из состава Sysinternals Suite, которая выводит весь текст, что найдет в файле. Строкой утилита считает любую последовательность из трех и более символов ASCII или Unicode, которая завершается нулевым символом.
Кроме URL, IP и ключей реестра стоит обратить внимание на следующие разновидности текстовых данных.
- Строки с именами процессов антивирусных программ и различных утилит исследования системы (например,
avp.exeдля Касперского,ekrn.exeдля ESET,drweb32.exeдля DrWeb илиprocexp.exeдля Process Explorer из состава Sysinternals Suite). Они могут свидетельствовать о том, что программа ищет эти процессы и собирается их завершить. - Строки с именами процессов средств виртуализации (например,
VBoxTray.exeилиVBoxService.exeдля Virtual Box,prl_cc.exeилиprl_tools.exeдля Parallels,vmsrvc.exeилиvmusrvc.exeдля Virtual PC) могут означать, что программа пытается определить, что ее запускают в виртуальной машине. - Строки с именами процессов браузеров (
iexplore.exe,firefox.exe,chrome.exeи т. п.) — возможно, признак внедрения кода в эти браузеры. Это может понадобиться для перехвата трафика или вводимой информации. Как вариант, программа может пытаться загрузить расширение или даже использовать целевой эксплоит для браузера. - Строки с именами системных процессов (
explorer.exe,svchost.exeи т. д.) могут быть признаком попыток внедрения вредоносного кода в эти процессы либо признаком запуска вредоносных процессов под видом системных. Иногда для поиска и манипуляций с процессомexplorer.exeиспользуют имя его окна —progman. Стоит поискать и его. - Строки подозрительного содержания. Например, что-нибудь типа
keylogger.txtилиPortscanner startip.
Для Yara можно написать универсальные правила, которые помогут искать строки, применив регулярные выражения. Например, для поиска URL или IP в файле можно написать так:
rule URL { strings: $url = /(https?:\/\/)?([\w\.]+)\.([a-z]{2,6}\.?)(\/[\w\.]*)*\/?/ wide ascii condition: $url } rule IP { strings: $ip = /([0-9]{1,3}\.){3}[0-9]{1,3}/ wide ascii condition: $ip }
Создатели некоторых вредоносов специально шифруют строки, но серьезные алгоритмы шифрования в таких случаях — это редкость. Так что шифровки можно поискать консольной утилитой Floss (FireEye Labs Obfuscated String Solver). Как и Strings, она ищет строки в файле, но иногда позволяет найти обфусцированные или зашифрованные варианты.
WWW
Часто вирусописатели не заморачиваются и шифруют строки при помощи XOR. Такие строки легко найти при помощи правила для Yara.
rule xor_string { strings: $string = "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" xor condition: $string }
Такое правило будет искать в файле строку с ключом реестра, поксоренную производным байтом. К сожалению, с регулярными выражениями модификатор xor применить нельзя.
INFO
Еще одна удобная программа для поиска строк — это pestudio. Она отмечает все подозрительные строки, на которые стоит обратить внимание.
Анализ информации PE-заголовка
При компиляции PE-файла в него включается заголовок (PE-header), который описывает структуру файла. При выполнении файла загрузчик ОС читает информацию из этого заголовка, а затем копирует содержимое файла в память и передает ему исполнение.
Заголовок PE содержит в себе много информации, например по какому адресу PE-файл должен быть загружен в память, адрес запуска (он же — точка входа), списки библиотек, функций и ресурсов, которые файл будет использовать. Так что изучение заголовка дает при исследовании очень многое.
Анализ таблицы импорта
Большинство программ, в том числе вредоносных, используют API-функции, которые предоставляет операционная система. Их код содержится в разных DLL, откуда их и импортирует исполняемый файл. Собственно, перечень этих функций, содержащийся в заголовке, — это один из самых полезных фрагментов информации при исследовании. Стоит на него взглянуть, и уже примерно понимаешь, что делает программа.
Все это можно посмотреть в так называемой таблице импорта. Например, при помощи CFF Explorer.

Или можно использовать Hiew.

Как вариант, здесь во всей красе может показать себя Python-модуль pefile. Для вывода секции импорта можно использовать такой скрипт:
import pefile ## не забудь указать реальный путь к исследуемому файлу pe = pefile.PE(<путь к файлу>) if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'): for dll_entry in pe.DIRECTORY_ENTRY_IMPORT: print(dll_entry.dll.decode('utf-8')) for api_entry in dll_entry.imports: print('\t' + api_entry.name.decode('utf-8')) else: print('Файл не содержит секцию импорта.')

Вот наиболее популярные DLL, которые встречаются в любых программах:
- kernel32.dll — содержит базовые функции: доступ и управление памятью, работа с файлами и устройствами;
- advapi32.dll — обеспечивает доступ к ключевым компонентам Windows (диспетчер служб, реестр), а также содержит API-функции криптографической подсистемы Windows;
- user32.dll — содержит компоненты графического интерфейса;
- gdi32.dll — содержит функции графических операций;
- winnet.dll — содержит высокоуровневые сетевые API-функции, реализующие такие протоколы, как FTP, HTTP и NTP.
На что стоит обратить внимание при исследовании таблицы импорта?
В первую очередь — на наличие библиотек ntdll.dll, wsock32.dll и ws2_32.dll. Эти библиотеки редко встречаются в обычных программах, поскольку содержат в себе низкоуровневые API-функции.
- ntdll.dll содержит функции доступа к компонентам системы. Если встретишь импорт API из этого DLL, это может означать, что программа пытается перехватить системные функции, реализует разные антиотладочные приемы, манипулирует процессами, противодействует антивирусам и т. п.
- wsock32.dll и ws2_32.dll содержат низкоуровневые API-функции для работы с сокетами. Они обычно используются в узкоспециализированном ПО — и если встретятся при анализе обычной программы, это должно насторожить.
Наличие библиотеки advapi32.dll и криптографических API-функций вроде CryptGenKey, CryptEncrypt, CryptDecrypt и подобных сразу вызывает подозрение, что перед нами очередной шифровальщик-вымогатель. Создатели таких программ часто используют стандартные крипто-API, а не придумывают собственные реализации.
Наличие этой же библиотеки вкупе с вызовом функций работы с реестром (типа RegCreateKeyEx, RegEnumKeyEx, RegDeleteKeyExA и т. п.) говорит о манипуляциях с реестром и возможной записи в автозагрузку. Подтверждает это наличие строк с соответствующими ключами реестра.
Также стоит обратить внимание на слишком маленький импорт или его отсутствие. Это крайне нетипично для обычных программ. Также не совсем типично присутствие в импорте связки API-функций LoadLibrary и GetProcAddress (либо LdrLoadDll и LdrGetProcAddress). Это однозначно говорит о реализации так называемого импорта времени выполнения (в отличие от динамического импорта, когда все импортируемые функции перечислены в секции импорта) и свидетельствует о попытке скрыть настоящее назначение файла. Либо это может говорить о том, что файл упакован.
INFO
Как правило при использовании связки LoadLibrary (или LoadLibraryEx) и GetProcAddress (или LdrLoadDll + LdrGetProcAddress) необходимы имена функций, которые будут импортироваться во время выполнения программы, поэтому если нашел эти API в импорте, есть смысл поискать строки с именами импортируемых во время выполнения API-функций. При этом стоит помнить, что они могут быть зашифрованы или обфусцированы.
Следующие API или их связки также требуют особого внимания. Их наличие в исследуемом файле — повод насторожиться.
- Связка из
VirtualAllocиVirtualProtectилиHeapAllocиVirtualProtectможет свидетельствовать о распаковке рабочей нагрузки вредоносного файла. - API-функции
CreateProcessилиShellExecuteмогут свидетельствовать о создании дочерних процессов, например чтобы отслеживать наличие в системе основного процесса и восстанавливать его в случае остановки (это один из распространенных и простейших способов создания так называемого «неубиваемого процесса»). - Связка из
OpenProcess,VirtualAlloc(илиVirtualAllocEx),WriteProcessMemoryиCreateRemoteThread— весьма красноречивый признак попытки внедрения кода в какой-либо процесс (особенно вкупе с наличием в файле строки с именем какого-либо процесса). SetWindowsHookExсвидетельствует об установке перехватов других API-функций. Очень часто это делается вовсе не во благо ничего не подозревающего пользователя.- Функция
RegisterHotKey— признак кейлоггера, она может использоваться для перехвата нажатий на клавиши; - Связка из
OpenSCManager,CreateServiceиStartServiceсвидетельствует о создании системного сервиса. Она тоже может использоваться во вред, в частности для создания «неубиваемого процесса». - Связка из
URLDownloadToFileиShellExecute— весьма вероятный признак трояна-даунлоадера; - Функция
TerminateProcessсовместно со строками имен процессов антивирусных программ в одном файле практически однозначно свидетельствует о желании исследуемого файла обезопасить себя, грохнув процесс антивируса; - Функции
IsDebuggerPresent,CheckRemoteDebuggerPresentилиOutputDebugStringмогут говорить о применении простейших антиотладочных приемов. В обычных программах они встречаются редко.
Правило для Yara, которое распознает, к примеру, связку из OpenProcess, VirtualAlloc, WriteProcessMemory и CreateRomoteThread, может выглядеть так:
rule api_bandle { strings: $str_api_1 = "OpenProcess" $str_api_2 = "VirtualAllocEx" $str_api_3 = "WriteProcessMemory" $str_api_4 = "CreateRemoteThread" condition: $str_api_1 and $str_api_2 and $str_api_3 and $str_api_4 }
Или так (с использованием Yara-модуля pe):
import "pe" //импортируем Yara-модуль pe rule api_bandle { condition: pe.imports("kernel32.dll", "OpenProcess") and pe.imports("kernel32.dll", "VirtualAllocEx") and pe.imports("kernel32.dll", "WriteProcessMemory") and pe.imports("kernel32.dll", "CreateRemoteThread") }
А вот правило для Yara для распознавания связки из LoadLibrary (или LoadLibraryEx) и GetProcAddress и строк с импортируемой во время выполнения связкой OpenProcess, VirtualAllocEx, WriteProcessMemory и CreateRemoteThread, которые могут быть закрыты побайтовым XOR:
import "pe" //импортируем Yara-модуль pe rule api_bandle { strings: $str_api_1 = "OpenProcess" xor $str_api_2 = "VirtualAllocEx" xor $str_api_3 = "WriteProcessMemory" xor $str_api_4 = "CreateRemoteThread" xor condition: $str_api_1 and $str_api_2 and $str_api_3 and $str_api_4 and (pe.imports("kernel32.dll", "LoadLibrary") or pe.imports("kernel32.dll", "LoadLibraryEx")) and pe.imports("kernel32.dll", "GetProcAddress") }
Для сравнения секций импорта разных файлов можно использовать хеш импорта, так называемый imphash. Это хеш md5 от секции импорта после некоторой нормализации. Подробнее о нем можешь почитать в блоге FireEye.
В Yara imphash можно определить с помощью функции imphash() модуля pe:
import "pe" rule imphash { condition: pe.imphash() == "8a25c84dc57052979d26f561d4f12335" }
На Python можно написать вот так:
import pefile ## не забудь указать реальный путь к исследуемому файлу pe = pefile.PE(<путь к файлу>) print('Imphash =', pe.get_imphash())
Используя значение imphash, вычисленное для одного файла, с помощью написанного выше правила Yara можно искать файлы с одинаковой таблицей импорта (и с большой вероятностью с похожим предназначением).
INFO
Утилита pestudio тоже позволяет анализировать секцию импорта, причем она не только показывает импортируемые API-функции, но и отмечает те, что часто встречаются в малвари, и показывает возможный вектор атаки по классификации MITRE ATT&CK.
Анализ таблицы экспорта
Вредоносные файлы с экспортируемыми функциями — редкое явление, однако иногда на просторах вирусных полей они встречаются. Бывают в том числе и вредоносные DLL.
Таблицу экспорта, в которой перечислены экспортируемые функции, можно так же, как и таблицу импорта, посмотреть в CFF Explorer или Hiew.

Здесь нужно обратить внимание на говорящие сами за себя названия экспортируемых функций, либо на названия функций в виде беспорядочного набора символов, либо на отсутствие названий функций при наличии только ординалов (номеров) функций (такое тоже может быть, ведь функции можно экспортировать не только по имени, но и по ординалу).
На Python можно сделать следующий скрипт для просмотра таблицы экспорта:
import pefile ## не забудь указать реальный путь к исследуемому файлу pe = pefile.PE(<путь к файлу>) if hasattr(pe, 'DIRECTORY_ENTRY_EXPORT'): for export_entry in pe.DIRECTORY_ENTRY_EXPORT.symbols: print('\t' + export_entry.name.decode('utf-8')) print('\t\tОрдинал:', str(hex(export_entry.ordinal))) print('\t\tRVA функции:', str(hex(export_entry.address))) else: print('Файл не содержит секцию экспорта.')
Анализ таблицы секций
Фактически все содержимое PE-файла разбито на секции. Каждая секция хранит в себе либо код, который будет исполнен при запуске файла, либо данные, необходимые для выполнения, либо ресурсы, используемые файлом. У каждой секции есть имя, однако операционная система определяет назначение секции не по имени, а по атрибуту Characteristics, а имена используются только для наглядности.
Помимо назначения секции атрибут Characteristics определяет операции, которые можно проводить с данными секции (чтение, выполнение, запись и т. д.). Более подробно о секции в целом и об этом атрибуте можно почитать в официальной документации. Наиболее часто используемые названия секций:
.textилиCODE— как правило, содержит исполняемый код (название секцииCODEхарактерно для программ, написанных на Delphi), в большинстве случаев имеет значение атрибута Characteristics, равное0x60000020(IMAGE_SCN_CNT_CODE & IMAGE_SCN_MEM_EXECUTE & IMAGE_SCN_MEM_READ);.dataилиDATA— обычно здесь лежат данные для чтения или записи (название секцииDATAтакже характерно для программ, написанных на Delphi), Characteristics чаще всего равен0xс0000040(IMAGE_SCN_CNT_INITIALIZED_DATA & IMAGE_SCN_MEM_READ & IMAGE_SCN_MEM_WRITE);.rdata— данные только для чтения, иногда здесь лежат таблицы импорта и экспорта, Characteristics равен0x40000040(IMAGE_SCN_CNT_INITIALIZED_DATA & IMAGE_SCN_MEM_READ) или0x50000040(IMAGE_SCN_CNT_INITIALIZED_DATA & IMAGE_SCN_MEM_READ & IMAGE_SCN_MEM_SHARED);.idata— информация об импорте (если секция отсутствует, то импорт содержится в секции.rdata), Characteristics чаще всего равен0xс0000040(такое же, как и у секции.data);.edata— информация об экспорте (если секция отсутствует, то экспорт содержится в секции.rdata), Characteristics обычно равен0xс0000040(такое же, как и у секции.data);.rsrc— ресурсы, используемые PE-файлом (иконки, диалоговые окна, меню, строки и т. д.), Characteristics равен0x50000040либо0x40000040.
Также могут встречаться секции .tls, .reloc, .pdata, .bss (или BSS), .debug, .CRT, .didat. Наличие секции BSS вместе с секциями CODE и DATA означает, что автор программы использовал компилятор Delphi, а если есть секции .bss и .CRT, это может означать, что программу компилировали в MinGW.
Увидеть это все легко можно с помощью, например, CFF Explorer.

На что нужно обратить внимание при анализе таблицы секций?
Во-первых, на необычные названия секций. Например, в виде беспорядочного набора символов.
Во-вторых, на несоответствие атрибута Characteristics назначению и содержимому секции. Например, может быть так, что у секции .text, в которой содержится исполняемый код, помимо прочего еще добавлено IMAGE_SCN_MEM_WRITE, то есть в секцию с кодом можно писать данные. Это явный признак самомодифицирующегося кода, и в обычных программах такое почти не встречается. То же можно сказать и про секцию с данными (.data или DATA): если в атрибуте Characteristics помимо прочего присутствует IMAGE_SCN_MEM_EXECUTE, то это очень серьезный повод для подозрения.
В-третьих, наличие секций вроде UPX0, UPX1 или .aspack красноречиво свидетельствует о применении соответствующих упаковщиков.

Также о применении упаковщиков может свидетельствовать высокое значение (близкое к 8) энтропии какой-либо секции. Посчитать энтропию секций PE-файла можно с помощью вот такого скрипта на Python:
import pefile ## не забудь указать реальный путь к исследуемому файлу pe = pefile.PE(<путь к файлу>) for section_entry in pe.sections: print(section_entry.Name.decode('utf-8')) print('\tCharacteristics:', hex(section_entry.Characteristics)) print('\tMD5 хэш секции:', section_entry.get_hash_md5()) print('\tЭнтропия секции:', section_entry.get_entropy())
Скрипт выводит имена всех секций PE-файла, значение атрибута Characteristics, md5-хеш и энтропию каждой секции.

В Yara энтропию можно определить с помощью функции entropy() модуля math.
Временная метка компиляции
Информация о времени компиляции анализируемого файла может быть полезна при построении графа атаки и его анализе. Время создания (и, соответственно, компиляции PE-файла) хранится в PE-заголовке в виде четырехбайтового числа, содержащего количество секунд, прошедших с 0 часов 0 минут 1 января 1970 года.

В удобоваримом виде значение этого времени можно посмотреть с помощью такого скрипта:
import pefile ## не забудь указать реальный путь к исследуемому файлу pe = pefile.PE(<путь к файлу>) print('Дата и время компиляции:', time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(pe.FILE_HEADER.TimeDateStamp)))
Для Delphi (а вредоносные файлы, написанные на Delphi, встречаются и по сей день) это значение всегда равно 0x2a425e19, что значит 0 часов 0 минут 19 июня 1992 года. В этом случае реальную дату компиляции можно попытаться определить из отметки времени секции .rsrc (в файлах, создаваемых Delphi, она всегда присутствует). Временная метка находится по смещению 4 от начала секции .rsrc и представляет собой четырехбайтовое число в формате MS-DOS time.
Посмотреть это значение в пристойном виде можно следующим скриптом:
import pefile ## не забудь указать реальный путь к исследуемому файлу pe = pefile.PE(<путь к файлу>) time_stamp_dos = pe.DIRECTORY_ENTRY_RESOURCE.struct.TimeDateStamp ## преобразуем время из MS DOS формата в «нормальный» вид day = time_stamp_dos >> 16 & 0x1f month = time_stamp_dos >> 21 & 0x7 year = (time_stamp_dos >> 25 & 0xff) + 1980 second = (time_stamp_dos & 0x1f) * 2 minute = time_stamp_dos >> 5 & 0x3f hour = time_stamp_dos >> 11 & 0x1f print('Дата и время компиляции: {}-{}-{} {:02d}:{:02d}:{:02d}'.format(day, month, year, hour, minute, second))
Если PE-файл компилировался Visual Studio и при компиляции в файл была включена отладочная информация (а это можно определить по наличию таблицы Debug Directory в PE-файле), то дата компиляции (помимо заголовка PE-файла) также содержится и в этой таблице:

Посмотреть временную метку компиляции из Debug Directory в «нормальном» виде можно следующим скриптом:
import pefile ## не забудь указать реальный путь к исследуемому файлу pe = pefile.PE(<путь к файлу>) time_stamp = pe.DIRECTORY_ENTRY_DEBUG[0].struct.TimeDateStamp print('Дата и время компиляции:', time.strftime('%d-%m-%Y %H:%M:%S', time.gmtime(time_stamp)))
При анализе временной метки компиляции под подозрением должны быть:
- недавняя дата компиляции программы (или нереальные ее значения, например еще не наступившая дата);
- несовпадение даты компиляции и версии компилятора (согласись, странно видеть дату компиляции, например, 20 июня 2005 года для экзешника, откомпилированного Visual Studio 19 версии);
- значение даты компиляции равно нулю (велика вероятность, что создатель программы это сделал намеренно, соответственно возникает вопрос — зачем);
- для файлов, которые по всем признакам откомпилированы в Delphi, дата не соответствует значению 0x2a425e19, а дата, полученная из секции
.rsrc, равна нулю или меньше, чем дата в PE-заголовке; - дата компиляции из заголовка анализируемого файла не совпадает с датой, указанной в Debug Directory (весьма вероятно, что эти значения были зачем-то скорректированы).
INFO
У некоторых компонентов Windows поле TimeDateStamp в PE-заголовке может иметь интересные значения — либо из будущего (к примеру, у меня для «Блокнота» это 8 сентября 2028 года), либо из прошлого (встречаются компоненты, датированные 1980 годом).
Анализ ресурсов исполняемого файла
Ресурсы, необходимые для работы exe-файла (такие как иконки, диалоговые окна, меню, изображения, информация о версии, конфигурационные данные), хранятся в секции .rsrc. Просмотреть можно при помощи все того же CFF Explorer.

Но все же для просмотра ресурсов в PE-файлах лучше использовать Resource Hacker.

Поскольку в секции ресурсов могут хранится любые данные, туда могут быть помещены либо вредоносная нагрузка, либо драйвер (например, перехватывающий системные функции), либо еще что-нибудь не очень хорошее. Поэтому стоит призадуматься, если найдешь здесь что-то совсем не похожее на иконки, картинки, диалоги, окна, информацию о версии и прочие безобидные вещи.
Rich-сигнатура
В статье я не упомянул такую примечательную структуру PE-файла, как Rich-сигнатура. На нее тоже стоит обратить внимание при анализе подозрительных файлов. Почитать о ней на английском можно здесь:
- Rich Headers: Leveraging this Mysterious Artifact of the PE Format (PDF)
- Microsoft's Rich Signature (NTCore)
Также я написал скрипт на Python для просмотра Rich-сигнатуры.
Заключение
С помощью нескольких относительно простых инструментов мы можем быстро проанализировать подозрительный исполняемый файл, частично (а иной раз и достаточно полно) понять, что он собой представляет, и вынести вердикт о возможной вредоносности. Однако при анализе сложных файлов, подвергнутых упаковке, шифрованию и обфускации, этого может оказаться недостаточно. Тогда без более детального статического и динамического анализа не обойтись. Об этом мы и поговорим в следующий раз.
Читайте ещё больше платных статей бесплатно: https://t.me/hacker_frei