Хакер - Ручная распаковка. Вскрываем кастомный пакер на примере вымогателя GlobeImposter 2.0
Строго говоря, пакер — это программа, которая упаковывает файлы, то есть уменьшает их размер. В свою очередь, протекторы заточены под создание сложностей в реверсе программы: они могут препятствовать снятию дампа, встраивать в программы антиотладочные приемы, функции определения работы внутри виртуальных машин и в целом всячески затруднять восстановление оригинального потока управления в приложении.
Надо сказать, что использование пакеров и протекторов не всегда свидетельствует о том, что перед нами вирус или другое вредоносное ПО: навесные защиты используют вполне легитимные программы для того, чтобы защитить свои алгоритмы от любопытных глаз реверсеров или спрятать механизмы регистрации, затруднив создание кряка или кейгена.
Первый взгляд на GlobeImposter
В конце 2017 года антивирусные сети зафиксировали распространение ботнетом Necurs новой версии GlobeImposter — шифровальщика, требующего выкуп за зашифрованные им файлы. Образец этого вредоносного ПО несколько отличается от остальных, и он сам немного поможет нам его победить. Как именно, расскажу дальше, а сейчас просто загрузим семпл в дизассемблер IDA.


WARNING
Все описанные в статье действия выполнялись внутри виртуальной машины, которая была изолирована от Сети. Если повторять их на основном компьютере, вирус-вымогатель GlobeImposter может заразить его и зашифровать твои данные.
Мы видим, что кода совсем мало, — IDA не разобрала львиную долю файла. Это похоже на какую-то упаковку, поэтому смотрим, что нам покажет Detect It Easy — популярный детектор пакеров и протекторов.


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


Разумеется, VirusTotal дает массу срабатываний, так как перед нами известный образец малвари. Но вирусные индикаторы не приносят нам практически никакой интересной информации, разве что сообщают о некоторых подозрительных WinAPI. В любом случае мы понимаем, что наш семпл упакован и нам его придется распаковывать. Для дальнейшей работы с образцом нужно включать виртуальную машину и загружать вирус в отладчик. Мы будем пользоваться отладчиком x64dbg, точнее его тридцатидвухбитной версией.

Приступаем к распаковке
Итак, семпл загружен в отладчик, и перед нами некоторое количество вызовов и переходов. Переключаемся в представление кода в виде графа, нажав горячую клавишу G. Визуальное представление кода изменилось, и стало немного проще. Как это всегда бывает при распаковке, нам нужно найти OEP (Original Entry Point — оригинальная точка входа), чтобы, встав на нее, снять дамп с процесса. Если опуститься на пару экранов вниз, можно увидеть код, напоминающий работу с базовым адресом загрузки модуля. Конструкция стандартная и простая, передача параметров через стек и вызов функции.

Давай попробуем установить точку останова на адрес 00403F30
, по которому находится вызов call
, и запустим программу.

INFO
На самом деле сразу же ставить точку останова и пропускать весь остальной код весьма смело — там может быть любой деструктивный пейлоад, который срабатывает при отладке или детектит виртуальную машину. Но мы работаем в изолированной среде и можем позволить себе легкое безрассудство, ведь всегда можно откатиться на сделанный заранее снимок состояния ВМ.
Сработала точка останова, и мы погружаемся в вызов call ransom_glob_sample.401100
, чтобы посмотреть, что там происходит.

Мы видим стандартный пролог функции. Давай вызовем контекстное меню и проверим это место на наличие пролога. Кликай левой кнопкой инструкции push ebp
и выбирай «Анализ → Добавить функцию», либо можешь нажать Shift + F.
Теперь, когда мы внутри этой функции, давай немного оглядимся. Сразу бросается в глаза блок кода, который напоминает работу с HTTP-соединениями, но обрати внимание: параметры, которые передаются в функции WinAPI, нулевые.

Очевидно, что этот код нам не интересен, потому что при передаче нулевых значений в качестве параметров он не будет работать. Например, для корректной работы функции WinHttpConnect
необходимо передать валидный хендл от вызова WinHttpOpen
, а также сервер и порт. Вот ее прототип.
HINTERNET WINAPI WinHttpConnect(
_In_ HINTERNET hSession,
_In_ LPCWSTR pswzServerName,
_In_ INTERNET_PORT nServerPort,
_Reserved_ DWORD dwReserved
);
Если эти значения переданы не будут, функция вернет NULL и больше ничего не сделает. Как видишь в ASM-листинге, в функцию передаются нули. Скажу больше: если установить точку останова где угодно внутри этого кодового блока, она не сработает, потому что код не получит управление. Во всяком случае, так происходит при первом запуске этого вируса на незараженной системе, а это значит, что наш код никакого отношения к распаковке семпла не имеет. Смотрим еще.
Чуть дальше в открытой нами функции наталкиваемся на такой очень интересный кодовый блок.

Здесь в память кладутся значения, которые представляют собой ASCII-представление записи "VirtualProtect"
, а дальше идет вызов GetProcAddress
. Если мы поставим точку останова сразу после GetProcAddress
, по адресу 00401548
, и запустим программу, то увидим, как функция VirtualProtect «соберется».

Разумеется, это динамическое получение адреса функции WinAPI VirtualProtect
. И это уже имеет непосредственное значение для распаковки!

INFO
Код распаковывается в какой-либо регион памяти и уже в распакованном виде выполняется оттуда. Но для этого нужно выделить память и изменить атрибуты доступа к ней, чтобы иметь возможность выполнять код. Функция VirtualProtect
как раз изменяет атрибуты доступа к региону памяти, поэтому она меня так заинтересовала.
Изучим эту функцию чуть подробнее. Вот ее прототип.
BOOL WINAPI VirtualProtect(
_In_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flNewProtect,
_Out_ PDWORD lpflOldProtect
);
Нас интересует третий параметр этой функции — flNewProtect
. Он как раз устанавливает тип доступа к региону памяти. Давай посмотрим на стек, чтобы узнать, какое значение туда передается.

Видим, что передается значение 0x40
, а документация MSDN показывает нам, что это права на чтение, запись и выполнение. Мы получили немного больше информации о том, как выглядит интересный нам регион памяти, теперь самое время прыгнуть в него. Прыжок происходит сразу после вызова VirtualProtect
.

После прыжка мы опять видим стандартный пролог функции, так что снова жмем «Анализ → Добавить функцию». После пролога есть несколько вызовов call
, а потом идет не совсем обычный код. Это однозначно не распакованный PE-файл, а скорее похоже на какой-то базонезависимый код вроде шелл-кода.

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

Давай выполним этот код до адреса 005DFA52
и посмотрим на дамп, начиная с адреса в ebp-28
.

Это надпись kernel32.dll
в двухбайтовой кодировке, о чем говорят нули (точки) между символами. Если мы потрассируем еще немного, то убедимся, что смысл этого кода (всего вызова call
, в который мы нырнули) — динамическое получение адресов функций LoadLibrary/GetProcAddress
.
Итак, выполним вызов до инструкции ret
и сделаем один шаг, чтобы оказаться на том месте, из которого мы пришли. Теперь наш странный код «вроде шелл-кода» становится понятным: это опять динамическое получение адресов функций WinAPI.
Если ставить точку останова на каждый push eax
и запускать код на исполнение, мы увидим, что получаются адреса для таких функций, как VirtualFree
, GetVersionExA
, TerminateProcess
, и других. Давай переключимся в представление кода в виде графа и прокрутим вниз — где-то должен быть переход в другой регион памяти.
Помнишь, в начале статьи я говорил, что этот семпл вируса поможет нам его распаковать?
Если в конце графа найдешь прыжок на распакованный код, чуть выше увидишь такой код.

Вызов call 80F98C
— не что иное, как OutputDebugStringA
с аргументом "Jump OEP"
! По всей видимости, авторы вируса забыли отладочную информацию, которая любезно сообщает нам о том, что сейчас будет прыжок в OEP.
Если мы поставим точку останова на jmp eax
, а потом сделаем еще один шаг отладчиком, то мы окажемся на OEP нашего вируса. Потом нужно будет реконструировать IAT плагином Scylla, который поставляется вместе с x64dbg (нажав на кнопочку IAT Autosearch), и снять дамп кнопкой Dump. После загрузки дампа в IDA мы видим совершенно другую картину, которая говорит нам о том, что файл распакован и IDA смогла его проанализировать.

Читайте ещё больше платных статей бесплатно: https://t.me/hacker_frei