Анализ атомов AV

Дата публикации 27 фев 2017 | Редактировалось 10 янв 2018

Анализ атомов1 AV

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

Как было сказано ранее, в VM код API содержит атомы, которые служат шлюзами VM. Через них управление получает VM и эмулирует API, которые не могут быть выполнены без использования среды VM. Простые API в свою очередь атомов не содержат.

Для тестов используется конструктор(это мотор, который описывает графом код и может собирать код по графу). Это не обязательно для подобных тестов, просто использовано для удобства.
Для детекта шифрованный образ PE декриптуется, выгружается в файл и запускается. Для примера взят Glyn. Так как не все AV его детектят, то для теста других AV следует взять иной образ.
В начале запустим непосредственно декриптор, но после переноса тела API в буфер. Данная манипуляция выполняется конструктором. При этом используется вызов аллокатора, в качестве которого используется VirtualAlloc(). Код конструктора содержит стандартный набор инструкций и не использует системные механизмы(ничего не вызывает вне себя). Это уменьшает его влияние на тесты. Для успешного прохода данного кода AV должен эмулировать аллокатор.

Конструктор пересобирает код API в буфер. Затем этот буфер исполняется, результат исполнения является ключём к декрипту образа. Для тестов взята сложная функция — GetProcAddress(). Результат теста (C1):

[​IMG]

Как сказано выше, в тест не включены AV, которые не содержат детект на образ и которые не смогли пройти тестовый код. В буфере код выполнен успешно. Определим число инструкций, из которых состоит тело API. Общее число их описано в графе. Получить его мы не можем, поэтому используем условную конструкцию к заданием возможного числа. Если условие верно (число инструкций < 10), то результатом будет детект (C2):

[​IMG]

Только AVG содержит < 10 инструкций в теле этой функции. Увеличим значение до 32 (C3):
[​IMG]

Видно, что остальные AV содержат более 10 инструкций. Далее определим структуру кода — наличие в нём ветвлений. Пройдя по всем инструкциям графа найдём ветвления (за исключением инструкции возврата Ret) и при их обнаружении выдадим детект (C4):

[​IMG]

Только Ikarus содержит нелинейный код (с ветвлениями). Уточним число инструкций, увеличив лимит до 96 (C3i). Результат тот же. Эта API является сложной реализацией.

Определим какие ветвления присутствуют в данной API у Ikarus (C4i). Перечислив все типы ветвлений, остаётся одно безусловное процедурное ветвление (Call rel).

В теории были описаны способы изоляции выборки данных (DF). Простейший способ обнаружить DF — расширение стека при доступе к сторожевой странице. Из атомов таких обращений нет (если не эмулируются намеренно) — он изолирован от эмулируемой среды. Выполним вызов атома, передав ссылку на гвард страницу стека: После возврата из API проверим стек на расширение и если он не расширен, то выдадим детект (C5).

[​IMG]
Изменим условие проверки на обратное для наглядности (C5e)...
[​IMG]

Проверим расширение стека непосредственно на инструкциях, что бы узнать поддерживается ли данный механизм. Обратимся напрямую к сторожевой странице и при расширении стека выдадим детект (С6). Ikarus данный механизм не поддерживает. Только Kaspersky выполняет расширение стека API. В таком случае выполняется доступ из тела API к её аргументу, либо стек расширяется при валидации указателей в VM (намеренно):

[​IMG]

Разложить код на трассу можно тремя путями — машинной трассировкой (TF), эмуляцией или динамической эмуляцией (DYE). Выполним (C7) это что бы получить доступ к атому (далее можно его исследовать непосредственно)...

[​IMG]

Функция трассирована через элементарный DYE-цикл. Детекты BitDefender и его клонов исчезли. Из этого следует что его атомы привязаны к положению частей кода, либо состоят не из одной инструкции, так как при выполнении атома в буфере эмуляция прекращается, убедимся в этом вставив после возврата из цикла DYE прямой вызов декриптора (C8):

[​IMG]

Определим номер инструкции, которая является атомом. Этим событием является возврат из атома и соответственно возврат значения в регистр. Зададим для примера минимальное значение в 5 итераций (С9):

[​IMG]

У AVG атом расположен на 5-й инструкции. Передадим инвалидный указатель в атом, прежде зарегистрируем ловушку (SEH) и при срабатывании ловушки выведем детект (C11):

[​IMG]


AVG
и Kaspersky разворачивают исключение при обнаружении инвалидного указателя. Обернём вызов API в SEH и передадим инвалидный указатель (C12):

[​IMG]

Появляется детект BitDefender, таким образом он не разворачивает исключение, а возвращает управление из атома. Интересно определить какой адрес передаётся в ловушку...

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

Показан принцип исследования тела апи/атомов. Можно исследовать другие AV (к примеру VBA и Yandex так же успешно проходят пересборку и аллокатор).
Путём деления булевого условия может быть выполнено чтение тела API (подобно как у бинарных деревьев). Но такое чтение требует множество итераций.


© Indy, 2017

3 6.023
Indy_

Indy_
Well-Known Member

Регистрация:
29 апр 2011
Публикаций:
4

Комментарии


      1. Indy_ 28 фев 2017
        sl0n

        > Инди посмотри в сторону АПИ вирустотала и накатай себе простейший скрипт

        Какой скрипт и зачем ?
      2. sl0n 28 фев 2017
        Инди посмотри в сторону АПИ вирустотала и накатай себе простейший скрипт на питоне для автоматизации тестов. Если нужно могу выложить примеры