Содержание статьи
Появившаяся некогда идея проверять файл сразу по многим антивирусам оказалась
очень удачной. Онлайновая антивирусная служба www.virustotal.com имеет тысячи
посещений ежедневно, при этом обладая дюжиной недостатков. Сервис не раскрывает
своих секретов, однако автор давно разобрал его по винтикам и теперь работает
над улучшенной реализаций.
В то время как одни пользователи держат на компьютере целый зоопарк различных
антивирусов, конфликтующих друг с другом и тормозящим ПК (не говоря уже о
стоимости лицензий или сложности поиска правильного «лекарства»), хакеры
предпочитают ловить малварь самостоятельно. В крайнем случае – проверяют
подозрительные файлы на бесплатных онлайновых службах типа того же
www.virustotal.com. Эти
же службы используются для «обкатки» вирусов собственного написания на предмет
обнаружения эвристическими анализаторами. И хотя, если верить
блогу Евгения
Касперского, хакеры не доверяют virus-total'у, поскольку он передает
подозрительные файлы антивирусным компаниям и вирусы начинают палиться еще на
старте, эта точка зрения отражает лишь малую часть действительности. Да,
действительно, профессиональные разработчики атакующих программ и rootkit'ов
проверяют их на «вшивость» исключительно локальным способом на своих собственных
машинах, предотвращая утечку информации, но… профессионалов единицы, к тому же
экспериментируя с virus-total'ом, хакеры определяют общие критерии ругательства
антивирусов, выявляя последовательности машинных команд/вызовов API-функций,
приводящих к срабатыванию эвристического анализатора. Однажды «обломав»
антивирус, хакер может многократно использовать найденный способ обхода
эвристика. Достаточно посетить любые форумы, где обитают вирусописатели, чтобы
убедиться, что они весьма неравнодушны к virus-total'у и активно его используют.
А что если создать еще более качественный сервис? Ведь virus-total примитивен
до ужаса – качество сканирования оставляет желать лучшего, не говоря уже о
длинных «социалистических» очередях, в которых приходится подолгу простаивать
из-за частых перегрузок сервера (а все потому, что балансировка нагрузки и
оптимизация изначально не предусматривались!).
На момент написания этих строк, мыщъх по заказу одной антивирусной компании
(имя которой разглашать не вправе) руководит разработкой онлайнового сервиса,
рассчитанного на «магистральную» загрузку и предоставляющего пользователям кучу
всевозможных рычагов управления. Естественно, исходный код к статье не
прилагается, да он и не нужен, главное – это концепт, плюс некоторые неочевидные
тонкости, с которыми придется столкнуться при «промышленных» масштабах
эксплуатации.
Естественно, это требует широких сетевых каналов, мощных многопроцессорных
систем и еще кучу всего. Словом, без солидных финансовых вложений тут никак не
обойтись. Однако никто не заставляет нас создавать сервис планетарного масштаба
и, если постараться, можно вполне уложиться в бюджет $2000 или даже меньше. Нам
потребуется интернет-канал с безлимитным тарифом, чтобы злые люди не кинули нас
на входящий трафик, который будет весьма значительным. В качестве компьютера
вполне подойдет машина с процессором Core2Duo и парой гигабайт оперативной
памяти. О проблемах с лицензированием антивирусов мы поговорим в одноименной
врезке, а пока отметим, что никаких особых программистских навыков не
понадобится. Подойдет любой язык (Си, Perl, PHP) и минимальный опыт работы с CGI
(пользуясь случаем, хочу порекомендовать библиотеку CGIC).
Virus-total изнутри
Virus-total устроен не просто, а очень просто. Он использует консольные
версии антивирусов, управляемые посредством командой строки и выдающие результат
сканирования в стандартный проток вывода, который легко перенаправить в файл или
пайп (pipe).
Что мы делаем? Через специальную форму на сайте закачиваем «подопытный» файл,
скармливаем его антивирусу, предварительно перенаправив вывод во временный файл/пайп,
который тут же парсим (то есть выбрасываем все лишнее, оставляя только статус
проверки и имя вируса). Парсить вывод легче всего Perl'ом, поддерживающим мощный
механизм регулярных выражений, но Си-программы намного более производительны, а
потому предпочтительнее (особенно, при большом наплыве пользователей).
На этом, собственно говоря, возможности virus-total'а и заканчиваются. Это
создает большие проблемы: во-первых, далеко не все антивирусы имеют консольные
версии, а, во-вторых, даже те, что имеют, поведением зачастую радикально
отличаются от полноценных GUI-версий. В чем легко убедиться, сравнив результаты
сканирования большой коллекции вирусов локальным способом и через virus-total –
сравнение будет отнюдь не в пользу virus-total'а.
Учитывая, что практически все антивирусы (и GUI-версии в том числе)
поддерживают запись результатов сканирования в log-файл и позволяют задавать имя
сканируемого файла через командную строку или на худой конец через механизм DDE
(Dynamic Data Exchange), ничего не стоит прикрутить GUI-версию к онлайновой
службе. Просто «скармливаем» антивирусу файл, форсируем запись результатов
сканирования в log-файл, который парсим так же, как и вывод консольных версий.
Остается только собрать «показания» всех имеющихся в нашем распоряжении
антивирусов, оформить их в виде HTML-таблицы и выдать на экран, что по силам
даже самым начинающим программистам.
В клинических случаях, когда антивирус начисто игнорирует командную строку
или не умеет вести логи, на помощь приходит механизм Windows-сообщений (Windows
Message или, сокращенно, WM). Посылая WM-сообщения элементам управления
антивируса, мы можем манипулировать кнопками, меню и прочими элементами
управления по своему усмотрению. Аналогичным способом извлекается и содержимое
окна, содержащего результаты проверки. Получив форматированный rich-текст или
plain-текст, пропускаем его через парсер – и все!
Маленькие секреты больших серверов
При попытке реализации вышеописанной модели неизбежно всплывут проблемы
удручающе низкой производительности. Но мы не боимся трудностей и начнем щемить
проблемы одну за другой. Первое и очевидное. Как показывает статистика,
различные пользователи преимущество проверяют одни и те же файлы, как правило,
принадлежащие Windows или популярным программным пакетам. Чтобы сократить
накладные расходы, рекомендуется подсчитывать контрольную сумму файла перед его
проверкой и, если такой файл уже проверялся ранее, выдавать уже готовые
результаты сканирования, сохраненные в базе данных. На первый взгляд, в
реализации данного алгоритма нет ничего сложного, но тут притаилось немало
подводных камней. Вот только наиболее актуальные:
- антивирусные базы обновляются постоянно и потому даже за короткий
промежуток времени информация о сканировании безнадежно устаревает.
Следовательно, необходимо вместе с контрольной суммой сохранять и дату
последнего времени сканирования (отображая ее пользователю), а так же
предусмотреть кнопочку «rescan»; - использование алгоритма CRC32 может показаться плохой идей, поскольку он
выдает множество коллизий (разные файлы имеют идентичные контрольные суммы),
к тому же его легко подделать, модифицировав любое количество байт файла и
затем скорректировав 4 байта так, чтобы скомпенсировать искажения. Однако
CRC32 шустро работает, обгоняя MD5 и другие хорошие алгоритмы. Поэтому
возникает идея: для каждого файла, прогоняемого через антивирусы, мы
генерируем CRC32 и MD5 (с учетом времени сканирования накладными расходами
на расчет контрольной суммы можно пренебречь), а при последующей проверке
залитого пользователем файла сначала проверяем CRC32 (проверяется очень
быстро) и, если такой контрольной суммы в нашей базе нет, MD5 можно и не
вычислять – зачем? Ведь и так видно, что данный файл еще не проверялся; - многие «честные» файлы (особенно входящие в состав операционной системы)
снабжены цифровой подписью или их целостность может быть проверена путем
обращения к серверам Microsoft, что осуществляется намного быстрее
антивирусного сканирования и, если по данным Microsoft, файл не изменен,
зачем его прогонять через антивирусы?!
Также крайне желательно реализовать опцию, позволяющую пользователю выбирать
режим сканирования с эвристикой и без (чего не сделано на virustotal). Эвристика
представляет собой довольно затратную по времени и ресурсам ЦП операцию, но
далеко не все пользователи доверяют полученным результатам и хотят видеть имя
конкретного вируса (если он есть), а не расплывчатое предупреждение, обычно
ругающееся на упаковщик/протектор, которым обработан честный файл. С другой
стороны, вирусописателям совершенно неинтересно сканирование по базе (так как
только что написанного вируса там заведомо нет) и они предпочли бы задействовать
только эвристику, экономя тем самым ресурсы нашего сервера. Так почему бы не
пойти им навстречу?
Закачка больших файлов предоставляет серьезную проблему, имеющую несколько
решений. Самое простое (и глупое) – установить верхний предел закачиваемого
файла в пару мегабайт (или около того), чуть-чуть умнее: лимитировать суммарный
размер всех файлов, закачанных за сутки с одного IP (но тут возникает проблема
определения IP, поскольку очень часто мы будем видеть не IP пользователя, а IP
прокси сервера провайдера). Полезно рекомендовать пользователям сжимать файлы
перед отправкой zip'ом или другим популярным архиватором для уменьшения нагрузки
на канал или делать это автоматически на клиентской стороне специальным скриптом.
Наконец, за сканирование больших файлов можно взимать деньги, но об этом мы
поговорим чуть позже, а пока продолжим тему оптимизации.
Профилировка показывает, что львиная доля накладных расходов приходится на
запуск антивируса, инициализацию его движка и загрузку антивирусных баз.
Перемещение антивирусов на виртуальный диск существенно увеличивает
«подвижность» системы, но накладные расходы на создание новых процессов
по-прежнему будут большими, поэтому мы используем GUI-версии антивирусов и,
путем эмуляции клавиатурного ввода, воздействуем на элементы управления,
заставляя их сканировать новые файлы и выдавать результат. При этом антивирус
запускается всего один раз. Красота! Впрочем, можно реализовать и динамический
алгоритм: при небольшой нагрузке на сервер о накладных расходах на порождение
новых процессов можно не заботиться, а с ростом нагрузки – просто брать
несколько файлов, закачанных пользователями за последние несколько минут и
«скармливать» их антивирусу одним скопом, в результате чего количество запусков
последнего резко сокращается. Главное не запутаться, какой пользователь что
закачал, но это уже мелочи технической реализации.
Естественно, сканирование лучше запускать на всех антивирусах параллельно, а
не последовательно и вместо того, чтобы «тупо» запрещать пользователю закрывать
окно браузера до окончания процесса сканирования (как это делает virus-total),
отслеживать TCP/IP соединение и при его обрыве автоматически «выбрасывать» файл,
принадлежащий данному пользователю, из очереди на сканирование. Плюс реализовать
стандартную кнопку «отмены» (так же отсутствующую у virus-total'а) – если
пользователь видит, что первые три-четыре антивируса ничего не находят, так
следует ли дожидаться результатов проверки всех антивирусов? Особенно, если
самые качественные антивирусы поставить вперед остальных, выделив им
максимальный приоритет ЦП. Как вариант, можно вообще не следить за TCP/IP сессий
и при заливке нового файла назначать пользователю ID задачи, который он может
ввести в любое время, отключившись от Сети и повторно подключившись, например,
через час, когда его очередь уже подошла. А можно рассылать результаты
сканирования по e-mail – тогда пользователь не будет скучать в ожидании своей
очереди.
И совсем не помешает прикрутить к нашему сервису утилиту вроде PEiD,
определяющую тип и версию упаковщика/проектора (правда, довольно часто
ошибающуюся). И опционально реализовать распаковку набором статических
распаковщиков, работающих намного быстрее тех, что встроены в антивирусы. Тут
есть один подводный камень – хотя 99% вирусов распознаются по распакованному
дампу, некоторые, особо ленивые, сотрудники антивирусных компаний включают в
базу сигнатуры упакованного файла и после распаковки он перестает опознаваться
как вирус. Однако, учитывая, что распакованный файл прогоняется через легион
антивирусов, вероятность ложно-негативного срабатывания стремится к нулю.
Зарабатываем деньги лопатами
Мир жесток и все в нем упирается в деньги. На голом энтузиазме никой
онлайновый сервис долго не продержится, поэтому приходится разрабатывать не
только программный код, но и жизнеспособную бизнес-схему. Рассмотрим возможные
источники дохода. Первое – рост посещаемости нашего сайта. На посещаемости, как
известно, можно нехило заработать, особенно, если мы, например, продаем
собственные защитные комплексы, предлагаем услуги по пен-тестингу и т.д.
Онлайновый сервис привлекает клиентов намного активнее любых баннеров и,
главное, привлекает именно тот контингент, который нам нужен, следовательно,
возросшие объемы продаж покроют все расходы на поддержку и обслуживание
серверов, отплату трафика и т. д.
Второе – отчисления от антивирусных компаний, чью продукцию мы рекламируем и
кому передаем штаммы свежих вирусов. Тут, правда, много не заработаешь,
поскольку лишь небольшая часть посетителей кликнет по ссылке «купить» антивирус,
а стоимость вирусного штамма обычно составляет $1 (а то и меньше). Вот и считай,
на какой уровень посещаемости нужно выйти, чтобы окупить расходы на поддержку
сервера, которые, кстати говоря, тем больше, чем выше посещаемость.
Третье – взимать деньги непосредственно с самих пользователей. Хочешь подолгу
стоять в очередях и сканировать файлы не больше чем … мегабайт? Пожалуйста,
пользуйся нашим сервисом бесплатно! Хочешь иметь определенные привилегии – будь
добр заплатить. Главное, выбрать удобную схему оплаты. Здесь вам не Америка,
здесь климат (финансовый) иной. Кредитные карты имеют единицы, электронные
системы платежей только начинают набирать популярность. Зато практически каждый
IT-специалист – владелец сотового телефона, а значит, можно воспользоваться
микро-платежами через SMS либо потребовать от клиента сообщить номер карты
универсальной оплаты (перечислив заданную сумму на счет, который он может
расходовать, когда пользуется нашим сервисом). Как показывает практика, сотовые
платежи приносят наибольшую отдачу, поскольку телефоны распространены
повсеместно, а сам процесс оплаты требует минимум телодвижений, и (что
немаловажно) клиент практически ничем не рискует. А вот с кредитными картами все
намного сложнее и есть риск, что нечестный оператор снимет с них совсем не ту
сумму, какая ожидалась. Тоже относится и к микро-платежам через SMS. Гарантий,
что снимут 150 рублей, а не 450, у клиента нет никаких.
А что в итоге?
Разумеется, в статье охвачены далеко не все проблемы, с которыми неизбежно
столкнется всякий, попытавшийся воздвигнуть подобный онлайновый сервис. Но
задача выполнена – мыщъх предоставил вполне законченную, отлаженную и
работоспособную схему, которая скоро будет запущена в промышленную эксплуатацию.
|
Юридические проблемы лицензирования
Пользовательские соглашения (EULA) на коммерческие антивирусы не разрешают
использовать их в онлайновых сервисах без заключения специальных контрактов, что
вообще-то логично. Однако не стоит думать, что всякий контракт обязательно
связан с необходимостью выплаты дополнительных отчислений. Вовсе нет! Достаточно
проявить надлежащий дипломатический подход!
Крупные бренды заинтересованы в рекламе своей продукции и потому охотно
разрешают использовать полнофункциональные версии антивирусов без всяких
отчислений, поскольку, конечный пользователь реально видит, кто сосет, а кто
нет. Правда, могут выдвинуть встречные условия типа сохранения логотипов,
генерации ссылок на их сайты и т. д. Все это мелочи, решаемые в рабочем порядке.
А что мелкие бренды? Они, конечно, понимают, что сравнение с конкурентами
будет не в их пользу. Тут есть один очень интересный момент. Мелкие антивирусные
компании страдают хронической нехваткой свежих вирусов, которые попадают к ним в
последнюю очередь, и потому онлайновый сервис, автоматически отсылающий уже
детектируемые конкурентами вирусы – для них прекрасное средство пополнения
вирусных баз и продвижения в различных рейтингах. С мелких брендов можно даже
взимать плату за каждый новый для них вирус, и большая вероятность, что платить
они согласятся!
Короче говоря, лицензионные проблемы – это и не проблемы вовсе. А вот разных
проволочек – предостаточно и нужно заранее быть готовыми к тому, что нас будут
перебрасывать от одного ответственного лица (которое ничего не решает) к
другому, третьему, и так по цепочке… Это уже издержки цивилизации, против
которых не попрешь.
Проблемы конфиденциальности
Вирусы встречаются не только в программах, но и в офисных документах, PDF'ах
и прочих файлах с конфиденциальной информацией, разглашать которую крайне
нежелательно, поэтому необходимо предусмотреть опцию «не отсылать данный файл в
антивирусные центры», при необходимости взводимую пользователем. Технически это
реализуется проще простого, но… как избежать злоупотреблений?! Особенно, если мы
строим наш бизнес на отправке свежих штаммов разработчикам антивирусов?
Идея первая (тупая до безобразия) – наплевать на все приличия и отсылать
файлы в антивирусные центры независимо от состояния каких-то там галочек.
Главное: создать у пользователя иллюзию, что его конфиденциальность строго
блюдут, ну а что происходит на самом деле, он все равно не узнает. Ну… до тех
пор, пока тайное не станет явным и не разразится скандал, идущий совсем не на
пользу нашему ресурсу.
Идея вторая – поддерживать эту опцию только для пользователей, открывшим у
нас счет. Кстати, это представляет собой нехилую мотивацию для оплаты услуг,
особенно с учетом того, что о конфиденциальности в основном беспокоятся
корпоративные пользователи, привыкшие платить за услуги (в отличие от домашних
юзеров, тяготеющих к халяве вне зависимости от стоимости полнофункционального
аккаунта).
Идея третья – файл все-таки отправлять, но перед этим удалять всю текстовую и
графическую информацию, что не противоречит ни логике, ни здравому смыслу, ни
даже соглашению, заключенному с пользователем нашего сервиса.