Записки исследователя компьютерных вирусов
Шрифт:
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, сдвигая все остальное содержимое файла вниз. Распознать такую заразу значительно сложнее, поскольку визуально структура файла выглядит неискаженной. Однако некоторые зацепки все-таки есть. Во-первых, оригинальная точка входа подавляющего большинства файлов расположена в начале кодовой секции, а не в ее конце. Во-вторых, зараженный файл имеет нетипичный стартовый код (подробнее об этом рассказывалось в предыдущей главе). И в-третьих, далеко не все вирусы заботятся о выравнивании сегментов (секций).
Последний случай стоит рассмотреть особо. Системному загрузчику, ничего не знающему о существовании секций, степень их выравнивания по барабану (простите, я хотел сказать «…она для него некритична»). Тем не менее во всех нормальных исполняемых файлах секции тщательно выравнены на величину, указанную в поле shaddralign. При заражении файла вирусом последний далеко не всегда оказывается так аккуратен, и некоторые секции могут неожиданно для себя очутиться по некратным адресам. Работоспособности программы это не нарушит, но вот факт вторжения вируса сразу же демаскирует.
Сегменты выравнивать тоже не обязательно (при необходимости системный загрузчик сделает это самостоятельно), однако программистский этикет предписывает выравнивать секции, даже если поле p_align равно нулю (то есть выравнивания не требуется). Все нормальные линкеры выравнивают сегменты по крайней мере на величину, кратную 32 байтам, хотя это происходит и не всегда. Тем не менее, если сегменты, следующие за сегментом кода, выровнены на меньшую величину – к такому файлу следует присмотреться повнимательнее.
Другой немаловажный момент: при внедрении вируса в начало кодового сегмента он может создать свой собственный сегмент, предшествующий данному. И тут вирус неожиданно сталкивается с довольно интересной проблемой. Сдвинуть кодовый сегмент вниз он не может, так как тот обычно начинается с нулевого смещения в файле, перекрывая собой предшествующие ему сегменты. Зараженная программа, в принципе, может и работать, но раскладка сегментов становится слишком уж необычной, чтобы ее не заметить.
Выравнивание функций внутри секций – это вообще вещь (в смысле: вещдок – вещественное доказательство). Кратность выравнивания функций нигде и никак не декларируется, и всякий программист склонен выравнивать функции по своему. Одни используют выравнивание на адреса, кратные 04h, другие – 08h, 10h или даже 20Ы Определить степень выравнивания без качественного дизассемблера практически невозможно. Требуется выписать стартовые адреса всех функций и найти наибольший делитель, на который все они делятся без остатка. Дописывая себя в конец кодового сегмента, вирус наверняка ошибется с выравниванием адреса пролога функции (если он вообще позаботится о создании функции в этом месте!), и он окажется отличным от степени выравнивания, принятой всеми остальными функциями. Попутно заметим, что определять степень выравнивания при помощи дизассемблера IDA PRO – плохая идея, так как она определяет ее неправильно, закладываясь на наименьшее возможное значение, в результате чего вычисленная степень выравнивания от функции к функции будет варьироваться.
Классическим примером вируса, внедряющегося в файл путем расширения кодового сегмента, является вирус Linux.Vit.4096. Любопытно, что различные авторы по-разному описывают стратегии, используемые вирусом для заражения. Так, Евгений Касперский почему-то считает, что вирус Vit записывается в начало кодовой секции заражаемого файла , в то время как он размещает свое тело в конце кодового сегмента файла 202000.pdf). Далее приведен фрагмент ELF-файла, зараженного вирусом Vit (рис. 2.6).
Рис. 2.6. Фрагмент файла, зараженного вирусом Lin/Vit. Поля, модифицированные вирусом, выделены траурной рамкой
Многие вирусы (и, в частности, вирус Lin/Obsidan) выдают себя тем, что при внедрении в середину файла «забывают» модифицировать заголовок таблицы секций (либо же модифицируют его некорректно). Как уже отмечалось выше, в процессе загрузки исполняемых файлов в память системный загрузчик считывает информацию о сегментах и проецирует их содержимое целиком. Внутренняя структура сегментов его совершенно не интересует. Даже если заголовок таблицы секций отсутствует или заполнен некорректно, запущенная на выполнение программа будет исправно работать. Однако несмотря на это, в подавляющем большинстве исполняемых файлов заголовок таблицы секций все-таки присутствует, и попытка его удаления оканчивается весьма плачевно – популярный отладчик gdb и ряд других утилит для работы с elf-файлами отказываются признать «кастрированный» файл своим. При заражении исполняемого файла вирусом, некорректно обращающимся с заголовком таблицы секций, поведение отладчика становится непредсказуемым, демаскируя тем самым факт вирусного вторжения.