R0 CREW

Malware Analysis Tutorial 6: Self-Decoding and Self-Extracting Code Segment (Перевод: Prosper-H)

Перейти к содержанию

Цели урока:

  1. Использовать Immunity Debugger для анализа и «комментирования» двоичного кода.
  2. Понять принципы методов самораспоковки (Self-Extraction) в сегмент кода.

1. Введение

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

1.1 Цели

Мы будем исследовать следующие функции Max++ (просто установите брэйкпойнты на все адреса указанные ниже):

  • 0x00413BC2
  • 0x00413BDD
  • 0x00413A2B
  • 0x00410000
  • 0x00413BF2

1.2 Основные техники

[B]Вначале, мы рекомендуем вам сделать все возможное, чтобы проанализировать выше упомянутые функции, прежде чем приступать к разделу 2.[/B] Ниже дано несколько трюков IMM, возможно вы найдете в них что-то полезное для себя:

  • Комментирование кода: во время реверсинга – это наиболее часто используемая фича. Просто «right click» в панели кода IMM и выберите «Edit Comment», или нажмите “;”.
  • Маркировка кода: можно установить метку (label) на адрес (применимо к обеим сегментам кода и данных). Когда помеченный адрес будет использован в инструкциях JUMP или инструкциях загрузки в память, его метка отобразится в процессе дизассемблирования. Вы можете использовать ее для присвоения меток функциям и переменным. Чтобы пометить адрес, нажмите «right click» в панели кода IMM и выберите «Label».
  • Брэйкпойнты: для установки программных брэйкпойнтов используйте «F2». Для установки аппаратных брэйкпойнтов «right click» в панели кода и выберите «Breakpoints => Hardware Breakpoint on Executioin». В данный конкретный момент, используйте только программные брэйкпойнты.
  • Навигация по «Панели кода»: в сегменте кода можно легко ходить по разным адресам. Используйте для этого следующее действие: «right click» в панели кода, затем выберите «Go to => Expression» и введите требуемый адрес.

2. Анализ кода начинающегося с адреса 0x00413BC2

На рис. 1. показано четыре связанных между собой инструкций: POP ESI (0x00413BC1), SUB ESI, 9 (0x00413BC2), POP ESP и RETN.

Рис. 1. Начало кода с 0x00413BC2

Как уже обсуждалось в Уроке 5, инструкция RETN (0x00413BC0) пропускается системой, после возвращении из обработки прерывания INT 2D (0x00413BBE). И хотя кажется, что POP ESI (0x413BC1) пропускается, в действительно она выполняется системой. Это приводит к тому, что ESI содержит значение 0x00413BB9 (которое помещено инструкцией CALL 0x00413BB9 по адресу 0x00413BB4). Затем инструкция SUB ESI, 9 по адресу 0x00413BC2 изменяет значение ESI на 0x00413BB0. После чего, следующая инструкция LODS загружает из памяти данные (размещенные по адресу 0x00413BB0) в регистр EAX (можете убедиться, что сейчас значение EAX = 0). Затем верхний элемент стэка снимается в регистр EBP, и происходит возврат (returns). Цель инструкции POP состоит в том, чтобы просто заставить поток исполнения вернуться назад (на 2 уровня выше) к адресу 0x413BDD.

Обратите внимание, что если инструкция INT 2D не вызвала расщепления байта, то инструкция RETN (0x00413BD7) направит поток исполнения в 0x413A40 (к инструкции IRETD). IRETD является инструкцией возврата из прерывания и она не может быть выполнена в Ring 3 режиме (тем самым доставляя проблемы пользовательским отладчикам, таким как IMM). Исходя из этого, становится ясна цель инструкции POP EBP (0x413BC6)Заключение:4-ре инструкции (0x00413BC2 - 0x00413BC7) отвечают за перенаправление потока исполнения назад к адресу 0x00413BDD.

3. Анализ функции 0x00413BDD

Рис. 2. Функция 0x00413BDD

Как показано на рис. 2, эта функция очищает регистры и вызывает три функции: 0x413A2B (функция расшифровки), 0x00401000 (еще один трюк с INT 2D) и call EBP (где EBP устанавливает правильную функцию 0x00401000).

4. Анализ функции 0x00413A2B.

Рис. 3. Функция 0x00413A2B

Как показано на рис. 7, функция 0x00413A2B состоит из семи инструкций, первые шесть которой формируют цикл (адреса 0x00413A2B - 0x00413A33). Прежде чем продолжать анализ, обратитесь за справкой к Мануалу Intel и почитайте о инструкциях LODS и STORS.

По сути, инструкция LODS (0x00413A2B) загружает двойное слово (4 байта) из памяти, на которую указывает ESI, в EAX, а STOS делает обратные действия. Когда копирование строки завершается, инструкция LODS (STOS) увеличивает значение регистра ESI (EDI) на 4. Следующие две инструкции, после инструкции LODS, выполняют простую операцию расшифровки, в которой регистр EDX используется в качестве ключа расшифровки, к которому применяются операции XOR и SUB для декодирования данных.

Цикл заканчивается, когда регистр EDI = EBP. Если вы понаблюдаете за значениями регистров EBP и EDI, в панели регистров, то вы обнаружите, что эта функция расшифровки, по сути, расшифровывает регион памяти с 0x00413A40 по 0x00413BAC.

Установите брэйкпойнт на 0x00413A35 (или установите на него указатель мыши и нажмите F4), благодаря чему вы завершите работу цикла и выйдете из него. Чтобы увидеть результат работы функции расшифровки, сравните рис. 4 и 5. Как видно, до декодирования, по адресу 0x00413A40, находилась инструкция IRET (interrupt return), а после декодирования, она превратилась в инструкцию INT 2D!

Рис. 4. Регион памяти 0x00413A40 - 0x00413BAC (до декодирования)

Рис. 5. Регион памяти 0x00413A40 - 0x00413BAC (после декодирования)

Теперь давайте нажмем «right click» на адресе 0x00413A2B, выберем «Label» и пометим функцию как «basicEncoding». (По сути, это должно объявить 0x00413A2B, как начальный адрес функции «BasicEncoding»). Позже, всякий раз, когда этот адрес будет показываться в панели кода, мы будем видеть его мнемонику. Это действие значительно облегчит наш последующий анализ.

5. Анализ кода начиная с 0x00410000

Функция 0x00410000 сначала очищает направление роста для ESI/EDI (прим. пер. то есть выполняется инструкция CLD) и сразу вызывает функцию 0x00413A18. В 0x00413A18, снова проигрывается трюк с INT 2D. Если анализатор вредоносных программ или отладчик не правильно обработают расщепление байта, то содержимое стэка будет не корректным и поток управления программы пойдет по не правильному маршруту (за более подробной информацией обращайтесь к Урокам 3, 4, 5).

В итоге, когда функция вернется на адрес 0x00413BED, EPB должен быть установлен правильно. Его значение должно быть 0x00413A40.

6. Анализ кода начиная с адреса 0x00413A40

Теперь давайте перейдем по инструкции CALL EBP (0x00413A40). На рис. 6 отображено тело функции 0x00413A40. Оно начинается с инструкции INT 2D (которая связана с инструкцией RET). Очевидно, что при нормальном выполнении и в NON-DEBUG режиме, когда EAX = 1 (см. Урок 4), однобайтовая инструкция RET должна быть пропущена и выполнение должно продолжиться.

Рис. 6. Еще одна функция расшифровки

Вопросы дня

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

  1. Как выйти из цикла? [Подсказка: отладчик IMM услужливо вырисовывает структуры циклов (каждый цикл обозначается при помощи сплошных линий, с левой стороны). Разместите брэйкпойнт на первой инструкции, идущей после конца цикла, - посмотрите на адрес 0x00413B1C]
  2. Какая часть кода/стэка была модифицирована? Какие начальные и конечные адреса? [Подсказка: смотрите на инструкции модифицирующие «память», т.е. инструкции по адресам 0x00413A6F, 0x00413A8D и 0x00413B0E]

© Translated by Prosper-H from r0 Crew