Автор: (c)Крис Касперски ака мыщъх
Легендарно-неуловимому rootkit'у Rustock.C посвящены десятки технических публикаций, детально описывающих, что именно он делает, но никто из реверсеров не говорит - как именно он это выяснил, какие инструменты и методики анализа использовались? Мыщъх предпринял попытку заполнить этот пробел, поделившись экзотическими блюдами хакерской кухни.
Качественная малварь в последнее время - большая редкость. В основном попадается жутко примитивная "шрапнель", написанная на языках высокого уровня с использованием готовых компонентов, выдранных из других вирусов или собранных в автоматизированных конструкторах типа метасплоита. Потом все это дело заворачивается несколькими слоями различных протекторов (опять-таки, широко известных) и после небольшой модификации кода распаковщика (или даже вообще без таковой) выпускается в сеть.
Анализировать такие штуки крайне нудно и делать это можно только из мазохистских побуждений или по долгу служебной необходимости. Ничего интересного там все равно нет. Используемые трюки профессионалам давно известны и обходятся на автомате. Калашникова. Потому что задолбали. Поубивал бы, да и патронов не всех все равно не хватит.
Rustock.C - словно проливной дождь в знойной пустыне. Это настоящий вызов, реально напрягающий мозги и на несколько дней (а то и недель) выбивающий хакера из круговорота повседневной суеты. Окружающий мир исчезает. Остается только монитор, клава, Русток и бесчисленное множество распечаток, осенним листопадом падающих на пол. Rustock.C затягивает, не отпуская даже во сне, заставляя хакера подскакивать среди ночи, лихорадочно опробуя только что вспыхнувшую идею, озарившую казалось совершенно неразрешимую проблему.
Детект виртуальных машин, куча антиотладочных приемов, многослойное шифрование, полиморфный код, жестокая обфускация, привязка к зараженной машине, повсюду нестандартные приемы с трюками, использующимися впервые. И все это происходит на самом низком уровне операционной системы в нулевом кольце с активным противодействием ядерным отладчикам и детекторам классических руткитов! К тому же, Rustock.C - это едва ли не единственный вирус, заражающий драйвера и умело обходящий брандмауэры и антивирусы! Короче, тут есть, на чем поработать и есть чему поучиться, совершенствуя свое мастерство!
Как и следует из его названия, Rustock.C - не первый в своем семействе. До него были версии A и B, построенные по тому же принципу, что и С, но гораздо хуже защищенные, а потому начинающим хакерам рекомендуется начинать свой путь именно с них. Когда версия B будет разобрана по винтикам и байтикам, почерк автора вируса станет знаком настолько, что "жуткий и ужасный" Rustock.C окажется не такой уж непроходимой проблемой. К слову сказать, у мыщъха имеется множество сэмплов, опознаваемых антивирусами как Rustock.C, но радикально отличающимися поведением расшифровщика третьего уровня (в частности, в некоторых сэмплах отсутствует привязка к чипсету, имеющаяся в других).
Также имеется версия E и еще куча других, однако анализировать всех их - смысла нет, поскольку вариации не так уж значительны и ничего нового мы не узнаем, а вот времени убьем изрядно.
Забавно, но даже антивирусным компаниям пришлось всерьез напрячься, чтобы раздобыть живые образцы этого легендарного вируса и это с учетом распределенных систем для отлова малвари с кучей датчиков и сенсоров, рассредоточенных по всему миру, грабящих весь трафик и сохраняющих его длительное время для последующего анализа. Но датчики упорно молчали. Вируса не было. То есть, не то, чтобы совсем не было, но скудность собранного "урожая" вызывает смутные сомнения в цифрах, приводимых различными исследовательскими группами: сколько времени неуловимый Rustock.C жил и как много машин он заразил?
Где же все-таки брать образцы для анализа?! В антивирусные компании обращаться бесполезно. Все равно не дадут. Во всяком случае, через официальные каналы. А вот по дружбе... в обход всех должностных инструкций... Однако, для завязывания знакомств требуется время и если хакер по натуре человек не очень общительный и не вращается в индустрии безопасности, зная всех и каждого, ему придется грызть асфальт зубами или задействовать профессиональные социальные сети, крупнейшей из которых на данный момент является www.linkedin.com.
Рисунок 1. Мыщъх с алиской на LinkedIn (алиска - вторая сверху, знает ассемблер и еще кучу языков, включая Перл, Питон, Си, Руби и...)
Грубо говоря, www.linkedin.com - это то же самое, что "Мой мир", только порядка на два круче и реализованный должным образом. В принципе, все социальные сети построены по общей схеме: "я", "мой друг" и "друг моих друзей", причем количество контактов при переходе от одной ступени к другой увеличивается в геометрической прогрессии! Имея десяток-другой знакомых первого уровня, через списки их контактов можно дотянуться практически до кого угодно, а дотянувшись - познакомится через общих друзей, используя их как залог своей лояльности, что мыщъх не пойдет и не начнет распространять полученный образец вируса налево и направо.
На http://www.offensivecomputing.net (требуется регистрация) есть один экземпляр Rustock.C, однако нет дропера и чтобы заставить "зверька" заработать, придется конкретно напрячь свой хвост (хотя после небольшой доработки "напильником" он соглашается жить под VM Ware и даже размножается, нужно только "отломать" процедуру детектирования, ну и, конечно, подобрать ключ привязки к машине, шифрующий основной код вируса).
Рисунок 2. www.offensivecomputing.net - огромная коллекция малвари на любой вкус.
Другой источник сэмплов - http://malwaredatabase.net/blog/, где Rustock.C периодически то появляется, то исчезает. А еще можно влиться в ряды распределенной сети "Malware Database Over Dropbox", где малварь хранится не на публичных серверах (которые закрываются также стихийно, как и открываются), а на локальных жестких дисках членов сети. Короче говоря, это тот же самый eMule, только без координирующих серверов. На компьютер устанавливается специальный клиент и файл, положенный в определенную папку, немедленно становится доступным всем остальным членам сети.
Rustock.C там есть, причем в широком ассортименте сэмплов, добытых с различных компьютеров, что существенно упрощает анализ, поскольку мусорный код вычищается путем сравнивания нескольких образцов друг с другом.
Для подключения к этому ресурсу необходимо быть принятым в ряды сообщества "Professional Reverse Engineers & Ethical Hackers" - http://ehre.collectivex.com/ (требуется регистрация, причем регистрация премодерируемая, т.е. координатор вправе немотивированно отказать, лично у меня вступление в ряды заняло с неделю достаточно оживленной переписки, естественно, на английском, чес-слово - процедура устройства в антивирусную компанию с открытием доступа к коллекции вирусов отняла у мыщъха гораздо меньше времени).
Рисунок 3. ehre.collectivex.com - сообщество профессиональных реверсеров и хакеров, живущих по понятиям.
На хакерских форумах ссылки на Rustock.C (выложенный на "Рапишиду") попадались не раз и не два, но сейчас все они битые, однако если запастись терпением, то откопать живого зверька вполне можно.
Добытый образец Rustock.C грузим в HIEW, IDA-Pro или Ольгу. Стоп! А Ольга тут причем? Ведь Rustock.C заражает драйвера режима ядра, а Ольга работает в прикладном режиме, что, впрочем, совсем не мешает ей грузить драйвер как DLL в Ring 3, где, конечно, драйвер работать не будет, но первый уровень шифровки из трех снимается Ольгой на счет "раз", а вот со вторым уже возникают практически непреодолимые трудности. Но не будем забегать вперед.
Рисунок 4. Rustock.C в Ольге.
Первый уровень полиморфизмом не страдает и во всех виденных мыщъхем образцах выглядит следующим образом (см. листинг 1, жирным шрифтом выделены значения, варьирующиеся от образца к образцу):
.00010200: 60 pushad .00010201: B9D5030000 mov ecx,0000003D5 ---v (1) .00010206: 31DB xor ebx,ebx .00010208: 31D2 xor edx,edx .0001020A: 81C331085F7D add ebx,07D5F0831 .00010210: 83D200 adc edx,000 .00010213: 49 dec ecx .00010214: 75F4 jne .00001020A ---^ (2) .00010216: BE33020100 mov esi,000010233 ---v (3) .0001021B: 89F7 mov edi,esi .0001021D: B905DF0000 mov ecx,00000DF05 ---v (4) .00010222: 89D8 mov eax,ebx .00010224: C1E803 shr eax,003 .00010227: 01C2 add edx,eax .00010229: 87DA xchg ebx,edx .0001022B: AD lodsd .0001022C: 29D8 sub eax,ebx .0001022E: AB stosd .0001022F: 49 dec ecx .00010230: 75F0 jne .000010222 ---^ (5) .00010232: 61 popad
Листинг 1. Первый уровень шифровки.
За концом расшифровщика следует "мусорный" код, который, собственно, и расшифровывается. Достаточно установить точку останова на команду POPAD, нажать <F9> (Run) и... первого слоя шифровки как не бывало. Можно смело сохранять дамп.
Решение номер два. Написать скрипт для IDA-Pro, расшифровывая код прямо в дизассемблере (плюс исчезают проблемы с возможными ошибками сохранения дампа). Способ надежный, но мыщъх - зверь ленивый и потому просто рипнул оригинальный код, засунул его в ассемблерную вставку на Си, дописал еще несколько строк, расходующихся на файловый ввод/вывод, в результате чего через пару минут появился статический расшифровщик (см. листинг 2).
Листинг 2. Статический расшифровщик первого уровня с рипнутым кодом.
В оригинальном коде расшифровщика потребовалось заменить всего одну строку (в листинге 1 она по адресу 00010216): MOV ESI,000010233h -> MOV ESI, [p]/ADD ESI, 233h, где p - указатель на блок памяти, в который загружен расшифровываемый файл, а 233h - смещение первого зашифрованного байта, следующего непосредственно за командой POPAD. Подобный прием (рипанье кода) - весьма эффективный способ для борьбы даже с навороченными шифровщиками, правда, если код шифровщика разбросан по десяткам функций, "размазанных" по всей программе, рипанье существенно усложняется и такие защиты уже предпочтительнее снимать в отладчике.
Рисунок 5. Первый уровень шифровки снят, а за ним второй! Голубым выделен код расшифровщика, серым - то, что он расшифровывает.
Расшифрованный Rustoc-C-unpack загружаем в IDA-Pro и смотрим на панель навигатора (см. рис. 5), где синим цветом показан код, а серым - данные. То есть, никакие это, конечно, не данные, а основное вирусное тело. Зашифрованное, разумеется. Расшифровщик второго уровня занимает сравнительно небольшую часть, сбившуюся в левый угол, однако не стоит надеяться, что он дастся нам также легко, как и предыдущий!
Рисунок 6. Функции расшифровщика второго уровня, переплетенные тесным клубком.
Попытка визуализации расшифровщика второго уровня вгоняет IDA-Pro в глубокую задумчивость, после чего она отображает жуткое хитросплетение графов, похожее на паутину, сотканную обкуренным пауком (см. рис. 6).
Попытки трассировки потока управления гаснут как бычок в писсуаре, оставляя нас наедине с кучей функций, условных и безусловных переходов, просмотр которых в укрупненном масштабе показывает, что Rustock.C разбивает код расшифровщика второго уровня на множество мелких блоков (см. рис. 7), которые было бы несложно собрать обратно, прогнав программу через отладчик и построив полную трассу потока выполнения, вот только... сделать это у нас не получится, поскольку Rustock.C активно сопротивляется отладке!!!
Рисунок 7. Фрагмент блок-схемы расшифровщика второго уровня.
Просматривая код распаковщика второго уровня, мы натыкаемся на кучу привилегированных команд, включающих в себя и обращение к отладочным регистрам, что на прикладном уровне не трассируется в принципе, вызывая исключение:
.00010C17: 0F21C0 mov eax,dr0 .00010C1A: E845340000 call .000014064 ---v (2) .00010C1F: 0F21C8 mov eax,dr1 .00010C22: E83D340000 call .000014064 ---v (3)
Листинг 3. Привилегированные машинные команды в расшифровщике второго уровня.
Следовательно, мы должны либо модифицировать код, переписав его так, чтобы он работал в Ring 3 без нарушения функционала, либо же воспользоваться эмулятором типа x86emu (Plug-in для IDA-Pro), который лучше всего брать прямо с CVS (https://sourceforge.net/projects/ida-x86emu/), где находится самая свежая версия с кучей фиксов, сильно отличающаяся от последнего официального релиза (см. рис. 8).
Впрочем, x86emu эмулирует ограниченный набор инструкций/регистров и потому без ручной работы здесь не обойтись. Как вариант, можно попробовать BOCHS (со встроенным отладчиком), но BOCHS очень медленно работает, а с популярными отладчиками ядерного уровня Rustock.C ведет отчаянную войну и потому вовсе не факт, что "живая" отладка приведет нас к цели быстрее эмулятора.
Рисунок 8. Эмулятор x86emu на CVS с последними фиксами.
Но обращения к отладочным регистрам - это мелочи. Очень быстро мы встречаем код, взаимодействующий с ядерной памятью, в частности - следующий фрагмент (см. листинг 4) осуществляет разбор таблицы экспорта ntoskrnl.exe на предмет поиска необходимых вирусу функций. Как это он делает?! Сначала что-то грузит из указателя, полученного из FS:[38h], где на прикладном уровне находится кол-во критических секций, принадлежащих потоку (TIB->Count of owned critical sections), что не дружит со здравым смыслом.
Но ведь Rustock.C отнюдь не на прикладном уровне работает! А ядро здесь держит Processor Control Region (или, сокращенно, _KPCR), по смещению 38h от начала которого лежит указатель на глобальную таблицу дескрипторов прерываний (IDT), "смотрящую" непосредственно в ядро (если, конечно, ее никто не захачил).
Как мы это узнали?! Раскладка ядерной памяти хорошо описана в документации на Soft-Ice, а определения самих структур можно найти в NTDDK от Microsoft или обратиться к замечательному ресурсу "Windows Vista Kernel Structures", содержащего практически всю информацию о ядре Висты (http://www.nirsoft.net/kernel_struct/vista/index.html).
000138C4 mov eax, large fs:38h ; _KPCR->IDT; 000138CA add eax, 4 000138CD mov eax, [eax] 000138CF xor al, al 000138D1 stc 000138D2 jb loc_10C00 00010C00 sub eax, 5A9D558h 00010C05 sub eax, 0FA562BA8h 00010C0A cmp word ptr [eax], 'ZM' 00010C0F pushf 00010C10 call sub_13667 ... 00010E31 cmp dword ptr [eax+ebx], 'EP' 00010E38 pushf 00010E39 call sub_14484
Листинг 4. Прямой поиск ядра в памяти.
Кстати говоря, Lukasz Kwiatek, также исследовавший Rustock.C, приводит очень похожий, но подозрительно "вылизанный" код (см. листинг 7, http://www.eset.com/threat-center/blog/?p=127), что наводит на определенные размышления - либо он дербанил другую версию, либо же прогнал код через деобфускатор.
00000261 mov eax, dword ptr fs:38 00000267 mov eax, [eax+4] 0000026D xor al, al 0000026F sub eax, 100h 00000275 cmp word ptr [eax], 'ZM' 0000027A jnz loc_26F 00000280 mov bx, [eax+3Ch] 00000284 and ebx, 0FFFFh 0000028A cmp dword ptr [eax+ebx], 'EP' 00000291 jnz loc_26F
Листинг 5. Прямой поиск ядра в памяти в варианте от Lukasz Kwiatek'a.
Возникает резонный вопрос: как жить дальше и что с этим делать?! Спускаться в ядро как-то не хочется. И правильно! Поднять ядро на прикладной уровень намного быстрее, да и надежнее! IDA-Pro позволяет грузить намного более одного файла одновременно, что осуществляется посредством вызова функции load_nonbinary_file(), доступной из plug-in'ов, но отсутствующей в пользовательском интерфейсе.
Ок, пишем plug-in, грузящий любые библиотеки и драйвера, какие мы только захотим (включая ядро операционной системы), после чего останется только присобачить несложный эмулятор окружения ядра (чтобы в селекторе FS был не мусор, а валидные данные) и можно смело продолжать эмуляцию посредством x86emu.
Рисунок 9. Внешний вид эмулятора x86emu.
Ключевой фрагмент plug-in'а, работающий на версиях IDA-Pro вплоть до 4.7 включительно, приведен ниже (см. листинг 6):
void idaapi run(int arg) { load_info_t *ld; warning("plugin \"dual-load\" is called!"); ld = build_loaders_list("KERNEL32.DLL"); load_nonbinary_file("KERNEL32.DLL","KERNEL32.DLL",".", NEF_SEGS|NEF_RSCS|NEF_NAME|NEF_IMPS|NEF_CODE,ld); load_nonbinary_file("NTDLL.DLL","NTDLL.DLL",".", NEF_SEGS|NEF_RSCS|NEF_NAME|NEF_IMPS|NEF_CODE,ld); qfree(ld); }
Листинг 6. Загрузка нескольких файлов в одну базу IDA-Pro 4.7.
Начиная с IDA-Pro 4.8, прототип функции load_nonbinary_file() был злостно изменен Ильфаком без всякой заботы об обратной совместимости и старые plug-in'ы перестали работать, однако небольшая косметическая операция (см. листинг 7) спасает операцию!
void idaapi run(int arg) { load_info_t *ld; warning("plugin \"dual-load\" is called!"); /* NOTE: KERNEL32.DLL and NTDLL.DLL has to be in the current directory!!! */ linput_t *p = open_linput("KERNEL32.DLL",false); // fix ld = build_loaders_list(p); load_nonbinary_file("KERNEL32.DLL", p, ".", NEF_SEGS | NEF_RSCS | NEF_NAME | NEF_IMPS | NEF_CODE, ld); close_linput(p); }
Листинг 7. Загрузка нескольких файлов в одну базу IDA-Pro 4.8+.
С загруженным ядром расшифровщик второго уровня снимается в IDA-Pro на ура и мы попадаем в... третий. А вот в нем... нас ждет настоящий "подарок" судьбы, ставящий в тупик и высаживающий на измену. Вирус, обращаясь к PCI-шине, извлекает оттуда параметры моста "PCI/ISA", формируя RC4-ключ на основе Device ID и Vendor ID, перебрать которые тупым Brute-Force совершенно нереально. Да и ненужно!!!
Роковая ошибка создателя Rustock.C заключается в том, что производителей чипсетов (где, собственно говоря, и находится обозначенный мост) не так уж и много. Просто идем на любую достаточно полную онлайновую базу PCI-устройств (например, www.pcidatabase.com), даем ей запрос, после чего осуществляем элегантный перебор на небольшой выборке.
Все! С падением последнего бастиона с вирусом можно делать все, что угодно. В частности, отломав детектор VM Ware (которая определяется через IDT), запустить его в среде виртуальной машины, наблюдая за изменениями в памяти и файловой системе. Никакие маскировочные приемы не помогут против посекторного сравнения образов виртуального жесткого диска до и после заражения. То же самое относится и к дампам памяти. Вторая роковая ошибка создателя Rustock.С - отсутствие перехвата функции KeBugCheckEx, которая, собственно, и сбрасывает дамп на диск.
А вот некоторые вообще не заморачиваются с ручной распаковкой и эмуляцией, запуская вируса под доброкачественным виртуализатором, с которым Rustock.C никак не сражается. Помимо SEYE-эмулятора (недоступного широким массам), вполне сгодится и BOCHS, в котором (с учетом наличия исходных текстов) ничего не стоит подделать Device ID и Vendor ID, правда мы должны заранее знать, что за чипсет установлен на зараженной машине. Естественно, пользы (познавательного плана) в подобном способе запуска вируса немного. Нормальные вирусы вообще-то и без танцев с бубном запускаются.
Наибольший интерес представляет именно скрупулезный анализ вируса и используемых им приемов, многие их которых стоит взять на вооружение, если не на свое, так хоть на чужое, в смысле - приготовиться к появлению "зверьков", оборудованных модулями, выдранными из Rustock.C и, возможно, основательно доработанными.