company_banner
9 июля 2014 в 14:37

Вирусы. Вирусы? Вирусы! Часть 1



Поговорим о компьютерных вирусах? Нет, не о том, что вчера поймал ваш антивирус. Не о том, что вы скачали под видом инсталлятора очередного Photoshop. Не о rootkit-e, который стоит на вашем сервере, маскируясь под системный процесс. Не о поисковых барах, downloader-ах и другой малвари. Не о коде, который делает плохие вещи от вашего имени и хочет ваши деньги. Нет, всё это коммерция, никакой романтики…

Мы поговорим о компьютерных вирусах, как о коде, который способен порождать собственные копии, изменяясь от поколения к поколению. Которому, как и его биологическим собратьям, необходим файл-носитель, работоспособный, и остающийся работоспособным, чтобы давать жизнь новым поколениям вируса. Которому для размножения необходима благодатная среда, много вкусных исполняемых файлов, а также, много глупых и активных пользователей, чтобы они их запускали. Так что название «вирус» не просто красивый ярлычок для описания вредоносной программы, компьютерный вирус, в его классическом понимании, является сущностью весьма близкой к его биологическому аналогу. Человечество, как это не раз доказывалось, способно создавать весьма изощренные решения, особенно когда дело касается создания чего-нибудь наносящего вред другим людям.

Итак, давным-давно, после того, как DOS пришел к людям, и у каждого программиста появилась своя маленькая вселенная, где адресное пространство было единым, а права на файлы были всегда rwx, появилась мысль о том, может ли программа копировать сама себя. «Конечно, может!», – сказал программист и написал код, который копирует собственный исполняемый файл. Следующая мысль была «а могут ли две программы объединиться в одну?». «Конечно, могут!», – сказал программист и написал первый инфектор. «Только вот зачем?» – подумал он, и это стало началом эпохи компьютерных вирусов. Как оказалось, гадить на компьютере и всячески пытаться избежать обнаружения очень весело, а создание вирусов является очень интересным с точки зрения системного программиста делом. Кроме того, появившиеся на рынке антивирусы предоставляли создателям вирусов серьёзный вызов их профессионализму.

В общем, для статьи вполне достаточно лирики, перейдем к делу. Я хочу рассказать о классическом вирусе, его структуре, основных понятиях, методах детектирования и алгоритмах, которые используются обеими сторонами для победы.

Анатомия вируса

Мы будем говорить о вирусах, живущих в исполняемых файлах форматов PE и ELF, то есть о вирусах, тело которых представляет собой исполняемый код для платформы x86. Кроме того, пусть наш вирус не будет уничтожать исходный файл, полностью сохраняя его работоспособность и корректно инфицируя любой подходящий исполняемый файл. Да, ломать гораздо проще, но мы же договорились говорить о правильных вирусах, да? Чтобы материал был актуальным, я не буду тратить время на рассмотрение инфекторов старого формата COM, хотя именно на нем были обкатаны первые продвинутые техники работы с исполняемым кодом.

Основными частями кода вируса являются infector и payload. Infector – это код, который ищет подходящие для заражения файлы и внедряет в них вирус, стараясь максимально скрыть факт внедрения и при этом не повредить функционалу файла. Payload – это код, который выполняет собственно необходимые вирмейкеру действия, например, рассылает спам, DoS-ит кого-нибудь, или просто оставляет на машине текстовой файлик «Здесь был Виря». Нам совершенно непринципиально, что там внутри payload, главное, что вирмейкер всячески старается скрыть его содержимое.

Начнём со свойств кода вируса. Чтобы код удобней было внедрять, разделять код и данные не хочется, поэтому обычно используется интеграция данных прямо в исполняемый код. Ну, например, так:
    jmp message

the_back:
    mov eax, 0x4
    mov ebx, 0x1
    pop ecx		; со стека будет взят адрес «Hello, World»
    mov edx, 0xF
    int 0x80
...

message:
    call the_back       ; после исполнения на стеке будет лежать адрес «возврата», т.е. адрес «Hello, World\n»
    db "Hello, World!", 0Dh, 0Ah

Или так:
push 0x68732f2f   ; “hs//”
push 0x6e69622f   ; “nib/”
mov ebx, esp ; в ESP теперь адрес строки «/bin/sh»
mov al, 11
int 0x80


Все эти варианты кода при определенных условиях можно просто скопировать в память и сделать JMP на первую инструкцию. Правильно написав такой код, позаботившись о правильных смещениях, системных вызовах, чистоте стека до и после исполнения, и т.д., его можно внедрять внутрь буфера с чужим кодом.

Предположим, вирмейкер имеет возможность написать код вируса в таком стиле, и теперь ему надо внедрить его в существующий исполняемый файл. Ему необходимо позаботиться о двух вещах:
  • Куда положить вирус? Необходимо найти достаточно места, чтобы вирус туда поместился, записать его туда, по возможности не разломав файл и так, чтобы в области, в которой вирус окажется, было разрешено исполнение кода.
  • Как передать управление на вирус? Просто положить вирус в файл недостаточно, надо еще совершить переход на его тело, а после завершения его работы вернуть управление программе-жертве. Или в другом порядке, но, в любом случае, мы же договорились не ломать ничего, да?

Итак, разберемся с внедрением в файл. Современные исполняемые форматы для платформы x86 в Windows и Linux – это PE (Portable Executable) и ELF (Executable and Linkable Format). Вы без проблем найдёте их спецификации в системной документации, и если будете заниматься вопросами защиты исполняемого кода, то точно не пропустите. Исполняемые форматы и системный загрузчик (код операционной системы, который занимается запуском исполняемого файла) являются одним из «слонов», на которых стоит операционная система. Процедура запуска .exe файла является очень сложным алгоритмически процессом с кучей нюансов, и рассказывать об этом можно в десятке статей, которые вы обязательно найдете сами, если тема вас заинтересует. Я ограничусь простым рассмотрением, достаточным для базового понимания процесса запуска. Чтобы в меня не кидались помидорами, далее под компилятором я буду иметь в виду весь комплекс программ, превращающий исходный код в готовый исполняемый файл, то есть, фактически, компилятор + линкер.

Исполняемый файл (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) для работающего с методами защиты исполняемого кода – это настольные книжки.

Так как адреса внутри всех этих секций связаны, просто шлепнуть кусок кода в середину секции, «перевязав» его JMP-ами не получится, исходный файл сломается. Поэтому популярными местами для внедрения кода вируса являются:
  • основная кодовая секция (перезапись вирусом начала исполняемого кода начиная прямо с Entry Point).
  • padding между окончанием заголовка и первой секцией. Там ничего нет и вполне можно уместить там небольшой вирус (либо его загрузчик) не сломав файл.
  • новая секция, которую можно дописать в заголовок и разместить в файле после всех остальных. В этом случае никакие внутренние смещения не поломаются, и с местом проблем тоже нет. Правда последняя секция в файле, в которой разрешено исполнение, конечно же, обратит на себя внимание эвристика.
  • padding между окончанием содержимого секции и ее выровненным концом. Это намного сложнее, так как сначала надо этот самый «конец» найти, и не факт, что нам повезет и места будет достаточно. Но для некоторых компиляторов это место можно обнаружить просто по характерным байтам

Есть способы и похитрее, некоторые я опишу во второй статье.

Теперь о передаче управления. Чтобы вирус отработал, его код должен каким-то способом получить управление. Самый очевидный способ: сначала управление получает вирус, а потом, после того, как он отработает – программа-хост. Это самый простой способ, но также имеют право на жизнь и варианты, когда вирус получает управление, например, после завершения работы хоста, или в середине исполнения, «замещая» исполнение какой-нибудь функции. Приведем несколько техник передачи управления (термин Entry Point или EP, используемый далее, – это точка входа, то есть адрес, на который системный загрузчик передаст управление после того, как подготовит исполняемый файл к запуску):
  1. JMP на тело вируса замещает первые байты, находящиеся в Entry Point файла. Затёртые байты вирус сохраняет в своём теле, и, по окончании собственной работы, восстанавливает их и передает управление на начало восстановленного буфера.
  2. Способ, похожий на предыдущий, но вместо байтов вирус сохраняет несколько полных машинных инструкций в Entry Point, тогда он может, ничего не восстанавливая (проследив только за корректной очисткой стека), выполнить их после окончания собственной работы и передать управление на адрес инструкции, следующей за «сворованными».
  3. Как и в случае с внедрением, есть способы и похитрее, но мы их тоже рассмотрим ниже, или отложим до следующей статьи.

Всё это – способы сделать корректную вставку буфера с кодом в некоторый исполняемый файл. При этом п.2 и п.3. подразумевают функционал, позволяющий понять, какие байты являются инструкциями, и где находятся границы между инструкциями. Ведь мы не можем «разорвать» инструкцию пополам, в этом случае все сломается. Таким образом, мы плавно переходим к рассмотрению дизассемблеров в вирусах. Понятие принципа работы дизассемблеров нам понадобится для рассмотрения всех нормальных техник работы с исполняемым кодом, поэтому ничего страшного, если я немного опишу его сейчас.

Если мы внедрим свой код в позицию точно между инструкциями, то сможем сохранить контекст (стек, флаги) и, выполнив код вируса, восстановить все обратно, вернув управление программе-хосту. Конечно, с этим тоже могут быть проблемы, если используются средства контроля целостности кода, антиотладка и т.п., но об этом тоже во второй статье. Для поиска такой позиции нам необходимо вот что:
  • поставить указатель точно на начало какой-нибудь инструкции (просто так взять рандомное место в исполняемой секции и начать дизассемблирование с него нельзя, один и тот же байт может быть и опкодом инструкции, и данными)
  • определить длину инструкции (для архитектуры x86 инструкции имеют разные длины)
  • переместить указатель вперед на эту длину. Мы окажемся на начале следующей инструкции.
  • повторять, пока не решим остановиться

Это минимальный функционал, необходимый для того, чтобы не попасть в середину инструкции, а функция, которая принимает указатель на байтовую строку, а в ответ отдает длину инструкции, называется дизассемблером длин. Например, алгоритм заражения может быть таким:
  1. Выбираем вкусный исполняемый файл (достаточно толстый, чтобы в него поместилось тело вируса, с нужным распределением секций и т.п.).
  2. Читаем свой код (код тела вируса).
  3. Берем несколько первых инструкций из файла-жертвы.
  4. Дописываем их к коду вируса (сохраняем информацию, необходимую для восстановления работоспособности).
  5. Дописываем к коду вируса переход на инструкцию, продолжающую исполнение кода-жертвы. Таким образом, после исполнения собственного кода вирус корректно исполнит пролог кода-жертвы.
  6. Создаем новую секцию, записываем туда код вируса и правим заголовок.
  7. На место этих первых инструкций кладем переход на код вируса.

Это вариант вполне себе корректного вируса, который может внедриться в исполняемый файл, ничего не сломать, скрыто выполнить свой код и вернуть исполнение программе-хосту. Теперь, давайте его ловить.

Анатомия детектора

Вдруг, откуда ни возьмись, появляется рыцарь на белом компе, в левой руке у него отладчик, а в правой – дизассемблер, программист антивирусной компании. Откуда он там взялся? Вы, конечно, догадались. С большой долей вероятности, он появился там из «смежной области». Антивирусная область в плане программирования весьма уважаема теми, кто в теме, ибо возиться этим ребятам приходится с весьма изощренными алгоритмами, причем в довольно стеснённых условиях. Сами посудите: у вас на входе сотня тысяч экземпляров всякой заразы и исполняемый файл, работать вы должны практически в реальном времени, а цена ошибки весьма высока.

Для антивируса, как и для любого конечного автомата, принимающего бинарное решение «да/нет» (инфицирован/здоров), существует два типа ошибок – false positive и false negative (ошибочно признал файл заразным, ошибочно пропустил зараженный). Понятно, что общее число ошибок надо снижать в любом раскладе, но false negative для антивируса куда более неприятна, чем false positive. «После скачивания торрента, перед установкой игры отключите антивирус» — знакомо? Это и есть «false positive» – crack.exe, записывающий что-то в исполняемый .exe файл для достаточно умного эвристического анализатора (см. ниже), выглядит как вирус. Как говорится: «лучше перебдеть, чем недобдеть».

Думаю, не надо описывать вам компоненты современного антивируса, все они крутятся вокруг одного функционала – антивирусного детектора. Монитор, проверяющий файлы на лету, сканирование дисков, проверка почтовых вложений, карантин и запоминание уже проверенных файлов – все это обвязка основного детектирующего ядра. Второй ключевой компонент антивируса – пополняемые базы признаков, без которых поддержание антивируса в актуальном состоянии невозможно. Третий, достаточно важный, но заслуживающий отдельного цикла статей компонент – мониторинг системы на предмет подозрительной деятельности.

Итак (рассматриваем классические вирусы), на входе имеем исполняемый файл и один из сотни тысяч потенциальных вирусов в нем. Давайте детектировать. Пусть это кусок исполняемого кода вируса:
XX XX XX XX XX XX	; начало вируса длиной N байт . . . 
68 2F 2F 73 68		push 0x68732f2f   ; “hs//”
68 2F 62 69 6E		push 0x6e69622f   ; “nib/”
8B DC			mov ebx, esp ; в ESP теперь адрес строки «/bin/sh»
B0 11			mov al, 11
CD 80			int 0x80
XX XX XX XX		; конец вируса длиной M байт . . .  

Сразу хочется просто взять пачку опкодов (68 2F 2F 73 68 68 2F 62 69 6E 8B DC B0 11 CD 80) и поискать эту байтовую строку в файле. Если нашли – попался, гад. Но, увы, оказывается эта же пачка байт встречается и в других файлах (ну мало ли кто вызывает командный интерпретатор), да еще и таких строк для поиска «стотыщ», если искать каждую, то никакая оптимизация не поможет. Единственный, быстрый и правильный способ проверить наличие такой строки в файле – это проверить ее существование по ФИКСИРОВАННОМУ смещению. Откуда его взять?

Вспоминаем «смежную область» — особенно места про то, куда вирус себя кладет и как передает себе управление:
  • вирус внедряется в padding между заголовком и началом первой секции. В этом случае можно проверить существование этой байт-строки по смещению
    «длина заголовка» + N (где N – число байт от начала вируса до байт-строки)
  • вирус лежит в новой, отдельной секции. В этом случаем можно проверить существование байт-строки от начала всех секций с кодом
  • вирус внедрился в padding между концом кода и концом кодовой секции. Можно использовать отрицательное смещение от конца секции, типа «конец кодовой секции» — М (где M — число байт от конца байт-строки до конца кода вируса) – «длина байт-строки»

Теперь оттуда же про передачу управления:
  • вирус записал свои инструкции прямо поверх инструкций в Entry Point. В этом случае ищем байт строку просто по смещению «Entry Point» + N(где N – число байт от начала вируса до байт-строки)
  • вирус записал в Entry Point JMP на свое тело. В этом случае надо сначала вычислить куда смотрит этот JMP, а потом искать байт-строку по смещению «адрес перехода JMP» + N(где N – число байт от начала вируса до байт-строки)

Что-то я устал писать «байт-строка», она переменной длины, хранить ее в базе неудобно, и совершенно необязательно, поэтому вместо байт-строки мы будем использовать её длину плюс CRC32 от неё. Такая запись очень короткая и сравнение работает быстро, так как CRC32 алгоритм не из медленных. Гнаться за устойчивостью к коллизиям контрольных сумм смысла нет, так как вероятность коллизии по фиксированным смещениям мизерная. Кроме того, даже в случае коллизии ошибка будет типа «false positive», что не так уж и страшно. Обобщаем все вышеописанное, вот примерная структура записи в нашей антивирусной базе:
  1. ID вируса
  2. флаги, указывающие откуда считать смещение (от EP, от конца заголовка, от конца первой секции, от начала всех секций, от адреса перехода инструкции JMP в EP и т.п.)
  3. смещение (offset)
  4. длина сигнатуры (Lsig)
  5. CRC32 сигнатуры (CRCsig)

Оптимизируем вход (оставляем только сигнатуры, которые «влазят» в данный файл, сразу из заголовка подготавливаем набор необходимых смещений) и далее:
{ # для всех подходящих записей 
-	на основании флагов вычисляем базовое смещение в файле (начало кодовой секции, entry point и т.п.)
-	прибавляем к нему offset
-	читаем Lsig байт
-	считаем от них CRC32
-	если совпало – мы поймали вирус
}

Ура, вот наш первый антивирус. Он достаточно крут, так как при помощи достаточно полной базы сигнатур, нормально подобранных флагов и хорошей оптимизации этот детектор способен очень быстро ловить 95% всяких зараз (подавляющее большинство современного malware это просто исполняемые файлы, без всякой способности к мутации). Далее начинается игра «кто быстрее обновит базы сигнатур» и «кому раньше пришлют новый экземпляр какой-нибудь гадости».

Сбор и каталогизация этой «гадости» является задачей весьма нетривиальной, но совершенно необходимой для качественного тестирования детектора. Сбор эталонной базы исполняемых файлов задача непростая: попробуйте найти все экземпляры зараженных файлов (для сложных случаев в нескольких экземплярах), каталогизировать их, перемешать с «чистыми» файлами и регулярно гонять по ним детектор с целью выявления ошибок детектирования. Такая база собирается годами, и является очень ценным активом антивирусных компаний. Возможно, я ошибаюсь, и её реально достать (всякие сервисы online-проверок на вирусы вполне в состоянии предоставить некоторый её аналог), но, когда я занимался этим вопросом, ничего похожего достать было нельзя (по крайней мере, под Linux).

Эвристический анализатор

Какое страшное слово – «эвристический анализатор», сейчас его и не увидишь в интерфейсах антивирусов (наверное, пугает пользователей). Это одна из самых интересных частей антивируса, так как в нее пихают все, что не укладывается ни в один из движков (ни сигнатурный, ни в эмулятор), и похож на доктора, который видит, что пациент кашляет и чихает, но определить конкретную болезнь не может. Это код, который проверяет файл на некоторые характерные признаки заражения. Примеры таких признаков:
  • некорректный (испорченный вирусом, но работоспособный) заголовок файла
  • JMP прямо в точке входа
  • «rwx» на секции кода

Ну, и так далее. Помимо указания факта заражения, эвристик может помочь принять решение – запускать ли более «тяжелый» анализ файла? Каждый признак имеет разный вес, от «подозрительный какой-то» до «не знаю чем, но файл заражен точно». Именно эти признаки дают большинство ошибок «false positive». Не забудем также о том, что именно эвристик может предоставить антивирусной компании экземпляры потенциальных вирусов. Сработал эвристик, но ничего конкретного не было найдено? Значит файл точно является кандидатом на отправку в антивирусную компанию.

Межвидовое взаимодействие и эволюция

Как мы увидели, для быстрого и точного сравнения детектору необходимы сами байты сигнатуры и ее смещение. Или, другим языком, содержимое кода и адрес его расположения в файле-хосте. Поэтому понятно, как развивались идеи сокрытия исполняемого кода вирусов – по двум направлениям:
  • сокрытие кода самого вируса;
  • сокрытие его точки входа.

Сокрытие кода вируса в результате вылилось в появление полиморфных движков. То есть движков, позволяющих вирусу изменять свой код в каждом новом поколении. В каждом новом зараженном файле тело вируса мутирует, стараясь затруднить обнаружение. Таким образом, затрудняется создание содержимого сигнатуры.

Сокрытие точки входа (Entry Point Obscuring) в результате послужило толчком для появления в вирусных движках автоматических дизассемблеров для определения, как минимум, инструкций перехода. Вирус старается скрыть место, с которого происходит переход на его код, используя из файла то, что в итоге приводит к переходу: JMP, CALL, RET всякие, таблицы адресов и т.п. Таким образом, вирус затрудняет указание смещения сигнатуры.

Гораздо более подробно некоторые алгоритмы таких движков и детектора мы посмотрим во второй статье, которую я планирую написать в ближайшее время.

Параллельно с развитием вирусных движков и противостоящих им детекторов активно развивались коммерческие защиты исполняемых файлов. Появилось огромное количество небольших коммерческих программ, и разработчикам нужны были движки, позволяющие взять EXE-файл и «завернуть» его в некоторый «конверт», который умеет защищенным образом генерировать валидный серийный номер. А кто у нас умеет скрывать исполняемый код, и внедрять его в исполняемые файлы без потери работоспособности? Правильно, те самые разработчики из «смежной области». Поэтому написание хорошего полиморфного вируса и навесной защиты исполняемого файла – это очень похожие задачи, с использованием одних и тех же алгоритмов и инструментов. Так же схожи и процесс анализа вирусов и создания сигнатур и взлом коммерческого ПО. В обоих случаях надо добраться до истинного кода и либо создать сигнатуру, либо достать из него алгоритм генерации серийного номера.

В интернетах существуют несколько страниц по теме «классификация компьютерных вирусов». Но мы же договорились, вирус – это то, что умеет само себя воспроизводить в системе, и чему необходим файл-носитель. Поэтому всякие трояны-руткиты-malware – это не вирусы, а тип payload-кода, который вирус может таскать на себе. Для описываемых в статье технологий классификация компьютерных вирусов может быть только одна: полиморфные и неполиморфные вирусы. То есть меняющиеся от поколения к поколению, либо нет.

Рассмотренный в статье детектор легко детектирует неполиморфные (мономорфными их назвать, что ли) вирусы. Ну а переход к полиморфным вирусам является отличным поводом, наконец, завершить эту статью, пообещав вернуться к более интересным методам сокрытия исполняемого кода во второй части.
Автор: @BoogerWooger
Mail.Ru Group
рейтинг 578,43
Строим Интернет

Комментарии (53)

  • 0
    — текст удален -
  • +16
    Спасибо, очень информативная статья! Не представляла, что это настолько нетривиальная задача, поиск и обезвреживание вирусов. Прямо противостояние снаряда и брони :)
    • +2
      Спасибо за спасибо. Антивирусы, помимо общих алгоритмов противостояния вирусам и в плане производительности тоже достойны уважения. Оптимизировать сравнение по сотне тысяч сигнатур на произвольных данных задачка очень непростая, поэтому когда жалуются «этот свинский антивирь съел весь процессор» мне антивирус немного жалко, он же работает, старается…
    • +1
      Это хоть и сложная задача, но очень увлекательная и интересная. Но есть один очень важный нюанс: «Аверы это догоняющая сторона» из этого вытекает следствие: «Уважаемый пользователь имей голову на плечах, даже когда стоит самый лучший антивирус»
  • НЛО прилетело и опубликовало эту надпись здесь
    • +8
      Я занимался этим вопросом около шести лет назад и под Linux. После анализа нескольких вирусных движков, и, учитывая, что подавляющее большинство исполняемых файлов в Linux скомпилированы и слинкованы стандартными компиляторами, удалось в эвристик затолкать пару десятков признаков, которые показывали точность около 90% (т.е. 90% сэмплов, зараженных вирусами эвристик считал зараженными). Но это палка о двух концах, этот же самый эвристик ругался на файлы, с коммерческой защитой — поэтому думаю, что если бы мне удалось собрать 100 экземпляров здоровых защищенных файлов, эвристик на 90% из них ругался бы тоже. В антивирусных базах для борьбы с этими «false positive» есть специальные «хорошие» сигнатуры, которые говорят антивирусу, что это защита, а не вирус.

      Поэтому называть эвристик отдельным механизмом детектирования не совсем правильно, он работает только в комплексе с другими методами. Например — нашли нестандартный пролог файла, значит надо запустить дизассемблер, посмотреть куда ведут переходы из пролога, и уже там «пошуровать» сигнатурным, например, сканером. Я во второй статье напишу про эвристик намного более подробно. Надеюсь, ответил на вопрос.
  • +5
    Спасибо, классно всё расписано!
    четко и все ясно!
  • +4
    Спасибо, было интересно. Есть вопрос:

    Применялась ли данная информация при написании Guard Mail.ru? И какая именно ее часть?
    • +22
      Наверное та, которая отвечает за сохранность Guard Mail.ru и за самовоспроизведение после удаления ;)
      • +2
        А была такая интрига.
    • +1
      причем тут вообще Guard.Mail.ru?
      • +3
        Вы серьезно? Вроде mrMendoza сделал мой ироничный комментарий чуть потолще и понятнее.
  • НЛО прилетело и опубликовало эту надпись здесь
    • +6
      Поправил, спасибо.
  • –8
    Я просто оставлю это здесь :)
    habrahabr.ru/post/172393/
    habrahabr.ru/post/188894/
    • +9
      А зачем?
      • +3
        Mail.ru негодуе :)
      • +8
        По ходу как бы намекают на немножко испачканную карму (в тантрическом смысле!!!) компании
  • +19
    Анатомия вирусов в блоге Mail.Ru — это что, троллинг такой?
    • +7
      Осторожнее, тут не понимают подобного юмора.
    • 0
      Значит я не один задался вопросом: «Что делает статья про вирусы в блоге Mail.ru?»
  • +5
    Уважаемые читатели, не хочу обидеть ничьих чувств к моей любимой компании. Ваши комментарии очень классные и остроумные, но я бы предпочел комментарии от программистов.
    • +6
      Учитывая отношение к Mail.Ru в этом плане и после «Не о поисковых барах, downloader-ах и другой малвари.», которое воспринимается как попытка предложить отвлечься от ваших же Guard'ов и ваших же даунлоадеров, большинство просто не может воспринимать пост всерьез, пока он лежит в корпоративном блоге компании, которая имеет такую негативную карму в этой области.
      • +2
        Печально, конечно, что корпоративный блог так портит впечатление. Надеюсь, большинство все таки поверит, что я ни в коей мере не старался «предложить отвлечься от наших же Guard-ов», а просто постарался написать хорошую статью.
        • +2
          Повод писать от себя? И от себя уже точно можно написать что отвлекаемся мы от недоделок аля Guard Mail.ru и yandex.bar.
          • +2
            Статья есть статья, кому надо найдет её поиском. А карму компании я с удовольствием повышу, от Mail.Ru в моей лично жизни я ничего плохого не видел. И про Guard я, правда, не могу поддержать дискуссию, у меня он никогда не стоял, и я про него реально вообще ничего не знаю.
            • +3
              И про Guard я, правда, не могу поддержать дискуссию, у меня он никогда не стоял, и я про него реально вообще ничего не знаю.

              Знаете, выглядит так, будто с людьми вы не общаетесь и вообще, с интернетом познакомились пару месяцев назад.

              Вот вопрос о упомянутом ПО на ресурсе самого mail.ru .

              Погуглите, почитайте на досуге. Кстати, для того, чтобы установить на свой ПК guard и прочие прелести, достаточно подключить чужой винтчестер к своему ПК, на котором Guard уже есть. Отличный пример само-репликации. Только странноватый он, пример этот, для «анти-вируса». А там и «Интернет», и «спутник», и поиск «по умолчанию», который выковыривается только сносом всего ПО, чисткой реестра и переустановкой браузера и, само собой, Браузер «Амиго», который самостоятельно ставить себя «Браузером по умолчанию» и даже при попытке запусков других браузеров перехватывает процесс и запускает исключительно себя, подтягиваются-устанавливаются.

              Поэтому я и спрашивал о том, как информация в статье применялась при разработке данного ПО.
              • +2
                Браузер «Амиго», который самостоятельно ставить себя «Браузером по умолчанию» и даже при попытке запусков других браузеров перехватывает процесс и запускает исключительно себя, подтягиваются-устанавливаются.

                Знакомый говорил, что видел вообще абсурд — амиго запускался при попытке зайти на любой сайт в браузере внутриигрового оверлея Steam — это уже вообще ни в какие рамки.

                Слава богу я с ним не сталкивался, и только вчера о нем узнал вообще…
                • –2
                  Люди, пожалуйста, уберите Амиго, Гварды и Яндекс бары из обсуждений — они вообще не из этой оперы и не имеют к статье ну ни малейшего отношения. Я не собираюсь их обсуждать, я реально про них ничегошеньки не знаю, зачем вы это сюда пишете!
                  • +6
                    У нас тут вроде статья о вирусах, почему нет.
              • +1
                Статья в основном описывает внедрение кода внутрь существующего исполняемого файла и детектирование факта таких внедрений. Насколько я понял, гвард себя не встраивает внутрь исполняемых файлов, и не ищет вирусы по сигнатурам, поэтому никакая информация из данной статьи не применялась при разработке данного ПО.

                А можно пояснить фразу «для того, чтобы установить на свой ПК guard и прочие прелести, достаточно подключить чужой винтчестер к своему ПК, на котором Guard уже есть»? Я слегка не уловил смысл
                • +2
                  Поясняю, из собственной практики. Как-то раз, с винтчестера, с виду вроде чистого, необходимо было перекинуть информацию, документы рабочие и т.п. Подключил винт к домашнему ПК, запустился со своего основного, скопировал документы, выключился, отключил винт, включился и вуаля! На моем рабочем столе «Интернет» от mail.ru, спутник, guard, поиск mail.ru по умолчанию в хроме, который устанавливался обратно самостоятельно после перезапуска браузера и после подтянулся «браузер» «Амиго» от mail.ru. То, что я мог занести это сам — исключено. Софт ставится один раз при разворачивании системы и тщательно подбирается, т.к. я стеснен в пространстве (SSD 128 Gb). Остальные закачки — исключительно свежие покупки в Steam. Все остальное смотрится online на проверенных ресурсах.
                  Последующий запуск системы уже на родной машине «подкидного» винта выявил, что все это добро там тоже стояло и пролезло на мой ПК с него.

                  И тут возникают мысли о самостоятельной репликации на новые устройства по принципу действия вирусов.
  • 0
    false negative для антивируса куда более неприятна, чем false positive

    Не всегда. Смотря какой false positive и какое отсутствие детекта.

    В настоящее время классические файловые вирусы встречаются довольно редко и тем более не часто используются для распространения другой полезной нагрузки.

    Сейчас вредоносные программы чаще распространяются иными способами. Интереснее было бы увидеть описание современных угроз, с которыми компании Mail.Ru скорее всего приходится сталкиваться.
    • +2
      Я имел в виду, что если файл чист, а антивирь его забанил (false positive) — это не так страшно, как если файл заражен, а антивирь его пропустил (false negative).

      Вы правы, классических вирусов сейчас практически нет, оно и понятно, писать их очень накладно, а профита не так много, как, например, в случае оперативного эксплойтинга какой-нибудь новой дырки. Я, собственно, поэтому статью и написал именно про вирусы, вряд ли я смогу рассказать про руткиты, трояны и malware больше, чем уже написано на хабре.
      Мне кажется, что в антивирусной компании дерутся за каждый новый полиморф, т.к их очень мало и разбирать их намного интересней чем клепать сигнатуры для десятков тупых неизменных бинарей.

      В продолжении статьи постараюсь еще конкретней очертить рассматриваемую область — это исполняемый код, который изменяет сам себя и методы его детектирования.

      Про угрозы, с которыми Mail.Ru имеет дело я не смогу написать, т.к. не работаю в отделе безопасности, сам бы почитал с удовольствием
      • 0
        Я имел в виду, что если файл чист, а антивирь его забанил (false positive) — это не так страшно, как если файл заражен, а антивирь его пропустил (false negative).


        Если антивирусная программа будет ложно детектировать вредоносный код в компоненте какой-либо очень популярной программы (а уж тем более в одном или нескольких системных файлах), это в какой-то степени даже хуже для пользователей, чем временное отсутствие детекта реальной сложной угрозы.
        • +2
          Как юзер, наверное неохотно, но соглашусь.

          Хотя мне очень не нравится наличие самомодифицирующегося кода или признаков, указывающих на вирус в коде «очень популярной программы»
      • +3
        imho, трудно сравнивать false+ и false-, т.к очень сильно зависит от конкретного образца. Пропустить адварь/недиструктивный троян — не фатально, а шифровальщика — капут. С одной стороны. А с другой — фолсануть на файле винампа, да почти любого прикладного по — не фатально. Переустановил, матюгнулся, с кем не бывает. А вот фолс на виндовом системнике, драйвере — это в лучшем случае BSOD/ребут, восстановление с точки бэкапа, в худшем — убитая система. Если систем — пара сотен (компания) — это очень неприятно.

        Фолс-позитив обязаны часто сигнатурам, которые клепаются автоматически. Сотни тысяч образцов в сутки не способен обработать ни один «живой» вирлаб, поэтому все так или иначе нанимают роботов. И тут появляется еще одно место балансировки — или роботы пропускают сквозь пальцы слишком много зверьков, или сильно повышаем вероятность фолса на чьем-нибудь безвредном файле.
        Алгоритмы не безупречны и сбой рано или поздно будет.
        Поэтому работа над ошибками в виде большой коллекции «чистого» софта — это тоже одна из важных и интересных задач всех вендоров.
      • 0
        Ну, смотря для кого «не так страшно».
        Если вы автор программы, на которую даёт false positive популярный антивирус, а сама программа компилируется (т.е. непосредственно на машинный код вы влиять не очень-то можете) — вас ждут весьма весёлые деньки и месяцы нетривиального секаса.
        • +2
          Ну по простому коду, который сам себя «не трогает» такие ошибки должны быть очень и очень редкими (например для того, чтобы не сработала сигнатура достаточно просто «сдвинуть» код — просто добавив что-нибудь безобидное в исходник)
          А если вы и в самом деле пишете самомодифицирующийся код, на который кидается эвристик антивируса, то на машинный код вы точно можете влиять, понимаете что происходит и вполне в состоянии заниматься «нетривиальным секасом»
      • +2
        Немного не согласен. Полиморфики все-таки существуют и очень даже применяются. Один более менее вменяемый малварный криптор без полиморфизма не напишешь. Не все же с помощью VB пакуется :) Кстати, будете разбирать полиморфизм, то посмотрите в сторону virus.win32.crypto.a, очень прикольная штука!
        • +2
          Спасибо, обязательно взгляну. Я имел в виду, что доля таких «более-менее вменяемых» невысока. Может я и неправ, и если кто нибудь из антивирусной компании будет читать этот пост, был бы рад услышать комментарии (если это не секрет, конечно).
  • +1
    Но мы же договорились, вирус – это то, что умеет само себя воспроизводить в системе, и чему необходим файл-носитель.
    Вы заузили определение вируса. Обычно ограничиваются самовоспроизведением.

    Поэтому всякие трояны-руткиты-malware – это не вирусы, а тип payload-кода, который вирус может таскать на себе. Для описываемых в статье технологий классификация компьютерных вирусов может быть только одна: полиморфные и неполиморфные вирусы. То есть меняющиеся от поколения к поколению, либо нет.
    Про классификации — всё очень зависит от точки зрения: полиморф или нет, резидент или нет, стелс или нет. По сути по разным аспектам поведения (изменчивость кода, нахождение в загруженном состоянии, способ сокрытия кода, соответственно).

    Можно, например, вспомнить вирусы, которые, по сути, содержали буткит + резидентный модуль, которые распространялся наружу в исполняемых файлах (а иногда и в бут-секторе, тоже код, но ни пол раза не файл), но на жертве хранил свой код в незанятых хвостах кластеров (не являющихся частями файлов, очевидно) или в MBR.
    • +2
      Именно это я и собирался сделать — сузить определение вируса до соответствующего биологическому определению, т.е. объекта, который способен порождать свои копии, и которому для работы и размножения необходима клетка-носитель.
      Резидент, стелс, буткит — термины, описывающие лишь способы, при помощи которых вирус ищет новые объекты для инфицирования и прячет себя. Ваш вариант с «буткит+резидентный модуль» это все равно вирус, т.к. ему для распространения необходимо положить свое тело (упакованное или нет) в исполняемый файл и потом получить управление на другой машине и развернуться там. И он тоже — либо полиморф, либо нет.
      Я вообще считаю что термин «компьютерный вирус» стали использовать неправильно, тупо называя им все нехорошие программы. Если проводить биологическую аналогию дальше, то трояны, руткиты и малварь, которые представляют собой совершенно самостоятельные программные модули, можно сравнить с бактериями — это полноценные клетки, которые способны существовать без носителя.
      • 0
        Резидент, стелс, буткит — термины, описывающие лишь способы, при помощи которых вирус ищет новые объекты для инфицирования и прячет себя… И он тоже — либо полиморф, либо нет.
        Как я пытался сказать выше: полиморфизм, подмена инфицированного на исходный (стелс), резидентность — ортогональные характеристики. Почему вы оказываете предпочтение одной из них? Полиморфизм — тоже один из способов сокрытия, не более.

        Ваш вариант с «буткит+резидентный модуль» это все равно вирус, т.к. ему для распространения необходимо положить свое тело (упакованное или нет) в исполняемый файл и потом получить управление на другой машине и развернуться там.
        Вариант с хранением тела не в файлах (MBR + хвосты кластеров) и заражением через бут-сектор дискет не подходит под ваше определение, но вполне себе классифицируется как вирус. При этом код лежит отнюдь не в файлах.

        А про «нехорошие программы» — дилетанты — они и в Африке дилетанты. Не стоит обращать на это внимания. Как и на всякие «Тыжкомпьютерщик? Почини чайник.»
        • +1
          С бут сектором попробую выкрутиться — это то же самое, только инфицируется не файл, а операционная система целиком. Если считать ОС — носителем, то такой вирус подходит под мою классификацию.

          Ну а про ортогональные характеристики: для перехода ко второй статье мне достаточно именно этой одной характеристики — полиморф, не полиморф. Грубо говоря, я рассматриваю вирус, который вообще ничего не делает, кроме как открывает исполняемые файлы (мне все равно каким образом) и записывает себя в них. Я оставляю только такой узкий набор для того, чтобы не отклоняться в сторону и провести больше аналогий с коммерческими защитами, в которых некоторые техники (например антиотладка) представлены более ярко.
  • +2
    И статейку про полиморфные вирусы тоже напишите, интересно. Помню лет 15-18 назад в Компьютерре читал статью про полиморфы — был ошеломлён тем, как можно писать такие вещи, которые самовопроизводятся и не совпадают ни в одном экземпляре.
    Любопытно послушать, куда с тех пор ушла эволюция.
    • +3
      Да напишу обязательно, многое в статье повисло «в воздухе».

      Правда эволюция к сожалению в этом вопросе далеко не ушла, самым продвинутым вирусным движкам уже много лет, многое было сделано в области коммерческих защит. Доступный коннект с сайтом разработчика софта и эти техники делает уже непопулярными — зачем защищать код внутри исполняемого файла, если его можно хранить на стороне разработчика. В общем постараюсь поскорее разродиться
    • +1
      Куда интереснее не полиморфизм, а метаморфизм. Вот это очень захватывающе, особенно разрабатывать. В средствах накладывания защиты ПО есть не мало попыток применять метаморфизм, но как правило на уровне вирт.машин
      • 0
        Раскройте, пожалуйста, термин «метаморфизм», мне не совсем понятно, что это такое.
        • +3
          Метаморфизм — это случай полиморфизма, когда программа в каждом новом поколении генерирует код, отличающийся от предыдущего поколения. Я обязательно про него подробно расскажу.

          Действительно, виртуальная машина это в общем-то метаморфный генератор, если научить её каждый раз на основе одного и того-же «базового» кода порождать различные варианты исполняемого
        • +2
          Дополню этот комментарий. Чтобы было понятнее возьмем две ситуации полиморфный вирус и метаморфный вирус. У первого, как правило, меняется только первая стадия, к примеру расшифровка или «мега-jmp», а у второго меняется ВЕСЬ код. Другими словами, если Вы авер и написали алгоритм успешно детектирующий алгоритм расшифровки, то в случае полиморфика Вы счастливые, а в случае метаморфа могут быть проблемы и дальше.
          К огромной радости пользователей ПК, метаморфики пишутся ооочень тяжело!
  • +1
    А я вот скажу, что жалко, жалко, что не было такого ресурса, когда я увлекался ассемблером. Правда, всё очень толково, а главное — интересно расписано, а это, блин, так важно, когда начинаешь кодить, а особенно — читать чужой код. Пойду на 2ю часть.
    • 0
      Спасибо за добрые слова. Никак не могу расстаться с любовью к ассемблеру. Когда он впервые понадобился мне, единственным доступным ресурсом был «Дом технической книги» на Ленинском проспект. А затем под всякие Windows-95,98 это стало просто нереально весело

      Сейчас на самом деле трудно программисту обосновать необходимость писать на голом С или асме, т.к. никто уже не даст тебе полезть в ядро системы и что-нибудь нахачить там. Тем не менее те, кто писал на низкоуровневых языках, в будущем, программируя на других, прекрасно понимают что происходит в железе, и это реально очень полезно.

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка