Записки исследователя компьютерных вирусов
Шрифт:
А теперь на сцену выходит наш прием с контрольными точками – и исследуемая программа тотчас ловится на GetModuleHandleA и CreateFileA. На момент вызова последней весь код и все данные зараженного файла уже полностью распакованы и просмотр содержимого сегмента данных немедленно разоблачает вирус по агрессивным текстовым строкам (листинг 1.3).
Листинг 1.3. Распакованный вручную I-Worm.Sobig.f сразу же выдает агрессивность своих намерений характерными текстовыми строками
Стартовый код
В девяностых годах двадцатого века, когда вирусы создавались преимущественно на ассемблере и писались преимущественно профессионалами, а коммерческие
ПРИМЕЧАНИЕ
Ассемблерные программы стартового кода лишены и потому, когда ассемблерный вирус внедряется в программу, написанную на языке высокого уровня, стартовый код отодвигается как бы «вглубь» файла, демаскируя тем самым факт своего заражения. Сегодня, когда ассемблерные вирусы становятся музейной редкостью, такой способ распознавания мало-помалу перестает работать, однако до полного списывания в утиль дело еще далеко.
Вообще-то никаких формальных признаков «нормального» start-up'a не существует и всяк разработчик волен реализовывать его по-своему. Однако свой собственный start-up цепляет к программе только извращенец. Обычные программисты для этих целей используют библиотечный стартовый код, поставляемый вместе с компилятором, зачастую даже и не подозревая о его существовании. Несмотря на то что даже в рамках одного-единственного компилятора существует множество разновидностей стартового кода, все они легко узнаваемы и факт отсутствия стартового кода надежно обнаруживается даже самыми начинающими из исследователей!
Приблизительная структура типичного стартового кода такова: сначала идет пролог, затем настройка обработчика структурных исключений (для C++ программ), обнаруживающая себя по обращению к сегментному регистру FS. Затем следует вызов функций GetVersion (GetVersionEx), GetModuleHandleA и GetStartupInfoA. Подробнее об идентификации стартового кода можно прочитать в книге Криса Касперски «Фундаментальные основы хакерства» или в «Hacker Disassembling Uncovered» его же, которую, кстати говоря, можно стащить отсюда: http://www.web-hack.ru/books/books.php?go=48
Здесь же мы не можем позволить себе подробно останавливаться на этом обширном вопросе и просто сравним стартовый код нормальной программы с кодом вируса Win2K.Inta.1676 (листинги 1.4 и 1.5).
Листинг 1.4. Так выглядит нормальный start-up от Microsoft Visual C++ 6.0…
Листинг 1.5. Атак выглядят окрестности точки входа вируса Win2K.Inta.1676
Смотрите, в то время как «хорошая» программа лениво опрашивает текущую версию операционной системы и иже с ней, зловредный вирус сломя голову несется в объятья драйвера inf.sys. Правильные программы так не поступают, и коварность вирусных планов разоблачается с первого взгляда!
ПРИМЕЧАНИЕ
Разумеется, отсутствие стартового кода еще не есть свидетельство вируса! Быть может, исследуемый файл был упакован или разработчик применил нестандартный компилятор/набор библиотек. Ну, с упаковкой мы уже разобрались, а с идентификацией компилятора поможет справиться IDA и ваш личный опыт.
ПРИМЕЧАНИЕ
Текущие
версии IDA PRO определяют версию компилятора непосредственно по стартовому коду и если он отсутствует или был изменен, механизм распознавания дезактивируется и поиском подходящей библиотеки сигнатур нам приходится заниматься вручную, через меню File Load file FLIRT signature file. И если обнаружится, что нормальный start-up y файла все-таки есть, но выполнение программы начинается не с него, – шансы на присутствие вируса существенно возрастают!Троянские программы, в большинстве своем написанные на языках высокого уровня, имеют вполне стандартный start-up и потому на такую наживку обнаруживаться не хотят. Взять, например, того же Kilez'a (листинг 1.6).
Листинг 1.6. Стандартный стартовый код червя I-Worm.Kilez.h
Даже «невооруженным» глазом видно, что стартовый код червя идентичен стартовому коду Microsoft Visual C++ 6.0, что совсем не удивительно, поскольку именно на нем червь и написан.
Точка входа
При внедрении вируса в файл точка входа в него неизбежно изменяется. Лишь немногие из вирусов ухитряются заразить файл, не прикасаясь к последней. Вирус может вписать по адресу оригинальной точки входа jump на свое тело, слегка подправить таблицу перемещаемых элементов, вклиниться в массив RVA-адресов таблицы импорта, внедриться в незанятые области кодовой секции файла и т. д., однако ареал обитания таких особей ограничен преимущественно застенками лабораторий, и в дикой природе они практически не встречаются. Не тот уровень подготовки у вирусописателей, не тот…
«Нормальные» точки входа практически всегда находятся в кодовой секции исполняемого файла (.text), точнее – в гуще библиотечных функций (Навигатор IDA PRO по умолчанию выделяет их голубым цветом), непосредственно предшествуя секции данных (рис. 1.3 и 1.4). Точки входа зараженного файла, напротив, чаще всего располагаются между секцией инициализированных и неинициализированных данных, практически у самого конца исполняемого файла.
Рис. 1.3. Так выглядит дизассемблерный листинг нормального файла. Точка входа расположена внутри секции. text в гуще библиотечных функций, приходясь приблизительно на середину файла
Так происходит потому, что при дозаписи своего тела в конец файла «вирусная» секция оказывается самой последней секцией инициализированных ячеек памяти, за которой простирается обширный регион неинициализированных данных, без которого не обходится практически ни одна программа. Это-то вирус и демаскирует! Ни один из известных автору упаковщиков исполняемых файлов так себя не ведет, и потому ненормальное расположение точки входа с высокой степенью вероятности свидетельствует о заражении файла вирусом!
Может ли вирус внедриться в середину файла? Да, может, но для этого ему придется разорвать себе задницу. Точнее – скорректировать все относительные ссылки между концом и началом файла, что очень и очень непросто. Правда, учитывая тот факт, что всякая секция, независимо от ее физического смещения в файле, может быть спроецирована по произвольному виртуальному адресу, вирус может обосноваться в кусочке незанятой памяти, оставшейся от выравнивания стартовых адресов секций по кратным адресам. (Обычно секции дискового образа выравниваются по адресам кратным 200h байт, а секции страничного имиджа на lOOOh байт. Поскольку различные секции могут иметь различные атрибуты доступа к памяти, а всякий атрибут распространяется на всю страницу целиком, «делить» одну физическую страницу могут лишь секции с идентичными атрибутами.)