Разбор буткита

    Всем привет! В связи с запуском курса «Реверс-инжиниринг» мы провели плановый открытый урок. На нём разобрали алгоритм работы буткита на разных стадиях его загрузки.



    Преподаватель — Артур Пакулов, вирусный аналитик в Kaspersky Lab.

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

    Буткит (Bootkit) — вредоносная программа, осуществляющая модификацию Master Boot Record — первого сектора первого физического диска либо загрузочного сектора — VBR. Программы такого рода, в основном, имеют троянский функционал и используются для осуществления каких-либо скрытых действий в системе. В нашем примере, буткит осуществляет повышение привилегий до уровня системы процессу, имя которого начинается на последовательность букв: “inte”. Фактически, буткит — это руткит, начинающий работать в 0 кольце защиты, который запускается ещё до начала загрузки операционной системы. Именно из-за этого он и представляет большой интерес для исследования.

    Чтобы разработать такую программу, обычных навыков реверс-инжиниринга недостаточно. Мало просто уметь читать листинг, нужно ещё и понимать такие вещи, как архитектура процессора, адресация памяти и т. д. Ключевые места буткита мы и посмотрели на открытом уроке.

    Для работы был подготовлен специальный семпл bootkit-xp.exe, работающий под Windows XP. Поэтому, кроме изучения буткита, немного поностальгировали по этой операционной системе. Но вообще, ОС XP была выбрана для того, чтобы проще было реверсить, так как XP — это хорошая наглядность и отсутствие лишних осложнений. Ну и, семпл был написан именно под эту OS, судя по его коду.

    А вот как выглядит инсталлятор буткита:



    Просто глядя на него, можно сделать определённые выводы. Например, сразу бросается в глаза, что этот файл имеет маленький вес. Если же глянуть на точку входа, то можно заметить, что в коде идёт получение дескриптора файла с именем символьной ссылки на первый физический диск — “PhysicalDrive0”:



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



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

    Продолжаем исследовать код.



    Как мы уже видели в HIEW, вызывается функция CreateFileA с интересным аргументом в качестве первого параметра Что именно тут делается? Тут уместно вспомнить про такое понятие, как объекты ядра. Ими нельзя манипулировать напрямую из режима пользователя, ими управляет ядро операционной системы. Из user mode программа может только сделать запрос на получение/изменение состояния какого-либо объекта ядра. Чтобы указать системе, с каким именно объектом ядра будет работать программа, требуется получить хендл нужного объекта ядра. При запросе на получение, если все проверки будут пройдены, OS вернёт нам handle запрашиваемого ОЯ. А уже используя handle, мы можем работать со связанным с ним ОЯ.

    Таким образом, на приведённом выше изображении с помощью CreateFile идёт обращение через символьную ссылку к первому подключенному физическому жёсткому диску. Если доступ предоставляется, то работать с таким “файлом” можно будет как и с любым другим простым файлом. Т. е. весь жёсткий диск целиком будет представляться как один большой файл.

    Итак, продолжим. Handle вернулся, и мы оказываемся здесь:



    Что дальше происходит? А дальше функция ReadFile, считывает первые 0x200 байт. А находится у нас там самый первый сектор первого физического диска.



    Как вы уже догадались, это Master Boot Record (MBR). MBR состоит из 3 частей: кодовой части, таблицы разделов и сигнатуры. В обычной ситуации bios вычитывает MBR в память по адресу 0:0x7c00h, передавая на неё управление. Так, кодовая часть MBR начинает выполняться. В процессе выполнения она парсит таблицу разделов, находит загрузочный сектор и грузит его. В случае буткита, если MBR будет перезаписан, его код уже сейчас получит управление.

    Ок, MBR считан а, что дальше? А дальше буткит снова открывает PhysicalDrive0, но с режимом доступа на запись.



    Дальше устанавливается указатель по 600-му смещению. То есть оригинальный сектор считывается и копируется в третий сектор.

    Зачем бэкапить сектор? Очевидно, что это нужно для того, что он понадобится в дальнейшем.

    Дальше запускается цикл. Естественно, глядя на код, нельзя не обратить внимание на константы типа var_1C, 1BEh и прочие. И, заодно, следует освежить в памяти структуру MBR, размещённую выше. В частности, нас интересует колонка «Смещение».



    Смотрите, прочитанный буфер первого сектора находится в lpBuffer. Далее к нему прибавляется 1BEh, Фактически, указатель направляется на начало таблицы разделов. Все данные, начиная с таблицы и до конца сектора вставляются в _marked_bytes, начиная с того же смещения — 1BE.



    То есть, в _marked_bytes вставляется вторая и третья часть оригинального MBR.

    Что происходит дальше? А дальше SetFilePointer устанавливает указатель в самое начало нашего “файла”, т. е. на MBR.



    Потом происходит запись (WriteFile) сформированного _marked_bytes и освобождение памяти. На этом функционал установщика буткита заканчивается.

    Но неплохо было бы посмотреть, а что именно находится в первой части _marked_bytes. Для этого, сдампим её на диск и проанализируем. Первое, что бросается в глаза, — уменьшение на 2 содержимого переменной по адресу 0x413.



    Если посмотреть техническую документацию, то можно найти, что переменная по адресу 0X413 содержит количество установленной физической памяти в килобайтах. Соответственно, код буткита “отрезает” два килобайта памяти:



    Теперь, чтобы не делалось дальше, будет считаться, что имеется на 2 килобайт памяти меньше, чем было. Зачем — пока непонятно.

    Дальше происходит рассчитывание физического адреса к “откусанному” куску памяти с помощью побитового сдвига влево на 6 разрядов:



    Сдвиг на 6 делает два действия разом — переводит килобайты в байты (домножив значение переменной на 2^10), тем самым получив физический адрес к откусанному куску памяти, и извлекает из него номер сегмента, поделив результат на 0x10 (2^4).

    После произойдёт копирование своего тела по этому куску памяти, причём, т. к. код буткита находится именно в “откусанном” куске, его дальше уже никто не потревожит. Мало того, никакие прерывания не изменят того, что написано в этой области памяти. Можно сказать, что код становится фактически невидимым для системы, как будто там и нет никакой памяти.

    Это только самое начало работы буткита. Дальше будет перехват прерывания, отслеживание сигнатуры ntldr, модификация модуля ядра ОС и т. д…

    Поэтому не будем спойлерить, лучше досмотрите вебинар до конца, чтобы ничего не пропустить.
    OTUS. Онлайн-образование
    Цифровые навыки от ведущих экспертов

    Похожие публикации

    Комментарии 0

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

    Самое читаемое