Анализ GuLoader: как подойти к деобфускации сложных образцов

Анализ GuLoader: как подойти к деобфускации сложных образцов

23 июня 2023 г.

Как исследователь вредоносного ПО, вы часто сталкиваетесь со сложными, сильно запутанными образцами. И GuLoader, вредоносное ПО, с которым мы сегодня имеем дело, является классическим тому примером.

Взгляните на этот псевдокод, полученный путем декомпиляции ассемблерного кода — он уродлив и нечитаем.

Столкнувшись с такой загадкой, вы можете растеряться. С чего вообще начать? Как вы относитесь к анализу этого образца? Давайте разберемся.

В этой статье мы рассмотрим стратегии деобфускации такого кода, используя GuLoader в качестве эталона. Вы узнаете о:

* Распространенная тактика запутывания, используемая злоумышленниками * Как их победить * Как деобфусцировать образец GuLoader

Статья основана на анализе вредоносного ПО GuLoader ранее опубликованный ANY.RUN. Посетите наш блог, чтобы найти образец, который мы будем анализировать, а также инструкции по распаковке и скрипт Ghidra, который частично автоматизирует многое из того, что мы собираемся охватить с этого момента.

Эта статья посвящена статическому анализу. Но если вы хотите динамически анализировать образец GuLoader, вы можете использовать ANY.RUN облачную изолированную программную среду для вредоносных программ.

Подать заявку на бесплатную 14-дневную пробную версию плана Enterprise, используя корпоративную электронную почту. Воспользуйтесь увеличенным сроком службы экземпляров ВМ, неограниченным количеством задач и версиями Windows с 7 по 11.

Динамический анализ позволяет увидеть, как вредоносное ПО работает в реальном мире, связав его поведение с его технической настройкой. Это способ проверить его действия при различных настройках системы и собрать IOC.

Не откладывая дальше, давайте углубимся.

Определение методов запутывания

После распаковки образца мы начинаем с ручного анализа шелл-кода и быстро понимаем, что он запутан. Изучение кода позволяет нам сгруппировать методы запутывания, которые использует GuLoader, по категориям:

* XMM-инструкции * Безусловные инструкции JMP * Мусорные инструкции * Поддельные инструкции по сравнению * Поддельные инструкции PUSHAD * Поддельные инструкции PUSH * Непрозрачные предикаты * И арифметические выражения

Сейчас самое время остановиться, проанализировать используемые методы запутывания и разработать стратегию дебофускации.

Например, в нашем случае большинство этих методов вводят код, который не меняет окончательный результат выполнения. Поэтому мы часто можем безопасно «NOP» их, чтобы улучшить читабельность. Но действуйте с осторожностью — не весь запутанный код не имеет отношения к работе программы, как мы скоро обнаружим.

Теперь давайте рассмотрим эти методы запутывания по отдельности и посмотрим, как их обойти.

1. Инструкции XMM

Код изобилует множеством инструкций XMM. Они кажутся беспорядочными и усложняют процесс анализа. Мы сталкиваемся с ними прямо с первого байта распакованного шеллкода.

Обратите внимание, что многие механизмы эмуляции спотыкаются о них из-за отсутствия поддержки по умолчанию. Мы проверили встроенные механизмы Angr, Triton и Ghidra — все они не справились.

Как мы с этим справились

В случае с GuLoader инструкции XMM фактически не влияют на предполагаемое поведение кода. Вы столкнетесь с подобными методами обфускации во многих вредоносных программах. Следовательно, мы можем безопасно заменить все инструкции XMM на «NOP», как показано в следующей таблице:

Вот как результат выглядит в дизассемблере Ghidra:

2. Безусловные инструкции JMP

Безусловные инструкции JMP сегментируют код на более мелкие фрагменты. Этот метод часто используется, чтобы избежать обнаружения антивирусным программным обеспечением и другими инструментами безопасности. Кроме того, это может сделать работу аналитиков более трудоемкой и утомительной, поскольку им приходится переключаться между этими блоками, особенно при работе с большим объемом кода. GuLoader и другие вредоносные программы обычно используют этот метод.

Как мы с этим справились

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

3. Нежелательные инструкции

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

Вы встретите их и в GuLoader.

Обращайте внимание на инструкции, которые не выполняют никаких действий ("NOP", "FNOP") и те, которые сдвигают или сдвигают на ноль биты ("SHL reg, 0"; "ROL reg, 0"). Также присутствуют другие не имеющие значения инструкции, такие как «OR reg, 0», «XOR reg, 0», «CLD», «WAIT».

Как мы с этим справились

Исправить поддельные инструкции по сравнению сложнее, чем просто заменить ненужные на "NOP". Мы не можем удалить все инструкции сравнения, так как некоторые из них необходимы для правильной работы кода. Один из способов решить эту проблему — «пометить» все инструкции сравнения, с которыми мы сталкиваемся. Если инструкции, использующие результат сравнения, не найдены, его можно безопасно отменить. Если мы находим условный переход или что-то подобное, мы снимаем пометку со сравнения, чтобы избежать удаления.

В следующей таблице показан пример, в котором все инструкции сравнения, кроме "CMP EDX,0x0", были выборочно заменены на NOP:

5. Поддельные инструкции PUSHAD

GuLoader также использует тактику запутывания, используя поддельные инструкции "PUSHAD" в сочетании с соответствующей инструкцией "POPAD". Они могут временно изменять значения регистров, но аннулируются "POPAD", восстанавливающим исходные значения регистров.

Как мы с этим справились

Наше исследование показало, что все инструкции "PUSHAD" в GuLoader являются посторонними. Итак, мы решаем эту проблему, заменяя «PUSHAD», «POPAD» и промежуточные инструкции на NOP:

Однако не все инструкции "POPAD" в GuLoader являются ненужными. Мы оставляем без соответствующего "PUSHAD" нетронутыми.

6. Поддельные PUSH-инструкции

Еще один метод запутывания, аналогичный предыдущему, — использование поддельных инструкций "PUSH". Эти инструкции помещают значение в стек только для того, чтобы немедленно извлечь его.

Примером может служить включение инструкции «PUSH SS», за которой могут следовать инструкции, изменяющие регистр или ячейку памяти. Однако последующая команда "POP SS" восстанавливает указатель стека до его исходного значения.

Как мы с этим справились

Отмена поддельных инструкций PUSH похожа на процесс поддельных PUSHAD, но очень важно не изменять неотправленные регистры.

7. Непрозрачные предикаты

Непрозрачные предикаты — это условные операторы, которые всегда возвращают значение true или false, однако их сложно анализировать или прогнозировать. Они встречаются в коде GuLoader и усложняют понимание логики.

Например, за парой инструкций, таких как «MOV BL, 0xB6» и «CMP BL, 0xB6», может следовать условный переход, такой как «JNZ ADDR». Сравнение всегда возвращает false, так как сравниваемое значение равно перемещенному значению, что делает переход ненужным и сбивающим с толку.

Как мы с этим справились

Преодоление непрозрачных предикатов может показаться сложной задачей из-за необходимости «предсказывать» условия перехода. Однако наша ситуация более проста, так как все непрозрачные предикаты попадают в блоки «PUSHAD» и «POPAD». Поэтому мы просто заменяем все предикаты между этими инструкциями на NOP.

8. Арифметические выражения

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

Одним из примеров является перемещение постоянного значения в регистр и выполнение арифметических операций:

Другой пример — помещение постоянного значения в стек и выполнение вычислений в памяти:

Как мы с этим справились

Для деобфускации арифметических выражений GuLoader мы применяем подход, аналогичный обработке фальшивых сравнений. Мы помечаем все инструкции «MOV», в которых второй аргумент является скалярным значением, и все инструкции «PUSH», в которых аргумент является скалярным. Обнаружив арифметическую операцию, мы обновляем значение константы в первой инструкции и заменяем текущее значение на NOP. Таким образом, первая инструкция имеет результат, а последующие арифметические инструкции заменяются NOP.

Ниже приведены примеры с оптимизированными операциями «MOV» и «PUSH»:

Будьте осторожны с размерами операндов. Сохранение правильного размера во время операций имеет решающее значение.

Подведение итогов нашего анализа

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

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

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


Оригинал