Rustock.C – секретные техники анализаКрис Касперски Легендарно-неуловимому rootkit'у Rustock.C посвящены десятки технических публикаций, детально описывающих, что именно он делает. Но никто из реверсеров не говорит, как он это выяснил, какие инструменты и методики анализа использовались. Пытаясь заполнить этот пробел, отведаем экзотические блюда хакерской кухни. Rustock.C – словно проливной дождь в знойной пустыне. Настоящий вызов, реально напрягающий мозги и на несколько дней (а то и недель) выбивающий хакера из круговорота повседневной суеты. Окружающий мир исчезает. Остается только монитор, клава, Русток и бесчисленное множество распечаток, осенним листопадом падающих на пол. Rustock.C затягивает, не отпуская даже во сне, заставляя подскакивать среди ночи и лихорадочно опробовать только что вспыхнувшую идею, озарившую, казалось, совершенно неразрешимую проблему. Детект виртуальных машин, куча антиотладочных приемов, многослойное шифрование, полиморфный код, жестокая обфускация, привязка к зараженной машине. Повсюду нестандартные приемы с трюками, использующимися впервые. И все это на самом низком уровне операционной системы в нулевом кольце с активным противодействием ядерным отладчикам и детекторам классических руткитов! Rustock.C – едва ли не единственный вирус, заражающий драйвера и умело обходящий брандмауэры и антивирусы. Короче, тут есть на чем поработать и чему поучиться! Как и следует из его названия, Rustock.C – не первый в своем семействе. До него были версии A и B, построенные по тому же принципу, но гораздо хуже защищенные, а потому начинающим хакерам рекомендуется начинать свой путь именно с них. Когда версия B будет разобрана по винтикам и байтикам, почерк автора вируса станет знаком настолько, что «великий и ужасный» Rustock.C окажется не такой уж непроходимой проблемой. К слову, у меня имеется множество сэмплов, опознаваемых антивирусами как Rustock.C, но радикально отличающихся поведением расшифровщика третьего уровня (в частности, в некоторых сэмплах отсутствует привязка к чипсету, которая есть в других). Существует еще версия E и куча других, однако, анализировать их все смысла нет. Вариации не так уж значительны и ничего нового мы не узнаем, а вот времени убьем изрядно. В поисках Rustock'аЗабавно, но даже антивирусным компаниям пришлось всерьез напрячься, чтобы раздобыть живые образцы легендарного вируса. Где же брать образцы для анализа? В антивирусные компании обращаться бесполезно. Все равно не дадут. Во всяком случае, через официальные каналы. А вот по дружбе, в обход всех должностных инструкций... Тут можно задействовать профессиональные социальные сети, крупнейшей из которых на данный момент является www.linkedin.com. Грубо говоря, www.linkedin.com – то же самое, что «Мой мир», только порядка на два круче и реализованный должным образом. Имея десяток-другой знакомых первого уровня, через списки их контактов можно дотянуться практически до кого угодно, а дотянувшись — познакомиться через общих друзей. Используя их как залог своей лояльности, что я не пойду и не начну распространять полученный образец вируса налево и направо, вполне реально добиться своей цели. На www.offensivecomputing.net (требуется регистрация) есть один экземпляр Rustock.C, однако – нет дропера. Чтобы заставить «зверька» заработать, придется конкретно напрячься. Хотя после небольшой доработки «напильником» он соглашается жить под VMWare и даже размножается (нужно только «отломать» процедуру детектирования, ну и, конечно, подобрать ключ привязки к машине, шифрующий основной код вируса). Другой источник сэмплов — malwaredatabase.net/blog, откуда Rustock.C периодически то появляется, то исчезает. Также можно влиться в ряды распределенной сети «Malware Database Over Dropbox», где малварь хранится не на публичных серверах (которые закрываются так же стихийно, как и открываются), а на локальных жестких дисках членов сети. Короче говоря, это тот же самый eMule, только без координирующих серверов. Rustock.C там есть, в широком ассортименте сэмплов, добытых с различных компьютеров, что существенно упрощает анализ, поскольку мусорный код вычищается путем сравнивания нескольких образцов друг с другом. Для подключения к этому ресурсу необходимо быть принятым в ряды сообщества «Professional Reverse Engineers & Ethical Hackers» (ehre.collectivex.com). Там тоже требуется регистрация, причем регистрация премодерируемая, то есть координатор вправе немотивированно отказать. Лично у меня вступление в ряды заняло с неделю достаточно оживленной переписки, естественно, на английском — чес-слово, процедура устройства в антивирусную компанию с открытием доступа к коллекции вирусов отняла гораздо меньше времени. Затерянный в недрах кодаДобытый образец Rustock.C грузим в HIEW, IDA-Pro или Ольгу. Стоп! А Ольга тут причем? Ведь Rustock.C заражает драйвера режима ядра, а Ольга работает в прикладном режиме. Впрочем, это совсем не мешает ей грузить драйвер как DLL в Ring-3 (где драйвер работать не будет, но первый уровень шифровки из трех снимается Ольгой на счет «раз»… со вторым уже возникают практически непреодолимые трудности). Первый уровень полиморфизмом не страдает и во всех видимых мной образцах выглядит так: Первый уровень шифровки.00010200: 60 pushad За концом расшифровщика следует «мусорный» код, который, собственно, и расшифровывается. Достаточно установить точку останова на команду POPAD, нажать <F9> (Run) и первого слоя шифровки как не бывало. Можно смело сохранять дамп. Решение номер два. Написать скрипт для IDA-Pro, расшифровывая код прямо в дизассемблере (тут исчезают проблемы с возможными ошибками сохранения дампа). Способ надежный, но я ленивый и потому просто рипнул оригинальный код, засунул его в ассемблерную вставку на Си, дописал еще несколько строк, расходующихся на файловый ввод/вывод… Через пару минут появился статический расшифровщик. Статический расшифровщик первого уровня с рипнутым кодом #define BASE 0x00010000 decrypt(char *p) main() В оригинальном коде расшифровщика потребовалось заменить всего одну строку: MOV ESI,000010233h -> MOV ESI, [p]/ADD ESI, 233h (где p – указатель на блок памяти, в который загружен расшифровываемый файл, а 233h – смещение первого зашифрованного байта, следующего непосредственно за командой POPAD). Подобный прием (рипанье кода) — весьма эффективный способ для борьбы даже с навороченными шифровщиками. Правда, если код шифровщика разбросан по десяткам функций, «размазанным» по всей программе, рипанье существенно усложняется и такие защиты уже предпочтительнее снимать в отладчике. Расшифрованный Rustoc-C-unpack загружаем в IDA-Pro. Теперь смотрим на панель навигатора, где синим цветом показан код, а серым — данные. Никакие это, конечно, не данные, а основное вирусное тело. Зашифрованное, разумеется. Расшифровщик второго уровня занимает сравнительно небольшую часть, сбившуюся в левый угол, однако, не стоит надеяться, что он дастся нам так же легко, как и предыдущий! Попытка визуализации расшифровщика второго уровня вгоняет IDA-Pro в глубокую задумчивость. После которой она отображает жуткое хитросплетение графов, похожее на паутину, сотканную обкуренным пауком. Гаснут как бычок в писсуаре и попытки трассировки потока управления, оставляя нас наедине с кучей функцией, условных и безусловных переходов, просмотр которых в укрупненном масштабе показывает, что Rustock.C разбивает код расшифровщика второго уровня на множество мелких блоков. Эти блоки несложно собрать обратно, прогнав программу через отладчик и построив полную трассу потока выполнения, вот только сделать это у нас не получится, поскольку Rustock.C активно сопротивляется отладке! Просматривая код распаковщика второго уровня, мы натыкаемся на кучу привилегированных команд, включающих в себя и обращение к отладочным регистрам. На прикладном уровне это не трассируется в принципе, вызывая исключение. Привилегированные машинные команды в расшифровщике второго уровня .00010C17: 0F21C0 mov eax,dr0 Следовательно, мы должны либо модифицировать код, переписав его так, чтобы он работал в Ring-3 без нарушения функционала – либо воспользоваться эмулятором типа x86emu (Plug-in для IDA-Pro), который лучше всего брать прямо с CVS (https://sourceforge.net/projects/ida-x86emu). Там находится самая свежая версия с кучей фиксов, сильно отличающая от последнего официального релиза. Впрочем, x86emu эмулирует ограниченный набор инструкций/регистров и потому без ручной работы здесь не обойтись. Как вариант, можно попробовать BOCHS (со встроенным отладчиком). Но BOCHS очень медленно работает, а с популярными отладчиками ядерного уровня Rustock.C ведет отчаянную войну. Потому вовсе не факт, что «живая» отладка приведет нас к цели быстрее эмулятора. Обращения к отладочным регистрам — это еще мелочи. Очень быстро мы встречаем код, взаимодействующий с ядерной памятью. В частности, следующий фрагмент осуществляет разбор таблицы экспорта ntoskrnl.exe на предмет поиска необходимых вирусу функций. Как это он делает? Сначала что-то грузит из указателя, полученного из FS:[38h], где на прикладном уровне находится «количество критических секций», принадлежащих потоку (Count of owned critical sections). Но ведь Rustock.C отнюдь не на прикладном уровне работает! А ядро здесь держит Processor Control Region (или, сокращенно, PCR), по смещению 38h от начала которого лежит указатель на глобальную таблицу дескрипторов прерываний (IDT). «Смотрит» она непосредственно в ядро (если, конечно, ее никто не захучил). Как мы это узнали? Раскладка ядерной памяти хорошо описана в документации на SoftICE, а определения самих структур можно найти в NTDDK от Microsoft или обратиться к замечательному ресурсу «Windows Vista Kernel Structures», содержащему практически всю информацию о ядре Висты (www.nirsoft.net/kernel_struct/vista/index.html). Прямой поиск ядра в памяти000138C4 mov eax, large fs:38h ; _KPCR->IDT; Кстати говоря, Lukasz Kwiatek, также исследовавший Rustock.C, приводит очень похожий, но подозрительно «вылизанный» код (www.eset.com/threat-center/blog/?p=127), что наводит на определенные размышления: либо он дербанил другую версию, либо прогнал код через деобфускатор. Прямой поиск ядра в памяти в варианте от Lukasz Kwiatek'a00000261 mov eax, dword ptr fs:38 Возникает резонный вопрос: как жить дальше и что с этим делать? Спускаться в ядро как-то не хочется. И правильно! Поднять ядро на прикладной уровень намного быстрее, да и надежнее! IDA-Pro позволяет грузить куда более одного файла одновременно. Это осуществляется посредством вызова функции load_nonbinary_file(), доступной из plug-in'ов, но отсутствующей в пользовательском интерфейсе. ОК, пишем plug-in, грузящий любые библиотеки и драйвера, какие мы только захотим (включая ядро операционной системы). После чего останется только присобачить несложный эмулятор окружения ядра (чтобы в селекторе FS был не мусор, а валидные данные) и можно смело продолжать эмуляцию посредством x86emu. Ключевой фрагмент plug-in'а, работающий на версиях IDA-Pro вплоть до 4.7 включительно, приведен ниже: Загрузка нескольких файлов в одну базу IDA-Pro 4.7void idaapi run(int arg) Начиная с IDA-Pro 4.8, прототип функции load_nonbinary_file() был злостно изменен без всякой заботы об обратной совместимости. Старые plug-in'ы перестали работать, однако небольшая косметическая операция решает проблему! Загрузка нескольких файлов в одну базу IDA-Pro 4.8+void idaapi run(int arg) С загруженным ядром, расшифровщик второго уровня снимается в IDA-Pro на ура, и мы попадаем в... третий. А вот в нем нас ждет настоящий «подарок» судьбы, высаживающий на измену. Вирус, обращаясь к PCI-шине, извлекает оттуда параметры моста «PCI/ISA», формируя RC4-ключ на основе Device ID и Vendor ID, перебрать которые тупым Brute-Force нереально. Да и не нужно! Роковая ошибка создателя Rustock.C заключается в том, что производителей чипсетов (где, собственно говоря, и находится обозначенный мост) не так уж и много. Просто идем на любую достаточно полную онлайновую базу PCI-устройств (например, www.pcidatabase.com), даем запрос – и осуществляем элегантный перебор на небольшой выборке. Все! С падением последнего бастиона с вирусом можно делать все, что угодно. Например, отломав детектор VMWare (которая определяется через IDT), запустить его в среде виртуальной машины, наблюдая за изменениями в памяти и файловой системе. Никакие маскировочные приемы не помогут против посекторного сравнения образов виртуального жесткого диска (до и после заражения). То же самое относится и к дампам памяти. Вторая ошибка создателя Rustock.С – отсутствие перехвата функции KeBugCheckEx, которая, собственно, и сбрасывает дамп на диск. ЗаключениеА вот некоторые не заморачиваются с ручной распаковкой и эмуляцией, запуская вирусы под доброкачественным виртуализатором. С ним Rustock.C никак не сражается. Помимо SEYE -эмулятора (недоступного широким массам), вполне сгодится и BOCHS, в котором (с учетом наличия исходных текстов) ничего не стоит подделать Device ID и Vendor ID. Правда, мы должны заранее знать, что чипсет установлен на зараженной машине. Пользы (познавательного плана) в подобном способе запуска вируса немного. Нормальные вирусы вообще-то и без танцев с бубном запускаются. Наибольший интерес представляет именно скрупулезный анализ вируса и используемых их приемов, многие их которых стоит взять на вооружение. Если не на свое, так хоть на чужое, в смысле приготовиться к появлению «зверьков», оборудованных модулями, выдранными из Rustock.C (и, возможно, основательно доработанными). WWW Сага о том, как сотрудники DrWeb ловили Неуловимого Джо, а потом поймали и прищемили: drweb.com/upload/a8601a8e66f6ff9a9c629c969482d292_1210059861_DDOCUMENTSArticales_PRDrWEB_Rustock_rus.pdf. Увлекательная детективная история с разоблачением... нет, не вируса, а его создателей, на роль которых выдвигаются парни с краклаба: rootkit.com/newsread.php?newsid=879. Краткое, но самое техничное описание вируса из всех встреченных мною (с описанием прохождения всех уровней шифрования): eset.com/threat-center/blog/?p=127. |