R0 CREW

Реверсинг малвари, применяющей свой собственный исполняемый формат : модули Hidden Bee

Оригинал: blog.malwarebytes.com

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

Причина проста: проще реализовывать сложный функционал внутри PE файла, чем в виде шелкода. PE формат имеет определённую структуру, предоставляя наибольшую гибкость. У нас есть заголовки и структуры, в которых определяется какие импорты должны быть загружены и куда, точно также как и то, куда нужно применить релокации. Это формат по умолчанию для скомпилированых под Windows файлов. Его структура используется Windows Loader для загрузки и исполнения нашего приложения. Даже, когда авторы вредоносного ПО пишут свои лоадеры, то они почти наверняка для PE формата.

Однако, иногда бывают исключения. Последний раз когда мы изучали пейлоады относящиеся к Hidden Bee (распространяется с использованием Underminer exploit-kit) мы заметили нечто необычное. Есть два пейлоада, которые не соответствуют PE формату. Их структура выглядела организованной и более сложной, чем то с чем мы обычно встречались изучая кусочки шелкода. Мы решили взглянуть получше и обнаружили, что авторы этого вредоносного ПО озаботились созданием собственного исполняемого формата, который следовал некой постоянной структуре.

Общий обзор

Первый пейлоад b3eb576e02849218867caefaa0412ccd (с расширением .wasm => пытающийся казаться Web Assembly) - это лоадер, который скачивает и распаковывает файла формата Cabinet:

Второй пейлоад 11310b509f8bf86daa5577758e9d1eb5, распакованный из файла Cabinet:

Сперва мы видим, что в отличие от большинства шелкодов, он начинается не с кода, а с некоего заголовка. Сравнивая оба модуля мы можем заметить, что этот заголовок имеет одинаковую структуру в обоих случаях.

Заголовки

Мы изучили его, пытаясь определить предназначение каждого из полей заголовка.

Первый DWORD 0x100000301 одинаков для обоих файлов. Мы не смогли подтвердить принадлежность этого поля к чему либо внутри модуля, так что предположили, что это некоторое магическое число, идентификатор этого формата.

Следующие два WORD’а это смещения на элементы относящиеся к таблице импорта. Первый (0x18) указывает на список библиотек. Второй блок (0x60) выглядел более загадочным по началу. Его предназначение может быть понято после загрузки модуля в IDA. Мы сможем увидеть перекрестные ссылки на эти поля:

Видно, что они используются в качестве IAT, а именно: это адреса импортируемых модулем функций:

Следующее значение - это DWORD (0x2A62). Проследовав по этому адресу в IDA, мы окажемся в начале новой функции:

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

Смысл следующего значения - 0x509C, также легко угадать, потому что оно совпадает с размером всего модуля.

Затем, следуют последние два DWORD’а заголовка. Второй DWORD (0x4D78) указывает на структуру очень схожую с таблицой релокаций формата PE. Можно предположить, что это она и есть, а предыдущий DWORD содержит её размер.

Вот так выглядит структура заголовка, восстановленная нами:

typedef struct {
	DWORD magic;

	WORD dll_list;
	WORD iat;
	DWORD ep;
	DWORD mod_size;

	DWORD relocs_size;
	DWORD relocs;
} t_bee_hdr;

Импорты

Как мы уже знаем, список импортируемых библиотек начинается на смещении 0x18. Мы можем наблюдать, как каждому из имен предшествует номер:

Эти номера не соотносятся с именем библиотеки: в двух различных модулях, одинаковая библиотека имеет различные присвоенные номера. Но если мы просуммируем их все, то обнаружим, что полная сумма совпадает с размером IAT. Так мы делаем вывод о том, что эти числа определяют, сколько функций будет импортировано из соответствующей библиотеки.

Описать это можно следующей структурой (длина поля name не определена):

typedef struct {
	WORD func_count;
	char name;
} t_dll_name;

IAT представляет собой список DWORD’ов:

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

DWORD checksum(char *func_name)
{
  DWORD result = 0x1505;
  while ( *func_name )
    result = *func_name++ + 33 * result;
  return result;
}

Зная это, мы сопоставили контрольные суммы с соответствующими именами функций:

Как только адрес функции найден, он помещается на место контрольной суммы.

Релокации

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

Сравнение с форматом PE

В то время как PE формат достаточно запутан, с его разнообразными заголовками, этот формат содержит в себе только самое необходимое. Большинство информации, которая обычно хранится в PE заголовках полностью отсутствует здесь.
Визуализированный вариант формата PE можно найти по ссылке (by Ange Albertini)

Сравните его с визуализацией только что разобранного формата:

Статический анализ

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

Эти теги можно импортировать в IDA с помощью плагина IFL:

Имея все API функции подсвеченными, становится намного проще понять, что именно делает модуль. Ниже приведен пример функции, в которой модуль устанавливает соединение с C2 сервером:

Динамический анализ

Формат файла не стандартный, поэтому он не поддерживается привычными программами для анализа. Однако, после того как мы его изучили, не составит труда написать свой собственный загрузчик, включающий в себя парсер заголовков, это поможет нам запускать файл и изучать его в динамике.

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

Простой но редкий случай

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

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

К счастью, полностью не стандартные форматы достаточно редко встречаются в мире вредоносного ПО; обычно авторы полагаются на уже существующие, время от времени внося повреждения или изменения в какие-нибудь части PE заголовков.

© Translated by sysenter special for r0 Crew