Некоторые приёмы статического анализа кода из арсенала вирусного аналитика
- Приёмы навигации по коду
- Расшифровка данных
- Восстановление обнулённых дескрипторов секций
- Статическая распаковка файла
- Обнаружение вредоносных программ
Вирусному аналитику нередко приходится сталкиваться с полиморфными объектами — исполнимыми файлами, упакованными средствами защиты программ от ручного или автоматизированного анализа. Данная статья описывает ряд приёмов работы с такими объектами при помощи редактора Hiew (Hacker's View). Статья ориентирована на начинающих специалистов, в задачи которых входит анализ исполнимых файлов в форматах PE или ELF, и предполагает знакомство с архитектурой x86 и ассемблером Intel.
Совет от разработчика Hiew
Некоторое время назад я прочитал статью «Коварный и ужасный sndrec32.exe». По моему мнению, вместо описанного там применения четырёх различных инструментов достаточно одного Hiew. Вся правка «коварного и ужасного» сводится к следующему:
1. Найти функцию определения количества свободных байт в оперативной памяти. Автор статьи умолчал, каким образом он пришел именно к GlobalMemoryStatus
, но с помощью Hiew это можно сделать так:
F4, F3 — переключение в режим дизассемблера.
F8, F7 — просмотр функций импорта.
F9, *memory*
— поиск имён по маске.
Последний шаг делается в предположении, что искомая функция будет содержать слово «memory» в имени. Оказывается, всего одна импортируемая функция соответствует такому фильтру — GlobalMemoryStatus
.
2. Найти вызовы данной функции в коде. Для этого достаточно нажать Enter. Ctrl-Enter позволяет найти следующий вызов. На интересные фрагменты кода можно сделать закладки с помощью клавиши + на цифровой клавиатуре. В нашем случае второй и третий вызовы по адресам .10077AC и .100789A почти идентичны.
3. Исправить код. Клавишами Alt-1 вернёмся к первой закладке, прочтём код и примем решение заменить знаковый переход jle
на беззнаковый jbe
по адресу .10077D5. В строке по этому адресу нажмём F3 для редактирования и F2 для ввода ассемблерного кода, затем вместо «jle
» введём «jbe
» и нажмём Enter. После этого нажатием Esc выйдем из ассемблера и сохраним изменения клавишей F9.
Заметим, что автор оригинальной статьи не упомянул о коде на адресе .100789A. Перейдя на вторую закладку клавишами Alt-2, обнаружим там аналогичную ошибку и исправим её.
1. Приёмы навигации по коду
Анализ кода в полиморфных объектах обычно затруднён большим количеством инструкций, передающих управление на другие участки кода. Зачастую «ненужные» инструкции запутывают код настолько, что понять его логику можно, только разработав специальный анализатор кода. В задачи такого анализатора может входить выяснение размера и смещения зашифрованных данных, поиск ключа для их расшифровки, определение адреса перехода на изначальную точку входа и другие функции.
Типичный вопрос, встающий перед разработчиком анализатора кода, — «Поддержку каких инструкций необходимо добавить?» Можно выяснить это с помощью дизассемблера IDA Pro, но запускать такое мощное средство анализа кода только, чтобы увидеть одну-две инструкции, было бы нерациональным. Куда быстрее ту же задачу можно решить с помощью Hiew.
Многие вирусные аналитики назначают Hiew в качестве альтернативного средства просмотра (Alt-F3) в настройках менеджера файлов. Автор статьи рекомендует сэкономить ещё один шаг и добавить к команде запуска Hiew параметр /Oc=OEP
, чтобы просмотр исполнимых файлов всегда начинался с точки входа. Если же задать в файле hiew8.ini
настройку StartMode = Hex
, то режимом просмотра по умолчанию станет шестнадцатеричный.
К точке входа можно перейти в любой момент, нажав последовательно F8 и F5.
Удобная возможность Hiew — следовать по смещениям, указанным в инструкциях передачи управления, например, call
или jmp
. Справа от каждой такой инструкции редактор показывает цифру, нажав которую на клавиатуре, можно перейти по указанному в инструкции смещению. Вернуться назад после такого перехода можно, нажав клавишу 0. (Автор находит более удобным использовать для этой цели клавишу z, для чего в файле hiew8.ini
следует задать настройку JumpTable = "z123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
).
Иногда оказывается полезной возможность перенастройки шкалы смещений Hiew путём сопоставления её нулевой точки с адресом произвольной инструкции. Для этого необходимо, установив указатель на нужный адрес, дважды нажать Ctrl-F5. При этом новая настройка шкалы сохраняется в виде соответствующей записи в окне, отображаемом при однократном нажатии Ctrl-F5, и может быть использована повторно. Перенастройка шкалы может быть полезна для оценки размера зашифрованных данных, а также для повышения наглядности адресов переходов типа call
и jmp
, отсчитываемых в режиме адресации Local (Alt-F1) от «нуля» шкалы.
2. Расшифровка данных
Несложные скрипты для расшифровки данных можно разрабатывать и применять непосредственно в Hiew, экономя время на запуск отладчика.
Разберём для примера известный crackme/keygenme от Ms-Rem, в котором проверка производится байт-кодом в виртуальной машине. Рассмотрим начало байт-кода программы:

Байт-код
Код функции, декодирующей очередную инструкцию виртуальной машины, выглядит так:
.00401284: 8A0C18 mov cl,[eax][ebx] .00401287: 88CD mov ch,cl .00401289: C0ED04 shr ch,4 .0040128C: 30E9 xor cl,ch .0040128E: 80E107 and cl,7 .00401291: C3 retn ; -^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-
Чтобы расшифровать данные с помощью Hiew, следует прежде всего выделить блок зашифрованных данных клавишей * и нажать Alt-F3 (CryBlk). Затем в окне редактирования кода напишем код расшифровки, аналогичный вышеприведённому. Hiew поддерживает выполнение ограниченного набора инструкций, список которых приведён в разделе «Crypt commands» справочного файла. С учётом ограничений код будет выглядеть так:
mov ah,al ror ah,4 and ah,0fh xor al,ah and al,7
Нажав F7 после редактирования кода, получим расшифрованные данные.

Расшифрованный байт-код
При необходимости сохранить скрипт расшифровки в файл можно вернуться в редактор нажатием Alt-F3, после чего нажать F9.
Нередко возникает необходимость в расшифровке данных, зашифрованных по алгоритму XOR с длинным ключом. В таком случае можно воспользоваться встроенной возможностью Hiew по расшифровке: перейти в режим правки клавишей F3 в режимах Code или Hex, нажать F8 и задать ключ шифрования. Установить новый ключ можно клавишами Ctrl-F8.
3. Восстановление обнулённых дескрипторов секций
Для затруднения распаковки многие упаковщики обнуляют области памяти модуля, в которых находится дескрипторы секций. Каждый дескриптор описывается структурой IMAGE_SECTION_HEADER
.
В этом случае можно взять дескрипторы секций из оригинального файла, установив в них смещения, равные виртуальным адресам секций. Смещения должны быть выравнены на величину SectionAlignment
, указанную в заголовке образа.
Возьмем вредоносную программу, упакованную таким полиморфным упаковщиком, и увидим после снятия дампа следующее:

Обнулённые дескрипторы секций
Чтобы восстановить дескрипторы секций, прежде всего скопируем их из оригинального, упакованного файла. Для этого откроем этот файл, выделим начало и конец области дескрипторов клавишей *, а затем запишем выделенные байты в файл с дампом памяти, нажав F2 и указав имя файла и смещение (1e8).

Выделенные дескрипторы в оригинальном файле
Затем, открыв файл с дампом, исправим смещения, указанные в дескрипторах (F8, F6). Нажатие F3 позволяет начать редактирование выбранного дескриптора, а F9 — принять сделанные изменения.

Редактирование дескриптора секции
4. Статическая распаковка файла
Несмотря на то, что снятие дампа с помощью отладчика обычно быстрее, в некоторых случаях это по тем или иным причинам невозможно, и приходится прибегать к статической распаковке. Часть задач в рамках статической распаковки также можно осуществить, не выходя из Hiew.
Для примера рассмотрим образец test.bin
из приложения к статье. Он упакован простейшим полиморфным упаковщиком, предназначенным для сокрытия вредоносного кода от обнаружения.
Открыв файл для анализа в Hiew и пройдя несколько переходов, мы увидим характерный код для доступа к данным, расшифровки и перехода на OEP:
; Получение адреса зашифрованных данных: .10007012: 60 pushad .1000703F: E829000000 call .01000706D --↓1 .1000706D: 5E pop esi .10007098: 81C670FFFFFF add esi,0FFFFFF70 ;' p' ; Расшифровка: .10007117: 8B16 mov edx,[esi] .10007145: 03D6 add edx,esi .10007168: 8B4604 mov eax,[esi][4] .1000718C: 812ADEEC02E9 sub d,[edx],0E902ECDE ;'щ☻ь▐' .100071AD: 83C204 add edx,4 .100071E0: 83E801 sub eax,1 .1000721C: 83C608 add esi,8 .100070A5: 833E00 cmp d,[esi],0 .100070C1: 0F84C7010000 jz .01000728E --↓1 ; Переход на OEP: .100072BC: 61 popad .100072BE: EB02 jmps .0100072C2 --↓1 .100072C2: E998B7FFFF jmp .010002A5F --↑1
Для распаковки достаточно сложить адрес следующей инструкции после call
(10007044) и значение в инструкции add
(0FFFFFF70). Сложение можно выполнить непосредственно в Hiew, нажав Alt-=. Зашифрованные данные, находящиеся по вычисленному адресу, удобнее всего представить как массив двойных слов (DWORD). Для этого следует нажать Shift-F9 и при необходимости выбрать режим показа двойных слов клавишей Tab.

Массив двойных слов
Нетрудно видеть, что для распаковки первого объекта нужно вычислить его адрес (для этого сложим смещение 10006FB8 с величиной FFFFA048), запомнить размер объекта (17ED) и применить ключ расшифровки (0E902ECDE).
Заметим, что, если в массиве двойных слов окажется значение, похожее на RVA, перейти по нему можно клавишей Enter.
5. Обнаружение вредоносных программ
Чтобы разработать метод обнаружения вредоносного кода по сигнатуре, нужно найти в нём пригодные для этого уникальные черты.
В начале файлов tukuhegu.dll
и welolazu.dll
(см. приложение к статье) можно увидеть такие участки:

Переход посредством установки SEH-обработчика

Установка перехвата с помощью инструкции jmp
Первый признак, пригодный для обнаружения, заключается в использования SEH-обработчика с последующей его активацией путём сознательного вызова исключения. Это «трюк», практически никогда не встречающийся в легитимных программах. Во втором случае происходит запись значения 1DA79 по адресу 1001ED17+1. Затем — запись в память опкода инструкции jmp
и смещения 1001ED17, что является типичным признаком процедуры перехвата функций. При наличии столь подозрительных инструкций не требуется понимания того, как именно работает код и что хранится по указанным смещениям: найденные инструкции могут быть использованы в качестве сигнатур для обнаружения данной программы антивирусом. С целью предварительной фильтрации файлов и уменьшения числа ложных срабатываний антивирус обычно выполняет дешёвые предварительные проверки перед поиском сигнатур, например, проверку на наличие у секции кода атрибута «writeable».
Возьмем еще один пример, на котором можно увидеть ошибки генератора кода в полиморфном движке. Для этого откроем в Hiew образец A0330534.bin
:

Бессмысленная конструкция

Ещё одна бессмысленная конструкция
Ни один современный компилятор не создаст кода, который после записи значения в локальную переменную немедленно заменяет это значение новым. Такие участки кода обычно являются признаками, пригодными для эвристического обнаружения полиморфных генераторов.
Рассмотрим более сложный случай: модуль трояна «Зевс» (файл zeus_hz0132.bin
в приложении), полученный автором статьи по ссылке из спама. Образец интересен тем, что код распаковщика занимает всего 136 байт.

Распаковщик «Зевса»
Сразу после открытия файла в Hiew бросается в глаза переход на третью инструкцию с помощью инструкций push
и ret
, нехарактерный для современных компиляторов. Ещё одной особенностью является то, что код не использует результаты после вызовов GetCommandLineW
и GetOEMCP
. Начиная с инструкции lodsd
, можно видеть цикл расшифровки и ключи шифрования. Просматривая код далее, заметим переход назад с достаточно большим смещением в участок расшифрованного кода, который, очевидно, является переходом на OEP. На этом анализ завершён, и можно быстро написать статический распаковщик.