Записки исследователя компьютерных вирусов
Шрифт:
На первый взгляд, помещение вирусом своего тела в секции неинициализированных данных ничего не меняет (если даже не демаскирует вирус), но при попытке поимки такого вируса за хвост он выскользнет из рук. Секция неинициализированных данных визуально ничем не отличается от всех остальных секций файла, и содержать она может все что угодно: от длинной серии нулей до копирайтов разработчика. В частности, создатели дистрибутива FreeBSD 4.5 именно так и поступают (листинг 2.9).
Листинг 2.9. Так выглядит секция. bss большинства файлов из комплекта поставки Free BSD
Ряд
Листинг 2.10. Так выглядит секция. bss в дизассемблере IDA Pro и большинстве других дизассемблеров
Заражение посредством расширения кодовой секции файла
Наибольшую скрытность вирусу обеспечивает внедрение в кодовую секцию заражаемого файла, находящуюся глубоко в середине последнего. Тело вируса, сливаясь с исходным машинным кодом, виртуально становится совершенно неотличимым от «нормальной» программы, и обнаружить такую заразу можно лишь анализом ее алгоритма (см. далее раздел «Основные признаки вирусов»).
Безболезненное расширение кодовой секции возможно лишь в elf– и coff-файлах (под «безболезненностью» здесь понимается отсутствие необходимости в перекомпиляции файла-жертвы), и достигается оно за счет того замечательного обстоятельства, что стартовые виртуальные адреса сегментов/секций отделены от их физических смещений, отсчитываемых от начала файла.
Алгоритм заражения elf-файла в общем виде выглядит так (внедрение в coff-файлы осуществляется аналогичным образом):
1. Вирус открывает файл и, считав его заголовок, убеждается, что это действительно elf-файл.
2. Заголовок таблицы секций (Section Header Table) перемещается вниз на величину, равную длине тела вируса. Для этого вирус увеличивает содержимое поля e_shoff, оккупирующего 20h-23h байты elf-заголовка.
ПРИМЕЧАНИЕ
Заголовок таблицы секций, равно как и сами секции, имеет значение только для компоновочных файлов, загрузчик исполняемых файлов их игнорирует, независимо от того, присутствуют они в файле или нет.
3. Просматривая Program Header Table, вирус находит сегмент, наиболее предпочтительный для заражения (то есть тот сегмент, в который указывает точка входа).
4. Длина найденного сегмента увеличивается на величину, равную размеру тела вируса. Это осуществляется путем синхронной коррекции полей p_filez и pjnemz.
5. Все остальные сегменты смещаются вниз, при этом поле p_of fset каждого из них увеличивается на длину тела вируса.
6. Анализируя заголовок таблицы секций (если только он присутствует в файле), вирус находит секцию, наиболее предпочтительную для заражения (как правило, заражается секция, находящаяся в сегменте последней: это избавляет вирус от необходимости перемещения всех остальных секций вниз).
7. Размер
заражаемой секции (поле sh_size) увеличивается на величину, равную размеру тела вируса.8. Все хвостовые секции сегмента смещаются вниз, при этом поле sh_offset каждой из них увеличивается на длину тела вируса (если вирус внедряется в последнюю секцию сегмента, этого делать не нужно).
9. Вирус дописывает себя к концу заражаемого сегмента, физически смещая содержимое всей остальной части файла вниз.
10. Для перехвата управления вирус корректирует точку входа в файл (e_entry) либо же внедряет в истинную точку входа jmp на свое тело (впрочем, методика перехвата управления – тема отдельного большого разговора).
Прежде чем приступить к обсуждению характерных «следов» вирусного внедрения, давайте посмотрим, какие секции в каких сегментах обычно бывают расположены. Оказывается, схема их распределения далеко не однозначна и возможны самые разнообразные вариации. В одних случаях секции кода и данных помещаются в отдельные сегменты, в других – секции данных, доступные только для чтения, объединяются с секциями кода в единый сегмент. Соответственно, и последняя секция кодового сегмента каждый раз будет иной.
Большинство файлов включает в себя более одной кодовой секции, и располагаются эти секции приблизительно так, как показано в листинге 2.11.
Листинг 2.11. Схема расположения кодовых секций типичного файла
Присутствие секции. finit делает секцию. text не последней секцией кодового сегмента файла, как чаще всего и происходит. Таким образом, в зависимости от стратегии распределения секций по сегментам, последней секцией файла обычно является либо секция. finit, либо. rodata.
Секция. finit в большинстве своем – это такая крохотная секция, заражение которой трудно оставить незамеченным. Код, расположенный в секции. finit и непосредственно перехватывающий на себя нить выполнения программой, выглядит несколько странно, если не сказать подозрительно (обычно управление на. finit передается косвенным образом как аргумент функции atexit). Вторжение будет еще заметнее, если последней секцией в заражаемом сегменте окажется секция. rodata (машинный код при нормальном развитии событий в данные никогда не попадает). Не остается незамеченным и вторжение в конец первой секции кодового сегмента (в последнюю секцию сегмента, предшествующему кодовому сегменту), поскольку кодовый сегмент практически всегда начинается с секции. init, вызываемой из глубины стартового кода и по обыкновению содержащей пару-тройку машинных команд. Вирусу здесь будет просто негде затеряться, и его присутствие сразу же станет заметным!
Более совершенные вирусы внедряются в конец секции. text, сдвигая все остальное содержимое файла вниз. Распознать такую заразу значительно сложнее, поскольку визуально структура файла выглядит неискаженной. Однако некоторые зацепки все-таки есть. Во-первых, оригинальная точка входа подавляющего большинства файлов расположена в начале кодовой секции, а не в ее конце. Во-вторых, зараженный файл имеет нетипичный стартовый код (подробнее об этом рассказывалось в предыдущей главе). И в-третьих, далеко не все вирусы заботятся о выравнивании сегментов (секций).