Взлом и анализ TDL3. Покоряем легендарный руткитАртем Баранов (artembaranov@yandex.ru) Как ты, я надеюсь, уже наслышан, под TDL3 понимают новое поколение руткитов tdss. Его отличают навороченные технологические фичи, которые оставили безоружными все антивирусы и почти все антируткиты. я покажу, как следует разбирать TDL3, в чем его технические особенности и почему антивирусы спасовали перед ним. Первая информация о рутките появилась на rootkit.com в блоге diablonova (rootkit.com/blog.php?newsid=970) и содержала сведения о предыдущих версиях TDL, а также, собственно, о новой версии. Распространение руткита началось где-то с октября, тогда же и вышла упомянутая статья. Бросалось в глаза, что руткит действует как файловый вирус, заражая драйвер-порта жесткого диска; обычно это atapi.sys, находящийся под драйвером класса disk.sys. При заражении размер драйвера не менялся, руткит перезаписывал часть ресурсов драйвера и устанавливал точку входа на себя. Это загрузчик, основная же часть руткита размещается в конце диска. Заражая atapi.sys, руткит предоставлял себе возможность стартовать на самом раннем этапе загрузки ОС. Кроме того, большинство антируткитов даже не определят присутствие руткита в памяти. Перехват диспатч таблицы IRP в объекте-драйвера atapi осуществлен так: при чтении зараженного драйвера-порта руткит подсовывает оригинальный файл. Поэтому все AV шли лесом, а так как руткит работал на самом низком уровне, то даже в перспективе определить заражение файла смогут далеко не все антируткиты. Не говоря уже про лечение. Спустя какое-то время аналитики DrWeb выпустили статью «От Backdoor.Tdss.565 и выше (aka TDL3)» (drweb.com/static/BackDoor.Tdss.565_(aka%20TDL3).pdf), которая раскрывает технические особенности TDL3, а также его последующих модификаций. Они же первые выпустили лечение этого руткита. Немного позднее вышла статья хакера thug4lif3 на rootkit.com (rootkit.com/newsread.php?newsid=979), также рассказывающая о внутреннем мире этого руткита. В отличие от Rustock.C, который все это время держал лидерство среди самых высокотехнологичных руткитов, TDL3 вполне нормально работает на виртуальной машине и под отладчиком, а его код не обфусцирован и его вполне можно разобрать по винтикам под отладчиком. Однако технологическая начинка TDL3 может сбить с толку начинающих хакеров, ибо она весьма не проста, но очень оригинальна. Начинаем копатьИнформации в вышеупомянутых статьях вполне достаточно для получения представления о том, с каким зверем мы имеем дело. А я расскажу по шагам, как исследовать руткит, плюс дам информацию, которая отсутствует в этих статьях. Нам понадобится виртуалка, желательно Windows XP SP3, настроенная для отладки, и Windbg, само собой, IDA для разбора. Загружаемся в обычной non-debug конфигурации, запускаем семпл. Ждем, пока дроппер исчезнет, затем перезагружаемся в debug-конфигурации и подключаем дебаггер. Если заражение прошло успешно, руткит выдаст в debug out строчку типа «This is your life, and it's ending one minute at a time». Полный набор предложений — смотри в статье аналитиков DrWeb. Вообще-то, при запуске руткитов на виртуалке не мешает установить параметр Disable acceleration for binary translation в установках процессора виртуальной машины. Как оказалось, tdss.565 и без него нормально запускается. Итак, первое что смотрим, — перехваты в диспатч-таблице драйвера-порта (на моей виртуалке это atapi.sys). Если сомневаешься в том, какой драйвер у тебя установлен, выполни команду !devstack \device\harddisk0\dr0. Самый нижний драйвер и есть драйвер-порта. Получим его диспатч-функции (загрузив первоначально символы для драйвера, .reload atapi.sys). Идем по этому адресу и видим:
Неплохой способ, обманывающий антируткиты: TDL3 нашел зануленное место в конце секции и вписал туда переход на свой код (очевидно, это общая для всех IRP функция-обработчик руткита). Здесь мы впервые сталкиваемся с его главной структурой, которая адресуется от адреса 0xFFDF0308. Да-да, ты не ошибся, она находится в KUSER_SHARED_DATA, вернее, в ее неиспользуемой (читай, малоиспользуемой) области. Проведя ночь в отладчике, я расковырял ее важные части. Далее я буду часто ссылаться на эту структуру.
Внутри хвостаКак ты понял, руткит состоит из двух частей: загрузчика, который находится в секции ресурсов, и хвоста, который включает в себя весь руткит-функционал. Задача загрузчика руткита — загрузить хвост в память и передать на него управление. Наша же задача — сдампить хвост в файл для последующего анализа в IDA. И здесь можно пойти двумя способами. Во-первых, можно сдампить руткит из памяти. Мы знаем структуру хвоста и что в его начале идет сигнатура TDL3, потом оригинальные ресурсы, а потом код. Берем значение RootkitDispatchFunc из основной структуры (у меня это 818e2e31) и выполняем команду поиска вниз по памяти сигнатуры «TDL3». Теперь осталось сдампить 0x5E00 байт (столько байт загрузчик выделяет для хвоста), начиная со стартового адреса, командой .writemem D:\1.bin 818df000 l5e00. Второй способ: восстанавливаем оригинальные диспатч-функции atapi.sys, копируя их из поля OrigAtapiFuncs структуры руткита и далее дампим секторы с диска с помощью, например, DiskExplorer. Вынести хуки руткита можно одной командой – m, копирующей память из одного буфера в другой; После этого мы получаем доступ к зараженному atapi.sys на диске и скрываемой руткитом области на диске. Сектор, с которого начинается хвост, хранится в поле TailStartDiskSector. Точку входа в хвост легко определить, зареверсив инфицированный atapi.sys. Где-то в конце стартовой функции загрузчика видим код Для разбора хвоста грузим его в иду, при этом указав в стартовом окне смещение, с которого мы дампили хвост в памяти. В дальнейшем будет гораздо удобнее работать с адресами, которые совпадают как в идее, так и в памяти. Загрузив файл, мы сразу можем перейти к разбору основной функции-обработчику IRP в рутките. Делаем dd poi(FFDF0308)+fc l1 и затем по полученному адресу идем в иду, и превращаем код в функцию – клавиша P. VFS руткитаТы, должно быть, в недоумении, зачем руткиту перехватывать все диспатч-функции atapi, когда для скрытия секторов достаточно одной – IRP_MJ_INTERNAL_DEVICE_CONTROL (aka IRP_MJ_SCSI). Драйверы дисков не обрабатывают create/close запросы, потому что не работают с дескрипторами, как FSD, которым нужно инициализировать файловые объекты для открываемых файлов. Такие драйверы (портов жестких дисков) принимают только запросы на чтение/запись секторов и вспомогательные запросы от диспетчера PnP и связанные с питанием. Тот же atapi.sys в незараженном состоянии обрабатывает create/close запросы, просто возвращая STATUS_SUCCESS. Дело в том, что руткит резервирует часть диска (в его конце) под область, где он будет хранить свои файлы, например, dll, которые он инжектит в процессы, а также сам хвост. Там он организует свою ФС, но самое интересное, что руткит монтирует ее на объект \Device\Ide\IdePortX, обрабатывая таким образом запросы на чтение/записи к файлам, которые поступают как из самого руткита, так и из его юзермодной части. Например, сам хвост может обращаться к своей ФС для чтения конфигурационного файла. VFS подробно расписана в white paper DrWeb, поэтому я не буду повторяться, а предоставляю тебе возможность изучить это самостоятельно. В силу того, что руткит виртуализует доступ к своим файлам, Win32 API вполне способны открывать файлы типа \Device\Ide\IdePort1\riyuicvp\tdlwsp.dll. В одной из версий руткита, очевидно, произошла ошибка, и dll, которую руткит должен был удалить из PEBа, оказалась видна. Техническая начинкаТеперь посмотрим на то, как хвост проводит подготовительные действия инициализации. После того, как руткит открыл драйвер-порта на диске, он ищет его объект-устройство с флагом FILE_DEVICE_CONTROLLER, к которому будет примонтирована его FS. Далее производит полную инициализацию своей структуры и заражает драйвер в памяти, кроме того, составляет карту секторов и смещений драйвера-порта, которые он должен скрыть (массив HideAreaEntry). Для затруднения анализа руткита при установке callback-функций он рассчитывает смещения до этих функций, вычисляя дельту, а затем вычитая из нее значение до получения требуемого смещения. Чтобы найти ссылку на функцию, нужно сделать обратную операцию вычитания дельты из смещения и затем найти ее простым поиском. В моем примере функция вычисления дельты выглядит так (смотри картинку «Функция вычисления дельты»). Например, ты нашел функцию завершения и хочешь узнать, в каком месте кода она регистрируется. В примере ее адрес 8198ACA2. Тогда 8198ACA2-8a243384=F774791E. Нажимаем <Alt-B> и вбиваем адрес в обратном порядке, то есть 1E 79 74 F7. Функция же обработки всех IRP является достаточно простой, особенно если знать формат главной структуры руткита. Она выполняет несколько целей: контролирует скрытие данных раздела руткита, скрытие зараженного atapi и обрабатывает функции VFS. Итак, если диапазоны адресов совпадают со скрываемыми, руткит устанавливает свою функцию завершения, в которой будет фильтровать данные. В функции завершения руткит вначале проверяет диапазон секторов для скрываемого раздела и, если есть перекрытие, зануляет данные в буфере. Последние версииБолее поздние версии TDL3 дополнены функционалом, который позволяет обходить антивирусы и антируткиты соответственно. В версии, следующей за вышеописанной, используется несколько другая техника перехватов. Диспатч-таблица функций atapi остается нетронутой, но руткит создает fake-объект драйвера со своими диспатч-функциями и перезаписывает указатель в устройстве, которое обслуживает загрузочный диск, на свой драйвер. |