Записки исследователя компьютерных вирусов
Шрифт:
Загрузите исследуемый файл в любой hex-редактор и попробуйте отыскать строку « | ELF». В зараженном файле таких строк будет две: одна – непосредственно в заголовке, другая – в кодовой секции или секции данных. Только не используйте дизассемблер! Очень многие вирусы преобразуют строку | FELF в 32-разрядную целочисленную константу 464C457Fh, которая маскирует присутствие вируса, но при переключении в режим дампа сразу же «проявляется» на экране. Далее приведен внешний вид файла, зараженного вирусом VirTool.Linux. Mmap.443, который использует именно такую методику (рис. 2.9).
Рис. 2.9. Фрагмент файла, зараженного вирусом VirTool.Linux.Mmap.443. ВНЕХ-дампе легко обнаруживается строка ELF, используемая вирусом для поиска жертв «своего»
Вирус Linux.Winter.343 (также известный под именем Lotek) по этой методике обнаружить не удается, поскольку он использует специальное математическое преобразование, зашифровывая строку ELF на лету (листинг 2.14).
Листинг 2.14. Фрагмент вируса Lotek, тщательно скрывающего свой интерес к elf-файлам
Непосредственное значение В9ВЗВА81h, соответствующее текстовой строке 'B||† (в приведенном выше листинге оно выделено жирным шрифтом), представляет собой не что иное, как строку ЫELF, преобразованную в 32-разрядную константу и умноженную на минус единицу. Складывая полученное значение с четырьмя первыми байтами жертвы, вирус получает ноль, если строки равны, и ненулевое значение в противном случае.
Как вариант, вирус может дополнять эталонную строку ELF до единицы, и тогда в его теле будет присутствовать последовательность 80 ВА ВЗ В9. Реже встречаются циклические сдвиги на одну, две, три… и семь позиций в различные стороны, неполные проверки (то есть проверки на совпадение двух или трех байт) и некоторые другие операции – все не перечислишь!
Более уязвимым с точки зрения скрытности является механизм реализации системных вызовов. Вирус не может позволить себе тащить за собой всю библиотеку libc, прилинкованную к нему статической компоновкой, поскольку существование подобного монстра трудно оставить незаметным. Существует несколько способов решения этой проблемы, и наиболее популярный из них сводится к использованию native-API операционной системы. Поскольку последний является прерогативой особенностей реализации данной конкретной системы, создатели UNIX де-факто отказались от многочисленных попыток его стандартизации. В частности, в System V (и ее многочисленных клонах) обращение к системным функциям происходит через дальний call по адресу 0007:00000000, а в Linux это осуществляется через служебное прерывание INT 80h (перечень номеров системных команд можно найти в файле /usr/include/asm/unistd.h). Таким образом, использование native-API существенно ограничивает ареал обитания вируса, делая его непереносимым.
Честные программы в большинстве своем практически никогда не работают через native-API (хотя утилиты из комплекта поставки Free BSD 4.5 ведут себя именно так), поэтому наличие большого количества машинных команд INT 80 h/CALL 0007:0000000 (CD 80/9 A 00 00 00 00 07 00) с высокой степенью вероятности свидетельствует о наличии вируса. Для предотвращения ложных срабатываний (то есть обнаружения вируса там, где и следов его нет), вы должны не только обнаружить обращения к native-API, но и проанализировать последовательность их вызовов. Для вирусов характерна следующая цепочка системных команд: sys_open, sys_lseek, ol djnmap/sysjTiunmap, sys_write, sys_close, sys_exit. Реже используются вызовы exec и fork. Их, в частности, использует вирус STAOG.4744. Вирусы VirTool.Linux.Mmap.443, VirTool.Linux.Elfwrsec.a, PolyEngine.Linux.LIME.poly, Linux.Winter.343и ряд других обходятся без этого.
Ниже приведен фрагмент файла, зараженного вирусом VirTool.Linux.Mmap.443 (рис. 2.10). Наличие незамаскированных вызовов INT 80h с легкостью разоблачает агрессивную природу программного кода, указывая на склонность последнего к саморазмножению.
А вот так для сравнения выглядят системные вызовы «честной» программы – утилиты cat из комплекта поставки Free BSD 4.5 (рис. 2.11). Инструкции прерывания не разбросаны по всему коду, а сгруппированы в собственных функциях-обертках. Конечно, вирус тоже
Некоторые (впрочем, довольно немногочисленные) вирусы так просто не сдаются и используют различные методики, затрудняющие их анализ и обнаружение. Наиболее талантливые (или, скорее, прилежные) разработчики динамически генерируют инструкцию INT 80 h/CALL 0007:00000000 на лету и, забрасывая ее на верхушку стека, скрытно передают ей управление. Как следствие – в дизассемблерном листинге исследуемой программы вызов INT 80 h/INT 80 h/CALL 0007:00000000 будет отсутствовать, и обнаружить такие вирусы можно лишь по многочисленным косвенным вызовам подпрограмм, находящихся в стеке. Это действительно нелегко, так как косвенные вызовы в изобилии присутствуют и в «честных» программах, а определение значений вызываемых адресов представляет собой серьезную проблему (во всяком случае, при статическом анализе). Вместе с тем таких вирусов пока существует немного (да и те – сплошь лабораторные), так что никаких поводов для паники пока нет. А вот шифрование критических к раскрытию участков вирусного тела встречается гораздо чаще. Однако для дизассемблера IDA PRO это не бог весть какая сложная проблема, и даже многоуровневая шифровка снимается без малейшего умственного и физического напряжения.
Рис. 2.10. Фрагмент файла, зараженного вирусом VirTool.Linux.Mmap.443, демаскирующим свое присутствие прямым обращением к native-API операционной системы
Впрочем, на каждую старуху есть проруха, и IDA Pro тому не исключение. При нормальном развитии событий IDA Pro автоматически определяет имена вызываемых функций, оформляя их как комментарии. Благодаря этому замечательному обстоятельству для анализа исследуемого алгоритма нет нужды постоянно лезть в справочник. Такие вирусы, как, например, Linux.ZipWorm, не могут смириться с подобным положением дел и активно используют специальные приемы программирования, сбивающие дизассемблер с толку. Тот же Linux.ZipWorm проталкивает номера вызываемых функций через стек, что вводит IDA в замешательство, лишая ее возможности определения имен последних (листинг 2.15).
Листинг 2.15. Фрагмент вируса Linux.ZipWorm, активно и небезуспешно противостоящего дизассемблеру IDA PRO
Рис. 2.11. Фрагмент «честного» файла cat из комплекта поставки Free BSD, аккуратно размещающего native-API вызовы в функциях-обертках
С одной стороны, вирус действительно добился поставленной перед ним цели, и дизассемблерный листинг с отсутствующими автокомментариями с первого приступа не возьмешь. Но давайте попробуем взглянуть на ситуацию под другим углом. Сам факт применения антиотладочных приемов уже свидетельствует если не о заражении, то, во всяком случае, о ненормальности ситуации. Так что за противодействие анализу исследуемого файла вирусу приходится расплачиваться ослабленной маскировкой (в программистских кулуарах по этому случаю обычно говорят «из зараженного файла вирусные уши торчат»).
Уши будут торчать еще и потому, что большинство вирусов никак не заботится о создании стартового кода или хотя бы плохонькой его имитации. В точке входа «честной» программы всегда (ну, или практически всегда) расположена нормальная функция с классическим прологом и эпилогом, автоматически распознаваемая дизассемблером IDA Pro (листинг 2.16).
Листинг 2.16. Пример нормальной стартовой функции с классическим прологом и эпилогом
В некоторых случаях стартовые функции передают бразды правления libc_ startjnain и заканчиваются по hit без ret. Это вполне нормальное явление. «Вполне», потому что очень многие вирусы, написанные на ассемблере, получают в «подарок» от линкера такой же стартовый код. Поэтому присутствие стартового кода в исследуемом файле не дает нам никаких оснований считать его здоровым (листинг 2.17).