Что нового

Article Коварный Локи: Как авторы вредоносного ПО обманывают дизассемблеры

Shodus 0

Shodus

Newbie
11.07.2020
14
27
Доброго времени суток. Разговор сегодня пойдёт о техниках антидизассемлировании. Надеюсь, что будет интересно :)

1608726418468.png


План рассказа:
  • Разберёмся, чем отличается "линейный дизассембрер" от "поточного"
  • Изучим популярные техники антидизассемблирования
  • Исследуем программу с техниками антидизассемблирования


Чем отличается "линейный дизассембрер" от "поточного"
Дизассемблер - это инструмент, преобразующий машинный код, объектный файл или библиотечные модули в текст программы на языке ассемблера. По режиму работы с пользователем делятся на автоматические ("Линейные") и интерактивные ("Поточные"). Основная трудность для дизассемлера - это отличить код от данных.

"Линейные дизассемблеры" последовательно дизассемблируют каждую отдельную инструкцию не думая о какой-либо логике. Алгоритмы линейного дизассемблирования часто неспособны отличить код от данных и поддаются искажению сильнее, чем поточные.

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

Запомните эти факты: В листинге дизассемблера любой байт программы обязан принадлежать только одной инструкции. Любой дизассемблер сначала обрабатывает ложное ответвление при условиях в программе (if ... else ...).

Популярные техники антидизассемблирования

Ложные условные переходы и инструкции call ведущие в пустоту

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

jz_jnz.png


Адрес перехода - 0x401519+1, по адресу 401519 находится инструкция call 40A81588h. Вызвать что-либо по адресу 40A81588h невозможно. Это тоже приём антидизассемблирования. Первый байт инструкции call это E8 ( 0x401519 ). Этот байт лишний. jz 401519+1 указывает на адрес 0x40151A. Байт E8 можно убрать и ассемблерный код станет более понятным.


Исправить такие непонятные инструкции помогут клавиши D и C в IDA RPO.
Клавиша D превращает текущий участок в данные.
Клавиша C превращает текущий участок в код.

Превратим байт 0xE8 в 0x90 (инструкция ничего не делать nop). Код поменялся и стал понятным.

2020-12-21_02-31.png


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


Второй вариант ложного перехода


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

2020-12-21_02-37.png


На картинке ниже перед переходом jz 4011D4+1, находится инструкция xor eax, eax. Результат операции xor для регистра с самим самой будет равен 0. Следовательно переход jz 4011D4+1 будет происходить всегда.
Здесь такая же ситуация с инструкцией call. Превратим байт 0xE8 в 0x90 (инструкция ничего не делать nop). Код поменялся и стал понятным.


Во всех ложных условных переходах можно заменить некоторые байты на nop ( 0x90 )или вообще поменять на инструкцию jmp для более хорошего чтение кода.

Исключение не для ошибок, а для маскировки

Теория

В архитектуре x86 существует механизм структурированной обработки исключений (Structured Exception Handling, SEH). Он позволяет управлять потоком выполнения так, чтобы отладчики и дизассемблеры не смогли отследить этот поток.

Цепочку выполнения SEH можно представить, как список функций, предназначенных для обработки исключений в пределах потока выполнения. Постараюсь объяснить SEH как я понимаю.

Например, есть исключение деление на ноль. Исключение попадает в список SEH -> Каждая функция в SEH проверяет, что это исключение подходит для данной функции -> Если никакая функция не смогла обработать исключение, исключение считается не обработанным и появляется диалоговое окно с ошибкой.
OC ищет цепочку SEH таким способом: ОС исследует регистр FS, содержащий сегментный селектор ( для получения доступа к блоку переменных окружения потока (TEB, thread environment block) ) -> исследует первый блок информации потока (TIB, thread information block) -> находит первый элемент внутри TIB, который является указателем на цепочку SEH ( EXCEPTION_REGISTRATION ).

Первый элемент в записи EXCEPTION_REGISTRATION указывает на предыдущую запись ( prev ). Второе поле является указателем на функцию-обработчик ( Handler ). SEH по принципу работы похож на стек.

SEH-5.jpg


Как используют уязвимость в SEH авторы вредоносов

Для искажения управления потоком с помощью SEH достаточно лишь уметь добавлять собственные обработчики на вершину списка. Часто в коде, где используется такая техника можно встретить элемент fs:[0] ( Она указывает на запись, которая находится на вершине стека в данный момент)

Этот вариант кода часто можно встретить в вирусах с использованием искажения SEH ( ExceptionHandler может называться по другому ).

C-подобный:
push ExceptionHandler ; функция-обработчик
push fs:[0] ; указатель на следующую запись
mov fs:[0], esp ; пытаемся поместить элемент на вершину цепочки
Против этой уязвимости компания Microsoft разработала Software Data Execution Prevention (DEB). Её часто называют SAFESEH. Она не позволяет добавлять сторонние обработчики исключений во время выполнения кода. Эту технологию можно обойти. Например в компиляторе от Microsoft отключить SAFESEH можно с помощью параметра /SAFESEH:NO

Пример реализации этого способа антидизассемблирования вы можете посмотреть в видео ниже.

Фальшивый адрес возврата для инструкции retn и отрицательный стек

Иногда злоумышленник может подменить адрес возврата ( retn ... ) из программы. Программа продолжит выполнение в совсем другой функции. Это можно использовать, чтобы замаскировать вредоносную программу под обычную.
Инструкция retn берёт первую запись из стека для адреса возврата. Обращаете внимание на стек во время инструкции retn. В функции main инструкцию retn можно встретить очень редко. Обращаете внимание в каких местах используется retn.

Иногда авторы вредоносного ПО делают стек в некоторых участках кода нулевым. В такой ситуации IDA PRO может ошибаться в некоторых деталях дизассемблирования. Например: указывать неправильное количество аргументов, которые передаются функции или не указывать перекрёстные ссылки. Чтобы отслеживать стек, перейдите в пункт Options -> General и включите Stack Pointer.


Пример реализации этих способов антидизассемблирования вы можете посмотреть в видео ниже.

Практика

Для практики возьмём программу Lab15-03 из книги Практический анализ вредоносного ПО. В этой программе достаточно интересно изучать техники, описанные выше. Расписывать здесь будет слишком долго и может быть не понятно. Поэтому, я решил попробовать записать видео.


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

1608726090843.png
 
Shodus 0

Shodus

Newbie
11.07.2020
14
27
Я оговорился в видео. Инструкция retn может быть в функции main и это не подозрительно.
Насторожиться можно если инструкция retn будет переходить по не системному или подозрительному адресу.
 
Последнее редактирование:
Верх Низ