Записки исследователя компьютерных вирусов
Шрифт:
Ниже приведен фрагмент файла, зараженного вирусом UNIX.inheader.6666 (рис. 2.8). Обратите внимание на поле длины elf-заголовка, обведенное квадратиком. Вирусное тело, начинающиеся с 34h байта, залито серым цветом. Сюда же направлена точка входа (в данном случае она равна 8048034b):
Рис. 2.8. Фрагмент НЕХ-дампа файла, зараженного вирусом UNIX.inheader.6666, внедряющимся в elf-заголовок. Поля elf – заголовка, модифицированные вирусом, взяты в рамку, а само тело вируса залито цветом
Как вариант, вирус может вклиниться между концом elf-заголовка и началом Program Header Table. Заражение происходит так же, как и в предыдущем случае, однако длина elf-заголовка
Перехват управления путем коррекции точки входа
Успешно внедриться в файл – это только полдела. Для поддержки своей жизнедеятельности всякий вирус должен тем или иным способом перехватить на себя нить управления. Классический способ, активно использовавшийся еще во времена MS-DOS, сводится к коррекции точки входа – одного из полей elf/coff/a.out-заголовков файлов. В elf-заголовке эту роль играет поле e_entry, в a.out-заголовке – a_entry. Оба поля содержат виртуальный адрес (не смещение, отсчитываемое от начала файла) машинной инструкции, на которую должно быть передано управление.
При внедрении в файл вирус запоминает адрес оригинальной точки входа и переустанавливает ее на свое тело. Сделав все, что хотел сделать, он возвращает управление программе-носителю, используя сохраненный адрес. При всей видимой безупречности этой методики она не лишена изъянов, обеспечивающих быстрое разоблачение вируса.
Во-первых, точка входа большинства честных файлов указывает на начало кодовой секции файла. Внедриться сюда трудно, и все существующие способы внедрения связаны с риском необратимого искажения исполняемого файла, приводящего к его полной неработоспособности. Точка входа, «вылетающая» за пределы секции. text, – явный признак вирусного заражения.
Во-вторых, анализ всякого подозрительного файла начинается в первую очередь с окрестностей точки входа (и ею же обычно и заканчивается), поэтому независимо от способа вторжения в файл вирусный код сразу же бросается в глаза.
В-третьих, точка входа – объект пристального внимания легиона дисковых ревизоров, сканеров, детекторов и всех прочих антивирусов.
Использовать точку входа для перехвата управления – слишком примитивно и, по мнению большинства создателей вирусных программ, даже позорно. Современные вирусы осваивают другие методики заражения, и закладываться на анализ точки входа может только наивный (вот так и рождаются байки о неуловимых вирусах…).
Перехват управления путем внедрения своего кода в окрестности точки входа
Многие вирусы никак не изменяют точку входа, но внедряют по данному адресу команду перехода на свое тело, предварительно сохранив его оригинальное содержимое. Несмотря на кажущуюся элегантность этого алгоритма, он довольно капризен в работе и сложен в реализации. Начнем с того, что для сохранения оригинальной машинной инструкции, расположенной в точке входа, вирус должен определить ее длину, но без встроенного дизассемблера это сделать невозможно.
Большинство вирусов ограничивается тем, что сохраняет первые 16 байт (максимально возможная длина машинной команды на платформе Intel), a затем восстанавливает их обратно, так или иначе обходя запрет на модификацию кодового сегмента.
Более совершенные вирусы сканируют стартовую процедуру заражаемого файла в поисках инструкций call или jmp. A найдя таковую – подменяют вызываемый адрес на адрес своего тела. Несмотря на кажущуюся неуловимость, обнаружить такой способ перехвата управления очень легко. Первое и главное – вирус, в отличие от легально вызываемых функций, никак не использует переданные ему в стеке аргументы. Он не имеет никаких понятий об их числе и наличии (машинный анализ количества переданных аргументов немыслим без интеграции в вирус полноценного дизассемблера, оснащенного мощным интеллектуальным анализатором). Вирус тщательно сохраняет все изменяемые регистры, опасаясь, что функции могут использовать регистровую передачу аргументов с неизвестным ему соглашением. Самое главное – при передаче управления оригинальной функции вирус должен либо удалить с верхушки стека адрес возврата (в противном случае их там окажется два), либо вызвать оригинальную функцию не командной call, но командой jmp. Для «честных» программ, написанных на языках высокого уровня, и то, и другое крайне нетипично, благодаря чему вирус оказывается немедленно разоблачен.
Вирусы, перехватывающие управление в произвольной точке программы (зачастую чрезвычайно удаленной от точки входа), выявить намного труднее, поскольку приходится анализировать довольно большие, причем заранее не определенные объемы кода. Впрочем, с удалением от точки входа стремительно возрастает риск, что данная ветка программы никогда не получит управление, поэтому все известные мне вирусы не выходят за границы первого встретившегося им ret.
Основные признаки вирусов
Искажение структуры исполняемых файлов – характерный, но недостаточный признак вирусного заражения. Быть может, это защита хитрая такая или завуалированный способ самовыражения разработчика. К тому же некоторые вирусы ухитряются внедриться в файл практически без искажений его структуры. Однозначный ответ дает лишь полное дизассемблирование исследуемого файла, однако это слишком трудоемкий способ, требующий усидчивости, глубоких знаний операционной системы и неограниченного количества свободного времени. Поэтому на практике обычно прибегают к компромиссному варианту, сводящемуся к беглому просмотру дизассемблерного листинга на предмет поиска основных признаков вирусного заражения.
Большинство вирусов использует довольно специфический набор машинных команд и структур данных, практически никогда не встречающихся в «нормальных» приложениях. Конечно, разработчик вируса при желании может все это скрыть и распознать зараженный код тогда не удастся. Но это в теории. На практике же вирусы обычно оказываются настолько тупы, что обнаруживаются за считанные доли секунды.
Ведь чтобы заразить жертву, вирус прежде должен ее найти, отобрав среди всех кандидатов только файлы «своего» типа. Для определенности возьмем elf-файл. Тогда вирус будет вынужден считать его заголовок и сравнить четыре первых байта со строкой | FELF, которой соответствует ASCII-последовательность 7F 45 4С 46. Конечно, если тело вируса зашифровано, вирус использует хеш-сравнение или же другие хитрые приемы программирования, строки ELF в теле зараженного файла не окажется, но более чем в половине всех существующих UNIX-вирусов она все-таки есть, и этот прием, несмотря на свою изумительную простоту, очень неплохо работает.