Малварь по шагам [№3] Детекты в памяти

Сравнительно новое явление, набирающее популярность у Av-вендоров. Я склонен рассматривать это как развитие проактивных методов. Как это работает?

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

Уже сейчас подобные технологии внедрили NOD32 и MSE (который, кстати, есть в составе windows под именем Windows Defender), возможно кто-то ещё. Следует ожидать, что со временем все вендоры оценят потенциал и заимеют свои аналоги.

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

ApiCall map

Насколько Я помню, NOD32 зачастую использует сигнатуры по карте апи-вызовов. Я не знаю как это называется на жаргоне, но давайте здесь будем использовать термин «Карта Апи вызвов». Принцип создания сигнатур по ApiCall описан здесь .

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

Unique bytecode

Если мне не изменяет память, MSE часто использует сигнатуры по байткоду. Для эксперимента, Я взял несколько детектируемых им семплов и убрал из них шаг за шагом, всё кроме сигнатуры. Во всех случаях это была группа редко встречающихся подряд ассемблерных инструкций.

Например, для Trojan:Win32/Swrort.A это следующий байткод по смещению 0x7BBF:

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

Сигнатуры по хидерам

Забодавший своими фолсами (!) «win32:evo-gen[susp]»  от AVAST, базируется на заголовках pe32. Многие вендоры начинают сканирование и решают нужно ли его углубить исходя из хидеров, что сделано в целях оптимизации.

Во внимание принимается структура секций, их размер, порядок, имена, энтропия и характеристики (RWE\DATA\CODE), вкупе с размером стека и флагами из NtHeaders  — это составляет единую сигнатуру.

Обход сводится к порче хидеров в памяти, после которой часть WinApi, их использующих перестанут корректно работать, что надо иметь в виду. Обычно это выглядит как удаление опорных точек для сканеров, таких как «PE00» и «MZ«.

Мойте семплы перед едой

Исходя из того, что для детектирования используется старый добрый файловый сканер, существует только один метод сделать файл не детектируемым в памяти — предварительно обкатать некриптованную тушку на всех сканерах существующих!

Для этого есть утилита SignFinder

22 thoughts on “Малварь по шагам [№3] Детекты в памяти

  1. Отличная, замечательная статья коих не хватает в нынешнее время. priv8
    Автору поклон.

  2. А копипаст статьи можно сделать, со ссылкой сюда, действительно хорошая статья, спасибо ! 🙂

    1. Конечно можно, но копировать смысла нет, ибо я иногда дополняю текст \ правлю опечатки.

  3. И ещё не много не понял фразу:

    «Существует только один метод сделать файл не детектируемым в памяти – предварительно обкатать некриптованную тушку на всех сканерах существующих!»

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

    1. Существует другой метод, это не дать в памяти находится всему телу. Раскодируется и исполняется одна инструкция.

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

      Эвристики детектирующие кодогенерацию пока ещё довольно слабы, можно обходится простыми решениями.

      1. У меня оптимизатор мусор выбрасывает. Компилируется только без оптимайзера с мусором.

        Я так понимаю, чтобы этого не было трэшген должен использовать результаты работы реального кода? То есть надо пропарсить локальные переменные и параметры функции и вставлять их в трэшген?

        1. Скорее всего дело в плохом мусоре, твой трешген умеет создавать\вызывать функции и делать вызовы winapi?

          Логика оптимизатора — если результат работы кода нигде не используется, то его можно выкинуть.

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

          1. Да, спасибо. Действительно не умеет. Исправлю этот недочет.

            И еще, последний вопрос. Подскажи, а как часто стоит вставлять эти «комментарии» для генерации мусора? Просто я пилил трэшген, который вставляет после каждой строки мусор.

            Есть ли какие-то определенные правила по которым я могу понять, что именно сюда мне стоит вставить «флаг» мусора? Спасибо.

            1. Уверен что генерацию шаблона из исходника можно автоматизировать, единственная причина ручного контроля — избежать ситуации при которой блок мусора на 100500 тактов вставляется внутрь цикла (или другое критичное место) и процесс виснет (загружая ядро процессора) на неопределенное время.

  4. прочитав ответы про тг (трешген) стало интересно. А что если использовать opaque predicates для ускорения работы треш кода? Ну то есть, есть у нас база вынапи спаршенная и мы знаем, что оно обнуляет какой-то регистр. И вот мы после вызова чекаем и если обнуления не было, то выполняем тг код, иначе выполняем реальный. Так же можно генерить мертвый код любой вложенности, который никогда не исполнится — такое авер задетектит? Или не стоит играться с opaque predicates?

    p.s. алсо не затруднит поделиться базой вынапишек?)

    1. Почитал про «opaque predicates», насколько понял — выражение, результат которого известен генератору, но трудно получить без исполнения кода.

      Ты предлагаешь пускать эмулятор по ложному следу, что есть банальный антиэмуль. Если есть гарантия что он сработает на всех эмуляторах — хватит одной проверки.

      Чтобы знать какой регистр обнуляется необходимо фазить, а не парсить WinApi . Метод прямо скажем не самый надежный.

      Трешген хорош тем что он стабилен и выжирает ресурсы эмулятора не давая ему повода для беспокойства.

      На счёт детектирования мертвого кода — сложно сказать, движков много и они разные.

      з.ы. Про базу уже отвечал, берите их от Rohitab API Monitor.

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

          1. По самим алгоритмам, как генерить качественный запутанный код. То есть, примерная структура как у меня:

            Есть некоторая таблица генераторов блоков

            I. Простые операции:
            1) математика (ксоры, добавления и т.д)
            2) вызовы апи
            3) вызовы функций сгенеренных (тут затык небольшой т.к почему-то иногда не компилит после генерации)

            II. Блоки
            1) for
            2) while…do
            3) do…while
            5) ветвление

            И вот в нужное место алгоритм выбирает либо сгенерить блок, либо операцию, и так рандомно. В итоге получается вложенная конструкция «непойми-чего» а-ля треша. Но как по мне качество такого треша — не очень.

            Как его сделать более запутанным, качественным, что ли. А то получается примерно такое:

            https://pastebin.com/raw/BxfAGsMS

            лично мне кажется что качество не очень. Или норм? Примерно такое выходит… У меня генератор апишек сейчас очень слабый, всего пару апи вручную захардкодил, чтобы оптимайзер не вырезал… Хотя вон в примере while сгенерил который оптимайзер вырежет…

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

          2. и еще. Я не совсем понял как юзать ту базу апи корректно. Ну, допустим там есть поинтер в параметрах, а как я узнаю, что в результате вызова апи не крашнет приложуха или апишка не создаст файл с именем из трех известных всем букв («лол») на рабочем столе, образно говоря?

            1. >как я узнаю, что в результате вызова апи не крашнет?

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

              Проблема в том что разные версии windows (и даже sp) могут вести себя по разному. Необходимо найти Api которые ведут себя одинаково. В конце финальный список дорабатывается руками.

Добавить комментарий