На заметку Как делать статический анализ исполняемых файлов


X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
6 085
Репутация
8 207
Интересная статья с хакера, посвященная статическому анализу:

В целом, если говорить про анализ исполняемых файлов, можно выделить два подхода — это статический анализ и динамический анализ.


Виды анализа исполняемых файлов

Статический анализ предполагает анализ файла без его запуска на выполнение. Он может быть базовым — в этом случае мы не анализируем непосредственно инструкции процессора в файле, а производим поиск нетипичных для обычных файлов артефактов (например, таких как строки или названия и последовательности API-функций), либо расширенным — в этом случае файл дизассемблируется и производится исследование инструкций, поиск их характерных для вредоносных программ последовательностей и определение того, что именно делала программа.

Динамический анализ заключается в исследовании файла с его запуском в системе. Он тоже может быть базовым и расширенным. Базовый динамический анализ — это исследование файла с его запуском без использования средств отладки, он заключается в отслеживании событий, связанных с этим файлом (например, обращение к реестру, дисковые операции, взаимодействие с сетью и т. п.). Расширенный динамический анализ заключается в исследовании поведения запущенного файла с применением средств отладки.

В этой статье я расскажу о базовых техниках статического анализа. Его преимущества:
  • позволяет получить результат достаточно быстро;
  • безопасен для системы при соблюдении минимальных мер предосторожности;
  • не требует подготовки специальной среды.
Основной недостаток базового статического анализа — это его низкая эффективность при анализе и распознавании сложных вредоносных программ, например упакованных неизвестным упаковщиком или использующих полное либо частичное шифрование файла с применением продвинутых алгоритмов.

Инструментарий

HEX-редакторы

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


HEX-редактор Hiew

Если не хочется тратить деньги, то можно обратить внимание, например, на (есть бесплатный вариант) или на .

Детекторы упаковщиков

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


Exeinfo PE

Эта программа, помимо детекта упаковщиков, имеет еще много других функций для анализа исполняемых файлов Windows, и во многих случаях можно обойтись ей одной.

Специализированные утилиты для исследования исполняемых файлов Windows

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


CFF Explorer

Так что настоятельно рекомендую CFF Explorer, тем более что программа бесплатная.

Python-модуль pefile

Python-модуль позволит обойтись при анализе PE-файлов исключительно интерпретатором Python. С ним практически все операции по базовому статическому анализу можно реализовать путем написания небольших скриптов. Прелесть всего этого в том, что заниматься исследованием PE-файлов можно в Linux.

Модуль присутствует в , и установить его можно через pip:
Код:
pip install pefile

Yara

Ну и в завершение всего списка весьма популярный и востребованный инструмент, ставший своеобразным стандартом в среде антивирусной индустрии, — проект . Разработчики позиционируют его как инструмент, который помогает исследователям малвари идентифицировать и классифицировать вредоносные сэмплы. Исследователь может создать описания для разного типа малвари в виде так называемых правил, используя текстовые или бинарные паттерны.

WWW
  • (обрати внимание — он слегка устарел)
Меры предосторожности

Чтобы обезопасить систему при проведении базового статического анализа подозрительных файлов, необходимо:
  • установить запрет на операцию чтения и выполнения анализируемого файла (вкладка «Безопасность» в контекстном меню «Свойства»);
  • сменить разрешение файла с .exe на какое-нибудь другое (или вообще убрать расширение анализируемого файла);
  • не пытаться открыть файл текстовыми процессорами и браузерами.
Можно обойтись этими мерами и не использовать виртуальную среду, хотя для полной безопасности можешь установить, например, и проводить анализ в нем (тем более что для динамического анализа без виртуалки, как правило, не обойтись).

Определение типа файла

Я думаю тебе известно, что признак PE-файла в Windows — это не только расширение .exe, .dll, .drv или .sys. Внутри него содержатся и другие отличительные черты. Первая из них — это сигнатура из байт вида "MZ" (или 0x4d, 0x5a в шестнадцатеричном представлении) в самом начале файла. Вторая — сигнатура также из двух байт PE и двух нулевых байтов следом (или 0x50, 0x45, 0x00, 0x00 в шестнадцатеричном представлении).

Смещение этой сигнатуры относительно начала файла записано в так называемом DOS-заголовке в поле e_lfanew, которое находится по смещению 0x3c от начала файла.

По большому счету наличие этих двух сигнатур в файле и подходящее расширение свидетельствует о том, что перед нами именно PE-файл, однако при желании можно посмотреть еще значение поля Magic опционального заголовка (Optional Header). Это значение находится по смещению 0x18 относительно начала сигнатуры PE. Значение этого поля определяет разрядность исполняемого файла:
  • значение 0x010b говорит о том, что файл 32-разрядный (помни, что в памяти числа располагаются с обратной последовательностью байт, сначала младший байт и далее старшие байты, то есть число 0x010b будет представлено последовательностью 0x0b, 0x01);
  • значение 0x020b говорит о том, что файл 64-разрядный.
Посмотреть это все можно несколькими способами. Первый — с помощью HEX-редактора.


Признаки PE-файла в HEX-редакторе Hiew

Второй — используя CFF Explorer или Exeinfo PE. Они наглядно показывают значения указанных сигнатур.

Третий способ — использовать возможности Python, запустив такой скрипт:
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:
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 либо применяем мои рекомендации из статьи « » и автоматизируем этот процесс с помощью небольшого скрипта на Python.
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 из состава , которая выводит весь текст, что найдет в файле. Строкой утилита считает любую последовательность из трех и более символов 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
}
Создатели некоторых вредоносов специально шифруют строки, но серьезные алгоритмы шифрования в таких случаях — это редкость. Так что шифровки можно поискать консольной утилитой (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
Еще одна удобная программа для поиска строк — это . Она отмечает все подозрительные строки, на которые стоит обратить внимание.

Анализ информации PE-заголовка

При компиляции PE-файла в него включается заголовок (PE-header), который описывает структуру файла. При выполнении файла загрузчик ОС читает информацию из этого заголовка, а затем копирует содержимое файла в память и передает ему исполнение.

Заголовок PE содержит в себе много информации, например по какому адресу PE-файл должен быть загружен в память, адрес запуска (он же — точка входа), списки библиотек, функций и ресурсов, которые файл будет использовать. Так что изучение заголовка дает при исследовании очень многое.

Анализ таблицы импорта

Большинство программ, в том числе вредоносных, используют API-функции, которые предоставляет операционная система. Их код содержится в разных DLL, откуда их и импортирует исполняемый файл. Собственно, перечень этих функций, содержащийся в заголовке, — это один из самых полезных фрагментов информации при исследовании. Стоит на него взглянуть, и уже примерно понимаешь, что делает программа.

Все это можно посмотреть в так называемой таблице импорта. Например, при помощи CFF Explorer.


Таблица импорта в CFF Explorer

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


Таблица импорта в Hiew

Как вариант, здесь во всей красе может показать себя Python-модуль pefile. Для вывода секции импорта можно использовать такой скрипт:
Python:
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('Файл не содержит секцию импорта.')

Вывод таблицы импорта с помощью скрипта на Python

Вот наиболее популярные 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:
Python:
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 от секции импорта после некоторой нормализации. Подробнее о нем можешь почитать .

В Yara imphash можно определить с помощью функции imphash() модуля pe:
Python:
import "pe"
rule imphash
{
  condition:
    pe.imphash() == "8a25c84dc57052979d26f561d4f12335"
}
На Python можно написать вот так:
Python:
import pefile
## не забудь указать реальный путь к исследуемому файлу
pe = pefile.PE(<путь к файлу>)
print('Imphash =', pe.get_imphash())
Используя значение imphash, вычисленное для одного файла, с помощью написанного выше правила Yara можно искать файлы с одинаковой таблицей импорта (и с большой вероятностью с похожим предназначением).

INFO

Утилита тоже позволяет анализировать секцию импорта, причем она не только показывает импортируемые API-функции, но и отмечает те, что часто встречаются в малвари, и показывает возможный вектор атаки по классификации .

Анализ таблицы экспорта

Вредоносные файлы с экспортируемыми функциями — редкое явление, однако иногда на просторах вирусных полей они встречаются. Бывают в том числе и вредоносные DLL.

Таблицу экспорта, в которой перечислены экспортируемые функции, можно так же, как и таблицу импорта, посмотреть в CFF Explorer или Hiew.


Таблица экспорта в CFF Explorer

Здесь нужно обратить внимание на говорящие сами за себя названия экспортируемых функций, либо на названия функций в виде беспорядочного набора символов, либо на отсутствие названий функций при наличии только ординалов (номеров) функций (такое тоже может быть, ведь функции можно экспортировать не только по имени, но и по ординалу).

На Python можно сделать следующий скрипт для просмотра таблицы экспорта:
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.

Таблица секций в CFF Explorer

На что нужно обратить внимание при анализе таблицы секций?

Во-первых, на необычные названия секций. Например, в виде беспорядочного набора символов.

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

В-третьих, наличие секций вроде UPX0, UPX1 или .aspack красноречиво свидетельствует о применении соответствующих упаковщиков.

Секции UPX0 и UPX1 в запакованном файле

Также о применении упаковщиков может свидетельствовать высокое значение (близкое к 8) какой-либо секции. Посчитать энтропию секций PE-файла можно с помощью вот такого скрипта на Python:
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() модуля .

Временная метка компиляции

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


Время компиляции файла в CFF Explorer

В удобоваримом виде значение этого времени можно посмотреть с помощью такого скрипта:
Python:
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 и представляет собой четырехбайтовое число в формате .

Посмотреть это значение в пристойном виде можно следующим скриптом:
Python:
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 в PE-файле и отметка о времени компиляции

Посмотреть временную метку компиляции из Debug Directory в «нормальном» виде можно следующим скриптом:
Python:
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.


Содержимое секции .rsrc в CFF Explorer

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


Просмотр информации о версии программы в секции ресурсов Resource Hacker

Поскольку в секции ресурсов могут хранится любые данные, туда могут быть помещены либо вредоносная нагрузка, либо драйвер (например, перехватывающий системные функции), либо еще что-нибудь не очень хорошее. Поэтому стоит призадуматься, если найдешь здесь что-то совсем не похожее на иконки, картинки, диалоги, окна, информацию о версии и прочие безобидные вещи.

Rich-сигнатура

В статье я не упомянул такую примечательную структуру PE-файла, как Rich-сигнатура. На нее тоже стоит обратить внимание при анализе подозрительных файлов. Почитать о ней на английском можно здесь:
  • Rich Headers: Leveraging this Mysterious Artifact of the PE Format ( )
  • (NTCore)
Также я написал .

Заключение

С помощью нескольких относительно простых инструментов мы можем быстро проанализировать подозрительный исполняемый файл, частично (а иной раз и достаточно полно) понять, что он собой представляет, и вынести вердикт о возможной вредоносности. Однако при анализе сложных файлов, подвергнутых упаковке, шифрованию и обфускации, этого может оказаться недостаточно. Тогда без более детального статического и динамического анализа не обойтись. Об этом мы и поговорим в следующий раз.
 
Последнее редактирование:
Верх Низ