Анализ атомов1 AV
Показан пример анализа атомов в AV. В данном тесте не используется код самих AV, а только результат тестирования в виде сигнатурного детекта. В следствие этого за один тест может быть проверено одно булево условие.
Как было сказано ранее, в VM код API содержит атомы, которые служат шлюзами VM. Через них управление получает VM и эмулирует API, которые не могут быть выполнены без использования среды VM. Простые API в свою очередь атомов не содержат.
Для тестов используется конструктор(это мотор, который описывает графом код и может собирать код по графу). Это не обязательно для подобных тестов, просто использовано для удобства.
Для детекта шифрованный образ PE декриптуется, выгружается в файл и запускается. Для примера взят Glyn. Так как не все AV его детектят, то для теста других AV следует взять иной образ.
В начале запустим непосредственно декриптор, но после переноса тела API в буфер. Данная манипуляция выполняется конструктором. При этом используется вызов аллокатора, в качестве которого используется VirtualAlloc(). Код конструктора содержит стандартный набор инструкций и не использует системные механизмы(ничего не вызывает вне себя). Это уменьшает его влияние на тесты. Для успешного прохода данного кода AV должен эмулировать аллокатор.
Конструктор пересобирает код API в буфер. Затем этот буфер исполняется, результат исполнения является ключём к декрипту образа. Для тестов взята сложная функция — GetProcAddress(). Результат теста (C1):
Как сказано выше, в тест не включены AV, которые не содержат детект на образ и которые не смогли пройти тестовый код. В буфере код выполнен успешно. Определим число инструкций, из которых состоит тело API. Общее число их описано в графе. Получить его мы не можем, поэтому используем условную конструкцию к заданием возможного числа. Если условие верно (число инструкций < 10), то результатом будет детект (C2):
Только AVG содержит < 10 инструкций в теле этой функции. Увеличим значение до 32 (C3):
Видно, что остальные AV содержат более 10 инструкций. Далее определим структуру кода — наличие в нём ветвлений. Пройдя по всем инструкциям графа найдём ветвления (за исключением инструкции возврата Ret) и при их обнаружении выдадим детект (C4):
Только Ikarus содержит нелинейный код (с ветвлениями). Уточним число инструкций, увеличив лимит до 96 (C3i). Результат тот же. Эта API является сложной реализацией.
Определим какие ветвления присутствуют в данной API у Ikarus (C4i). Перечислив все типы ветвлений, остаётся одно безусловное процедурное ветвление (Call rel).
В теории были описаны способы изоляции выборки данных (DF). Простейший способ обнаружить DF — расширение стека при доступе к сторожевой странице. Из атомов таких обращений нет (если не эмулируются намеренно) — он изолирован от эмулируемой среды. Выполним вызов атома, передав ссылку на гвард страницу стека: После возврата из API проверим стек на расширение и если он не расширен, то выдадим детект (C5).
Изменим условие проверки на обратное для наглядности (C5e)...
Проверим расширение стека непосредственно на инструкциях, что бы узнать поддерживается ли данный механизм. Обратимся напрямую к сторожевой странице и при расширении стека выдадим детект (С6). Ikarus данный механизм не поддерживает. Только Kaspersky выполняет расширение стека API. В таком случае выполняется доступ из тела API к её аргументу, либо стек расширяется при валидации указателей в VM (намеренно):
Разложить код на трассу можно тремя путями — машинной трассировкой (TF), эмуляцией или динамической эмуляцией (DYE). Выполним (C7) это что бы получить доступ к атому (далее можно его исследовать непосредственно)...
Функция трассирована через элементарный DYE-цикл. Детекты BitDefender и его клонов исчезли. Из этого следует что его атомы привязаны к положению частей кода, либо состоят не из одной инструкции, так как при выполнении атома в буфере эмуляция прекращается, убедимся в этом вставив после возврата из цикла DYE прямой вызов декриптора (C8):
Определим номер инструкции, которая является атомом. Этим событием является возврат из атома и соответственно возврат значения в регистр. Зададим для примера минимальное значение в 5 итераций (С9):
У AVG атом расположен на 5-й инструкции. Передадим инвалидный указатель в атом, прежде зарегистрируем ловушку (SEH) и при срабатывании ловушки выведем детект (C11):
AVG и Kaspersky разворачивают исключение при обнаружении инвалидного указателя. Обернём вызов API в SEH и передадим инвалидный указатель (C12):
Появляется детект BitDefender, таким образом он не разворачивает исключение, а возвращает управление из атома. Интересно определить какой адрес передаётся в ловушку...
Касперский расширяет стек из атомов, но можно исследовать его на несколько выборок данных за один вызов, расположив указатели в разных страницах стека и выбрав API с несколькими указателями.
Показан принцип исследования тела апи/атомов. Можно исследовать другие AV (к примеру VBA и Yandex так же успешно проходят пересборку и аллокатор).
Путём деления булевого условия может быть выполнено чтение тела API (подобно как у бинарных деревьев). Но такое чтение требует множество итераций.
© Indy, 2017
Анализ атомов AV
Дата публикации 27 фев 2017
| Редактировалось 10 янв 2018