Записки исследователя компьютерных вирусов
Шрифт:
Убедитесь также, что точка входа не начинается с машинной команды jump или call, передающей управление куда-то в конец файла. Обычно вирусы либо накладывают jump непосредственно поверх «живого» кода исходной программы (естественно, предварительно сохранив его оригинальное содержимое в своем теле), либо внедряют сюда целую функцию, предназначенную для отвода глаз («голый» jump привлекает к себе слишком много внимания). Реже здесь удается встретить текстовые строки или какой-нибудь забавный код, как, например, в случае с Win 32.cabanas.b(рис. 1.5).
Рис. 1.5. Так выглядит дизассемблерный листинг файла, пораженного вирусом Win32.Cabanas.b – непосредственно в точке входа находится jump
Рис. 1.6.
Кстати говоря, вирус может внедриться и не в первую машинную команду, а, пропустив несколько инструкций, пристроиться где-то в середину стартового кода. Для этого ему даже не потребуется включать в свое тело трассировщик или дизассемблер – с некоторым риском можно ограничиться и поиском опо-кода инструкции call (E8h XXh XXh XXh XXh, где XXh – относительный адрес перехода, отсчитываемый от конца команды) или mov хх, dword prt fs:(00000000h), загружающей в регистр хх указатель на текущий обработчик структурных исключений и встречающейся в подавляющем большинстве современных программ. Взгляните на файл, зараженный вирусом Win32.КМЕ (рис. 1.6). Окрестности точки входа на первый взгляд выглядят вполне нормально, но, если присмотреться к ним повнимательнее, можно обнаружить весьма подозрительный cal 1, вылетающий за пределы кодовой секции файла. Он-то и передает управление вирусному телу!
Между прочим, точка входа имеет смысл не только в исполняемых файлах, но и в динамических библиотеках тоже. При загрузке DLL в память (и выгрузке тоже) управление автоматически передается стартовой функции, подготавливающей библиотеку к нормальной работе. С точки зрения вируса, заражение DLL по своей природе ничем не отличается от обычного исполняемого файла и осуществляется аналогичным образом. Аналогичным образом оно и распознается.
Рис. 1.7. Так выглядит дизассемблерный листинг файла, зараженного вирусом WinNT.Chatter: исполняемый код, расположенный в секции. reloc, предназначенной для хранения перемещаемых данных, сигнализирует о ненормальности ситуации
Нестандартные секции
При заражении исполняемого файла методом дозаписи своего тела в его конец у вируса есть альтернатива: либо увеличить размер последней секции файла, слившись с ней в алхимическом браке, либо создать свою собственную секцию. Оба этих способа легко распознаются визуально:
1. Код, расположенный в конце последней секции файла (.data, relос. rsrc), – весьма характерный признак наличия вируса, равно как и секция. text, замыкающая собой файл и после недолгих мытарств передающая управление «вперед» – на нормальную точку входа (рис. 1.7).
2. То же самое относится и к секциям с нестандартными именами, зачастую совпадающими с именем самого вируса или маскирующимися под секции, создаваемые упаковщиками исполняемых файлов (но сам файл при этом остается не упакован!) (рис. 1.8). Вирус может внедриться и между уже существующими секциями, проецируя «свою» секцию в любой свободный участок виртуальной памяти, не перекрываемый секциями жертвы. Поскольку в «честных» программах секции чаще всего проецируются в память строго в порядке своего физического расположения в файле, то появление секции, нарушающей этот порядок, вызывает большие подозрения.
Рис. 1.8. Так выглядит дизассемблерный листинг файла, зараженного вирусом Win32.Nathan, внедряющегося в собственноручно созданную секцию с нестандартным именем «Nathan», разоблачающую вирус с головой
Таблица импорта
Операционные системы семейства Windows поддерживают два основных способа компоновки: статический и динамический. При статической компоновке имена (ординалы) вызываемых API-функций выносятся в специальную таблицу – таблицу импорта, изучение которой дает более или менее полное представление о природе исследуемой программы и круге ее интересов. К потенциально опасным функциям в первую очередь относятся сетевые функции, функции
Конечно, зловредной программе ничего не стоит загрузить все эти функции и самостоятельно, путем динамической компоновки, в простейшем случае опирающейся на вызов LoadLibrary/GetProcAddress, a то и вовсе на «ручной» поиск API-функций в памяти (адрес системного обработчика структурных исключений дает нам адрес, принадлежащий модулю KERNEL32.DLL, базовый адрес которого определяется сканированием памяти на предмет выявления сигнатур «MZ» и «РЕ» с последующим разбором РЕ-заголовка). Но в этом случае текстовые строки с именами соответствующих функций должны присутствовать в теле программы (если только они не зашифрованы и не импортируются по ординалу). Подробнее см. главу 4, разделы «Секреты проектирования shell-кода» и «Техника вызова системных функций».
ВНИМАНИЕ
Статистика показывает, что таблица импорта троянских программ обычно носит резко полярный характер. Либо она вообще практически пуста, что крайне нетипично для нормальных, неупакованных, программ, либо содержит обращения к потенциально опасным функциям в явном виде. Конечно, сам факт наличия потенциально опасных функций еще не свидетельствует о троянской природе программы, но без особой нужды ее все-таки лучше не запускать.
Анализ таблицы импорта позволяет выявить также и ряд вирусных заражений. Собственно, у вируса есть два пути: использовать таблицу импорта файла-жертвы или создавать свою. Если необходимых вирусу API-функций в импорте жертвы нет и она не импортирует функции LoadLibrary/GetProcAddress, вирус должен либо отказаться от ее заражения, либо тем или иным образом импортировать недостающие функции самостоятельно. Некоторые вирусы используют вызов по фиксированным адресам, но это делает их крайне нежизнеспособными, ограничивая ареал обитания лишь теми версиями ОС, на которые явно рассчитывали их создатели; другие же определяют адреса функций «вручную»: по сигнатурному поиску или ручным анализом таблицы импорта; первое – громоздко и ненадежно, второе – слишком сложно в реализации для начинающих.
И вот тут-то и начинается самое интересное. Разберем два варианта: использование готовой таблицы импорта и внедрение своей. На первый взгляд кажется, что отследить «левые» обращения к импорту жертвы просто нереально, так как они ничем не отличаются от «нормальных». Теоретически. Практически же все не так уж и безнадежно. Большинство сред разработки компилирует программы с инкрементной линковкой и вместо непосредственного вызова всякой импортируемой функции, вызывает «переходник» к ней. Таким образом, каждая импортируемая функция вызывается лишь однажды и IDA генерирует лишь одну перекрестную ссылку. При заражении файла картина меняется, и к API-функциям, используемым вирусом, теперь ведут две и более перекрестные ссылки. Это – вернейший признак вирусного заражения! Вернее и быть не может (листинги 1.8 и 1.9)!
Листинг 1.8. «Заглушка», представляющая собой переходник к импортируемой функции и оттягивающая все перекрестные ссылки на себя
Листинг 1.9. Таблица импорта исследуемого приложения: наличие «паразитной» ссылки на CreateFileA указывает на факт вирусного заражения
А что, если вирус захочет создать собственную секцию импорта или как вариант – попытается расширить уже существующую? Ну, две секции импорта для операционных систем семейства Windows – это слишком! Хотя… Почему, собственно, нет? Вирус создает еще одну секцию импорта, дописывая ее в конец файла, копирует туда содержимое оригинальной таблицы импорта, добавляет недостающие API-функции и затем направляет поле Import Table на «свою» таблицу импорта. По факту загрузки файла операционной системой вирус проделывает обратную операцию, перетягивая таблицу импорта «назад». Необходимость последней операции объясняется тем, что система находит таблицу импорта по содержимому поля Import Table, а непосредственно сам исполняемый файл работает с ней по фиксированным адресам.