Сдвиг кодовой секции вниз
Трудно объяснить причины, по которым вирусы внедряются в начало кодовой секции (сегмента) заражаемого файла или создают свой собственную секцию (сегмент), располагающуюся впереди. Этот прием не обеспечивает никаких преимуществ перед записью своего тела в конец кодовой секции (сегмента) и к тому же намного сложнее реализуется. Тем не менее, такие вирусы существуют, и будут подробно здесь рассмотрены.
Наилучший уровень скрытности достигается при внедрении в начало секции .text и осуществляется практически тем же самым образом, что и внедрение в конец, с той лишь разницей, что для сохранения работоспособности зараженного файла, вирус корректирует поля sh_addr и p_vaddr, уменьшая их на величину своего тела и не забывая о необходимости выравнивания (если выравнивание действительно необходимо). Первое поле задает виртуальный стартовый адрес для проекции секции .text, второе – виртуальный стартовый адрес для проекции кодового сегмента.
В результате этой махинации вирус оказывается в самом начале кодовой секции и чувствует себя довольно уверенно, поскольку при наличии стартового кода выглядит неотличимо от "нормальной" программы. Однако работоспособность зараженного файла уже не гарантируется, и его поведение рискует стать совершенно непредсказуемым, поскольку виртуальные адреса всех предыдущих секций окажутся полностью искажены. Если при компиляции программы компоновщик позаботился о создании секции перемещаемых элементов, то вирус (теоретически) может воспользоваться этой информацией для приведения впереди идущих секций в нормальное состояние, однако исполняемые файлы в своем подавляющем большинстве спроектированы для работы по строго определенным физическим адресам и потому неперемещаемы. Но даже при наличии перемещаемых элементов вирус не сможет отследить все случаи относительной адресации. Между секцией кода и секцией данных относительные ссылки практически всегда отсутствуют, и потому при вторжении вируса в конец кодовой секции работоспособность файла в большинстве случаев не нарушается. Однако внутри кодового сегмента случаи относительной адресации между секциями – скорее правило, нежели редкость. Взгляните на фрагмент дизассемблерного листинга утилиты ping, позаимствованный из UNIX Red Hat 5.0. Команду call, расположенную в секции .init, и вызываемую ею подпрограмму, находящуюся в секции .text, разделяют ровно 8002180h – 8000915h == 186Bh байт, и именно это число фигурирует в машинном коде (если же вы все еще продолжаете сомневаться, загляните в Intel Instruction Reference Set: команда E8h ? это команда относительного вызова):
.init:08000910 _init proc near ; CODE XREF: start+51vp
.init:08000910 E8 6B 18 00 00 call sub_8002180
.init:08000915 C2 00 00 retn 0
.init:08000915 _init endp
…
.text:08002180 sub_8002180 proc near ; CODE XREF: _init^p
Листинг 11 фрагмент утилиты ping, использующей, как и многие другие программы, относительные ссылки между секциями кодового сегмента
Неудивительно, что после заражения файл перестает работать (или станет работать некорректно)! Но если это все-таки произошло, загрузите файл в отладчик/дизассемблер и посмотрите – соответствуют ли относительные вызовы первых кодовых секций пункту своего назначения. Вы легко распознаете факт заражения, даже не будучи специалистом в области реинжинеренга.
В этом мире ничего не дается даром! За скрытность вирусного вторжения последнему приходится расплачиваться разрушением большинства заражаемых файлов. Более корректные вирусы располагают свое тело в начале кодового сегмента – в секции .init. Работоспособность заражаемых файлов при этом не нарушается, но присутствие вируса становится легко обнаружить, т. к. секция .init редко бывает большой, и даже небольшая примесь постороннего кода сразу же вызывает подозрение.
Рисунок 7 0x07 типовая схема заражения исполняемого файла путем расширения его кодовой секции
Некоторые вирусы (например вирус Linux.NuxBee) записывают себя поверх кодового сегмента заражаемого файла, перемещая затертую часть в конец кодовой секции (или, что более просто, в конец последнего сегмента файла). Получив управление и выполнив всю работу "по хозяйству", вирус забрасывает кусочек своего тела в стек и восстанавливает оригинальное содержимое кодового сегмента. Учитывая, что модификация кодового сегмента по умолчанию запрещена и разрешать ее вирусу не резон (в этом случае факт заражения очень легко обнаружить), вирусу приходится прибегать к низкоуровневым манипуляциям с атрибутами страниц памяти, вызывая функцию mprotect, практически не встречающуюся в "честных" приложениях.
Другой характерный признак: в том месте, где кончается вирус и начинается незатертая область оригинального тела программы, образуется своеобразный дефект. Скорее всего, даже наверняка, граница раздела двух сред пройдет посередине функции оригинальной программе, если еще не рассечет машинную команду. Дизассемблер покажет некоторое количество мусора и хвост функции с отсутствующим прологом.