Перейти к содержанию
Цели урока:
- Использовать Immunity Debugger для анализа и «комментирования» двоичного кода.
- Понять принципы методов самораспоковки (Self-Extraction) в сегмент кода.
1. Введение
В этом уроке будет обсуждаться несколько интересных техник предназначенных для анализа функций, предназначенных для расшифровки и извлечения (decoding/self-extraction) участков кода. Подобные методы часто используются вредоносными программами, чтобы избежать статический анализ. Основной подход, который мы будем здесь использовать, состоит в пошаговом выполнении вредоносной программы с параллельным комментированием ее кода.
1.1 Цели
Мы будем исследовать следующие функции Max++ (просто установите брэйкпойнты на все адреса указанные ниже):
- 0x00413BC2
- 0x00413BDD
- 0x00413A2B
- 0x00410000
- 0x00413BF2
1.2 Основные техники
Вначале, мы рекомендуем вам сделать все возможное, чтобы проанализировать выше упомянутые функции, прежде чем приступать к разделу 2. Ниже дано несколько трюков 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. Еще одна функция расшифровки
Вопросы дня
Основная часть функции состоит из многоуровневых циклов, которые декодируют и перезаписывают (часть) сегмента кода. Вокруг этой функции и будут строиться наши сегодняшние «вопросы дня»:
- Как выйти из цикла? [Подсказка: отладчик IMM услужливо вырисовывает структуры циклов (каждый цикл обозначается при помощи сплошных линий, с левой стороны). Разместите брэйкпойнт на первой инструкции, идущей после конца цикла, - посмотрите на адрес 0x00413B1C]
- Какая часть кода/стэка была модифицирована? Какие начальные и конечные адреса? [Подсказка: смотрите на инструкции модифицирующие «память», т.е. инструкции по адресам 0x00413A6F, 0x00413A8D и 0x00413B0E]
© Translated by Prosper-H from r0 Crew



Reply With Quote
Thanks
