Как получить описание вируса
- 16168
- 9,3
- 2
- 4
Обратите внимание!
Спонсоры конкурса: Лаборатория биотехнологических исследований 3D Bioprinting Solutions и Студия научной графики, анимации и моделирования Visual Science.
Эволюция и происхождение вирусов
В 2007 году сотрудники биологического факультета МГУ Л. Нефедова и А. Ким описали, как мог появиться один из видов вирусов — ретровирусы. Они провели сравнительный анализ геномов дрозофилы D. melanogaster и ее эндосимбионта (микроорганизма, живущего внутри дрозофилы) — бактерии Wolbachia pipientis. Полученные данные показали, что эндогенные ретровирусы группы gypsy могли произойти от мобильных элементов генома — ретротранспозонов. Причиной этому стало появление у ретротранспозонов одного нового гена — env, — который и превратил их в вирусы. Этот ген позволяет вирусам передаваться горизонтально, от клетки к клетке и от носителя к носителю, чего ретротранспозоны делать не могли. Именно так, как показал анализ, ретровирус gypsy передался из генома дрозофилы ее симбионту — вольбахии [7]. Это открытие упомянуто здесь не случайно. Оно нам понадобится для того, чтобы понять, чем вызваны трудности борьбы с вирусами.
Из давних письменных источников, оставленных историком Фукидидом и знахарем Галеном, нам известно о первых вирусных эпидемиях, возникших в Древней Греции в 430 году до н.э. и в Риме в 166 году. Часть вирусологов предполагает, что в Риме могла произойти первая зафиксированная в источниках эпидемия оспы. Тогда от неизвестного смертоносного вируса по всей Римской империи погибло несколько миллионов человек [8]. И с того времени европейский континент уже регулярно подвергался опустошающим нашествиям всевозможных эпидемий — в первую очередь, чумы, холеры и натуральной оспы. Эпидемии внезапно приходили одна за другой вместе с перемещавшимися на дальние расстояния людьми и опустошали целые города. И так же внезапно прекращались, ничем не проявляя себя сотни лет.
Вирус натуральной оспы стал первым инфекционным носителем, который представлял действительную угрозу для человечества и от которого погибало большое количество людей. Свирепствовавшая в средние века оспа буквально выкашивала целые города, оставляя после себя огромные кладбища погибших. В 2007 году в журнале Национальной академии наук США (PNAS) вышла работа группы американских ученых — И. Дэймона и его коллег, — которым на основе геномного анализа удалось установить предположительное время возникновения вируса натуральной оспы: более 16 тысяч лет назад. Интересно, что в этой же статье ученые недоумевают по поводу своего открытия: как так случилось, что, несмотря на древний возраст вируса, эпидемии оспы не упоминаются в Библии, а также в книгах древних римлян и греков [9]?
Строение вирусов и иммунный ответ организма
Рисунок 1. Первооткрыватель вирусов Д.И. Ивановский (1864–1920) (слева) и английский врач Эдвард Дженнер (справа).
Почти все известные науке вирусы имеют свою специфическую мишень в живом организме — определенный рецептор на поверхности клетки, к которому и прикрепляется вирус. Этот вирусный механизм и предопределяет, какие именно клетки пострадают от инфекции. К примеру, вирус полиомиелита может прикрепляться лишь к нейронам и потому поражает именно их, в то время как вирусы гепатита поражают только клетки печени. Некоторые вирусы — например, вирус гриппа А-типа и риновирус — прикрепляются к рецепторам гликофорин А и ICAM-1, которые характерны для нескольких видов клеток. Вирус иммунодефицита избирает в качестве мишеней целый ряд клеток: в первую очередь, клетки иммунной системы (Т-хелперы, макрофаги), а также эозинофилы, тимоциты, дендритные клетки, астроциты и другие, несущие на своей мембране специфический рецептор СD-4 и CXCR4-корецептор [13–15].
Одновременно с этим в организме реализуется еще один, молекулярный, защитный механизм: пораженные вирусом клетки начинают производить специальные белки — интерфероны, — о которых многие слышали в связи с гриппозной инфекцией. Существует три основных вида интерферонов. Синтез интерферона-альфа (ИФ-α) стимулируют лейкоциты. Он участвует в борьбе с вирусами и обладает противоопухолевым действием. Интерферон-бета (ИФ-β) производят клетки соединительной ткани, фибробласты. Он обладает таким же действием, как и ИФ-α, только с уклоном в противоопухолевый эффект. Интерферон-гамма (ИФ-γ) синтезируют Т-клетки (Т-хелперы и (СD8+) Т-лимфоциты), что придает ему свойства иммуномодулятора, усиливающего или ослабляющего иммунитет. Как именно интерфероны борются с вирусами? Они могут, в частности, блокировать работу чужеродных нуклеиновых кислот, не давая вирусу возможности реплицироваться (размножаться).
Причины поражений в борьбе с ВИЧ
Тем не менее нельзя сказать, что ничего не делается в борьбе с ВИЧ и нет никаких подвижек в этом вопросе. Сегодня уже определены перспективные направления в исследованиях, главные из которых: использование антисмысловых молекул (антисмысловых РНК), РНК-интерференция, аптамерная и химерная технологии [12]. Но пока эти антивирусные методы — дело научных институтов, а не широкой клинической практики*. И потому более миллиона человек, по официальным данным ВОЗ, погибают ежегодно от причин, связанных с ВИЧ и СПИДом.
Рисунок 5. Схема развития феномена ADE при вирусных инфекциях. а — Взаимодействие между антителом и рецептором FcR на поверхности макрофага. б — Фрагмент С3 комплемента (компонент комплемента, после присоединения которого весь этот комплекс приобретает способность прилипать к различным частицам и клеткам) и рецептор комплемента (complement receptor, CR) способствуют присоединению вируса к клетке. в — Белки комплемента С1q и С1qR способствуют присоединению вируса к клетке (в составе молекулы C1q имеется рецептор для связывания с Fc-фрагментом молекулы антитела). г — Антитела взаимодействуют с рецептор-связывающим сайтом вирусного белка и индуцируют его конформационные изменения, облегчающие слияние вируса с мембраной. д — Вирусы, получившие возможность реплицироваться в данной клетке посредством ADE, супрессируют противовирусные ответы со стороны антивирусных генов клетки. Рисунок с сайта supotnitskiy.ru.
Подобный вирусный механизм характерен не только для ВИЧ. Он описан и при инфицировании некоторыми другими опасными вирусами: такими, как вирусы Денге и Эбола. Но при ВИЧ антителозависимое усиление инфекции сопровождается еще несколькими факторами, делая его опасным и почти неуязвимым. Так, в 1991 году американские клеточные биологи из Мэриленда (Дж. Гудсмит с коллегами), изучая иммунный ответ на ВИЧ-вакцину, обнаружили так называемый феномен антигенного импринтинга [23]. Он был описан еще в далеком 1953 году при изучении вируса гриппа. Оказалось, что иммунная система запоминает самый первый вариант вируса ВИЧ и вырабатывает к нему специфические антитела. Когда вирус видоизменяется в результате точечных мутаций, а это происходит часто и быстро, иммунная система почему-то не реагирует на эти изменения, продолжая производить антитела к самому первому варианту вируса. Именно этот феномен, как считает ряд ученых, стоит препятствием перед созданием эффективной вакцины против ВИЧ.
Открытие биологов из МГУ — Нефёдовой и Кима, — о котором упоминалось в самом начале, также говорит в пользу этой, эволюционной, версии.
Сегодня не только ВИЧ представляет опасность для человечества, хотя он, конечно, самый главный наш вирусный враг. Так сложилось, что СМИ уделяют внимание, в основном, молниеносным инфекциям, вроде атипичной пневмонии или МЕRS, которыми быстро заражается сравнительно большое количество людей (и немало гибнет). Из-за этого в тени остаются медленно текущие инфекции, которые сегодня гораздо опаснее и коварнее коронавирусов* и даже вируса Эбола. К примеру, мало кто знает о мировой эпидемии гепатита С, вирус которого был открыт в 1989 году**. А ведь по всему миру сейчас насчитывается 150 млн человек — носителей вируса гепатита С! И, по данным ВОЗ, каждый год от этой инфекции умирает 350-500 тысяч человек [33]. Для сравнения — от лихорадки Эбола в 2014-2015 гг. (на состояние по июнь 2015 г.) погибли 11 184 человека [34].
* — Коронавирусы — РНК-содержащие вирусы, поверхность которых покрыта булавовидными отростками, придающими им форму короны. Коронавирусы поражают альвеолярный эпителий (выстилку легочных альвеол), повышая проницаемость клеток, что приводит к нарушению водно-электролитного баланса и развитию пневмонии.
Рисунок 8. Электронная микрофотография воссозданного вируса H1N1, вызвавшего эпидемию в 1918 г. Рисунок с сайта phil.cdc.gov.
Почему же вдруг сложилась такая ситуация, что буквально каждый год появляются новые, всё более опасные формы вирусов? По мнению ученых, главные причины — это сомкнутость популяции, когда происходит тесный контакт людей при их большом количестве, и снижение иммунитета вследствие загрязнения среды обитания и стрессов. Научный и технический прогресс создал такие возможности и средства передвижения, что носитель опасной инфекции уже через несколько суток может добраться с одного континента на другой, преодолев тысячи километров.
Поговорим о компьютерных вирусах? Нет, не о том, что вчера поймал ваш антивирус. Не о том, что вы скачали под видом инсталлятора очередного Photoshop. Не о rootkit-e, который стоит на вашем сервере, маскируясь под системный процесс. Не о поисковых барах, downloader-ах и другой малвари. Не о коде, который делает плохие вещи от вашего имени и хочет ваши деньги. Нет, всё это коммерция, никакой романтики…
В общем, для статьи вполне достаточно лирики, перейдем к делу. Я хочу рассказать о классическом вирусе, его структуре, основных понятиях, методах детектирования и алгоритмах, которые используются обеими сторонами для победы.
Мы будем говорить о вирусах, живущих в исполняемых файлах форматов PE и ELF, то есть о вирусах, тело которых представляет собой исполняемый код для платформы x86. Кроме того, пусть наш вирус не будет уничтожать исходный файл, полностью сохраняя его работоспособность и корректно инфицируя любой подходящий исполняемый файл. Да, ломать гораздо проще, но мы же договорились говорить о правильных вирусах, да? Чтобы материал был актуальным, я не буду тратить время на рассмотрение инфекторов старого формата COM, хотя именно на нем были обкатаны первые продвинутые техники работы с исполняемым кодом.
Начнём со свойств кода вируса. Чтобы код удобней было внедрять, разделять код и данные не хочется, поэтому обычно используется интеграция данных прямо в исполняемый код. Ну, например, так:
Все эти варианты кода при определенных условиях можно просто скопировать в память и сделать JMP на первую инструкцию. Правильно написав такой код, позаботившись о правильных смещениях, системных вызовах, чистоте стека до и после исполнения, и т.д., его можно внедрять внутрь буфера с чужим кодом.
Исполняемый файл (PE или ELF) состоит из заголовка и набора секций. Секции – это выровненные (см. ниже) буферы с кодом или данными. При запуске файла секции копируются в память и под них выделяется память, причем совсем необязательно того объёма, который они занимали на диске. Заголовок содержит разметку секций, и сообщает загрузчику, как расположены секции в файле, когда он лежит на диске, и каким образом необходимо расположить их в памяти перед тем, как передать управление коду внутри файла. Для нас интересны три ключевых параметра для каждой секции, это psize, vsize, и flags. Psize (physical size) представляет собой размер секции на диске. Vsize (virtual size) – размер секции в памяти после загрузки файла. Flags – атрибуты секции (rwx). Psize и Vsize могут существенно различаться, например, если программист объявил в программе массив в миллион элементов, но собирается заполнять его в процессе исполнения, компилятор не увеличит psize (на диске содержимое массива хранить до запуска не нужно), а вот vsize увеличит на миллион чего-то там (в runtime для массива должно быть выделено достаточно памяти).
Флаги (атрибуты доступа) будут присвоены страницам памяти, в которые секция будет отображена. Например, секция с исполняемым кодом будет иметь атрибуты r_x (read, execute), а секция данных атрибуты rw_ (read,write). Процессор, попытавшись исполнить код на странице без флага исполнения, сгенерирует исключение, то же касается попытки записи на страницу без атрибута w, поэтому, размещая код вируса, вирмейкер должен учитывать атрибуты страниц памяти, в которых будет располагаться код вируса. Стандартные секции неинициализированных данных (например, область стека программы) до недавнего времени имели атрибуты rwx (read, write, execute), что позволяло копировать код прямо в стек и исполнять его там. Сейчас это считается немодным и небезопасным, и в последних операционных системах область стека предназначена только для данных. Разумеется, программа может и сама изменить атрибуты страницы памяти в runtime, но это усложняет реализацию.
Также, в заголовке лежит Entry Point — адрес первой инструкции, с которой начинается исполнение файла.
Необходимо упомянуть и о таком важном для вирмейкеров свойстве исполняемых файлов, как выравнивание. Для того чтобы файл оптимально читался с диска и отображался в память, секции в исполняемых файлах выровнены по границам, кратным степеням двойки, а свободное место, оставшееся от выравнивания (padding) заполнено чем-нибудь на усмотрение компилятора. Например, логично выравнивать секции по размеру страницы памяти – тогда ее удобно целиком копировать в память и назначать атрибуты. Даже вспоминать не буду про все эти выравнивания, везде, где лежит мало-мальски стандартный кусок данных или кода, его выравнивают (любой программист знает, что в километре ровно 1024 метра). Ну а описание стандартов Portable Executable (PE) и Executable Linux Format (ELF) для работающего с методами защиты исполняемого кода – это настольные книжки.
Если мы внедрим свой код в позицию точно между инструкциями, то сможем сохранить контекст (стек, флаги) и, выполнив код вируса, восстановить все обратно, вернув управление программе-хосту. Конечно, с этим тоже могут быть проблемы, если используются средства контроля целостности кода, антиотладка и т.п., но об этом тоже во второй статье. Для поиска такой позиции нам необходимо вот что:
- поставить указатель точно на начало какой-нибудь инструкции (просто так взять рандомное место в исполняемой секции и начать дизассемблирование с него нельзя, один и тот же байт может быть и опкодом инструкции, и данными)
- определить длину инструкции (для архитектуры x86 инструкции имеют разные длины)
- переместить указатель вперед на эту длину. Мы окажемся на начале следующей инструкции.
- повторять, пока не решим остановиться
Это минимальный функционал, необходимый для того, чтобы не попасть в середину инструкции, а функция, которая принимает указатель на байтовую строку, а в ответ отдает длину инструкции, называется дизассемблером длин. Например, алгоритм заражения может быть таким:
- Выбираем вкусный исполняемый файл (достаточно толстый, чтобы в него поместилось тело вируса, с нужным распределением секций и т.п.).
- Читаем свой код (код тела вируса).
- Берем несколько первых инструкций из файла-жертвы.
- Дописываем их к коду вируса (сохраняем информацию, необходимую для восстановления работоспособности).
- Дописываем к коду вируса переход на инструкцию, продолжающую исполнение кода-жертвы. Таким образом, после исполнения собственного кода вирус корректно исполнит пролог кода-жертвы.
- Создаем новую секцию, записываем туда код вируса и правим заголовок.
- На место этих первых инструкций кладем переход на код вируса.
Это вариант вполне себе корректного вируса, который может внедриться в исполняемый файл, ничего не сломать, скрыто выполнить свой код и вернуть исполнение программе-хосту. Теперь, давайте его ловить.
Думаю, не надо описывать вам компоненты современного антивируса, все они крутятся вокруг одного функционала – антивирусного детектора. Монитор, проверяющий файлы на лету, сканирование дисков, проверка почтовых вложений, карантин и запоминание уже проверенных файлов – все это обвязка основного детектирующего ядра. Второй ключевой компонент антивируса – пополняемые базы признаков, без которых поддержание антивируса в актуальном состоянии невозможно. Третий, достаточно важный, но заслуживающий отдельного цикла статей компонент – мониторинг системы на предмет подозрительной деятельности.
Итак (рассматриваем классические вирусы), на входе имеем исполняемый файл и один из сотни тысяч потенциальных вирусов в нем. Давайте детектировать. Пусть это кусок исполняемого кода вируса:
Как мы увидели, для быстрого и точного сравнения детектору необходимы сами байты сигнатуры и ее смещение. Или, другим языком, содержимое кода и адрес его расположения в файле-хосте. Поэтому понятно, как развивались идеи сокрытия исполняемого кода вирусов – по двум направлениям:
- сокрытие кода самого вируса;
- сокрытие его точки входа.
Сокрытие кода вируса в результате вылилось в появление полиморфных движков. То есть движков, позволяющих вирусу изменять свой код в каждом новом поколении. В каждом новом зараженном файле тело вируса мутирует, стараясь затруднить обнаружение. Таким образом, затрудняется создание содержимого сигнатуры.
Сокрытие точки входа (Entry Point Obscuring) в результате послужило толчком для появления в вирусных движках автоматических дизассемблеров для определения, как минимум, инструкций перехода. Вирус старается скрыть место, с которого происходит переход на его код, используя из файла то, что в итоге приводит к переходу: JMP, CALL, RET всякие, таблицы адресов и т.п. Таким образом, вирус затрудняет указание смещения сигнатуры.
Гораздо более подробно некоторые алгоритмы таких движков и детектора мы посмотрим во второй статье, которую я планирую написать в ближайшее время.
Рассмотренный в статье детектор легко детектирует неполиморфные (мономорфными их назвать, что ли) вирусы. Ну а переход к полиморфным вирусам является отличным поводом, наконец, завершить эту статью, пообещав вернуться к более интересным методам сокрытия исполняемого кода во второй части.
Пн | Вт | Ср | Чт | Пт | Сб | Вс |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |