Make Nod32 Mirror Updated
Внимание! Данный пост был опубликован более года назад и, возможно, уже утратил свою былую актуальность. Но это не точно.

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

Если подробнее - то она не подходила для версий антивируса старше четвертой. Как оказалось - нод не умеет самостоятельно определять под-директорию из которой ему следует обновиться, и как следствие все пытались обновиться из корня - и как следствие обновлялась только “четверка”. Далее - старая версия не умела обновлять компоненты антивируса. Далее - в итоговое зеркало не попадали все необходимые “модули”. Ещё - был чертовски кривой веб-интерфейс.

Так же просмотрел значительное количество решений других разработчиков. Забавно то, что 90% из них написаны на php, и состоят из одного файла с лютым хардкодом всего что можно, и чего нельзя. Да и не поддерживаются совсем.

А что же теперь? А теперь мне удалось избавиться и от этих проблем, и попутно (с момента публикации этого поста уже произошли какие-то изменения, однозначно) повысить стабильность. Не получилось пока реализовать авто-обновление, но я над этим думаю. Традиционно скрипт умеет сам искать ключи.

screnshot

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

Но это не всё. Здесь я бы хотел рассказать чуть подробнее о результатах анализа алгоритмов работы и некоторых практиках, к которым в итоге пришел.

Общий подход

  • Повторное использование кода даже в bash-скриптах - это очень хорошая практика. Разнесение функций по логически разделенным файлам - добро. Если у тебя есть необходимость скачивать файлы из сети - создай отдельный файл, назови его, скажем - network.sh, и всё что связано с работой по сети - пиши в нем. В итоге все вызовы curl/wget у тебя сосредоточены в одном месте, и ты не паришься.

  • Именования функций с префиксом имени файла - что-то в роде namespace-ов - добро. Если ты видишь функцию, например, ui_message - то сразу понимаешь что её описание находится в файле ui.sh.

  • Используй boostrap-файл. Это отдельный скрипт, подключая который ты инициализируешь все необходимые переменные и подключаешь все требуемые файлы, вынося в него весь общий код инициализации. В последствии если тебе (или другому разработчику) потребуется создать что-то своё - он просто подключает bootstrap-файл и получает в своё распоряжение весь функционал и готовое окружение. Да что я тебе рассказываю..

  • Дай пользователю возможность использовать свои собственные конфиги. Суть в том что в процессе работы ты будешь что-то добавлять в общий конфиг, а что то удалять. Ты будешь обновлять скрипт, а вместе с ним и его конфиг. Как следствие переписывание “стандартного” конфига из раза в раз вызывает дикую боль. А если ты позволяешь подключать пользовательские конфиги сразу после своего основного - можно об этом не беспокоиться.

  • Инициализируй критичные переменные принудительно.

  • Не стесняйся ставить точки с запятой после операции/метода/языковой конструкции там, где это возможно. Так ты даешь явное указание что это является её завершением, да и код становится читабельнее.

  • Пиши комментарии по необходимости, пиши их на английском языке и только, не стесняйся давать полные названия переменным. На счет крайнего - я серьезно. Не описывай переменную “tes” подразумевая “Temporary echo result”. Пиши “temporary_echo_result”. Через день ты забудешь что имел в виду так её называя, а другим разобраться в твоем коде станет ой как не просто.

  • И ещё раз - повторное использование кода. Не надо делать 10 вызовов echo - сделай одну функцию ui_message, и всё выводи ей. В итоге если тебе надо будет переформатировать вывод - ты не будешь судорожно искать “так, а где у меня выводится этот текст?”, ты будешь просто фильтровать вывод в одной функции. Очень удобно.

О том как работает Nod32

Как уже говорил выше - нод не умеет брать обновления из директорий v1, v2, nod_upd что расположены в корне твоего зеркала. Вот просто не умеет. Для того чтоб он “пятерка” брала обновления из директории v5, необходимо её туда перенаправить. Как? Смотри, User-Agent’ы у нода (четверки) такие:

ESS Update (Windows; U; 32bit; VDB 29908; BPC 4.2.71.3; OS: 6.2.9200 SP 0.0 NT; TDB 29908; CH 0.0; LNG 1049; x64c; APP essbe; BEO 1; ASP 0.0; FW 0.4; PX 0; PUA 0; RA 0; PEV 29480)
ESS Update (Windows; U; 32bit; VDB 21093; BPC 4.2.71.3; OS: 5.1.2600 SP 3.0 NT; TDB 21093; CH 1.1; LNG 1049; x32c; APP eav; BEO 1; ASP 0.10; FW 0.0; PX 0; PUA 1; RA 0)
"ESS Update (Windows; U; 32bit; VDB 22122; BPC 4.2.71.3; OS: 6.1.7600 SP 0.0 NT; TDB 22122; CH 1.1; LNG 1049; x32c; APP essbe; BEO 1; ASP 0.0; FW 0.0; PX 0; PUA 1; RA 0)

Что их объединяет? Правильно: BPC 4.*. А какие у пятерки?

ESS Update (Windows; U; 32bit; PVT F; VDB 30070; BPC 5.0.95.0; OS: 6.1.7601 SP 1.0 NT; TDB 30070; CL 0.0.0; LNG 1049; x64c; APP eav; BEO 1; ASP 0.10; FW 0.0; PX 0; PUA 1; RA 0; PEV 29837)
ESS Update (Windows; U; 32bit; PVT F; VDB 22122; BPC 5.0.2228.1; OS: 5.2.3790 SP 2.0 NT; TDB 22122; CL 1.1.1; LNG 1049; x32s; APP eea; BEO 3; ASP 0.10; FW 0.0; PX 0; PUA 0; RA 0; HWF: 0100AA70-13A2-F3BB-DEE3-D2ABBCF7E297)
ESS Update (Windows; U; 32bit; PVT F; VDB 22122; BPC 5.0.95.0; OS: 6.1.7600 SP 0.0 NT; TDB 22122; CL 0.0.0; LNG 1049; x64s; APP eavbe; BEO 1; ASP 0.10; FW 0.0; PX 0; PUA 0; RA 0)

Логично предположить, что у “шестерки” это BPC 6.*. Что мы делаем? Мы регуляркой находим значение версии в user-agent’е, проверяем наличие нужной под-директории, и если всё хорошо - то 301 редиректом перенаправляем Nod32 в нужную под-директорию. Да, нод понимает редиректы. Пример конфига:

  # Redirect request to correct update.ver file, based on nod32 user-agent version
  location ~* "^(?/update\.ver)$" {
    if ($http_user_agent ~* "BPC (?[\d]{1,2})\.") {
      set $new_location "v$version$file_url";
    }
    if (-f $document_root/$new_location) {
      return 301 $scheme://$server_name/$new_location;
    }
  }

Да, if-зло, но без него здесь ну никак. Если знаешь как реализовать лучше - дай знать в комментариях к этому посту.

Как дебажить запросы Nod32?

Я дебажил с помощью прозрачного прокси-сервера Charlies. Запускаешь его, вырубаешь всё остальное что ломится в сеть, и нажимаешь “Обновить базу данных сигнатур вирусов” в Nod32. В итоге можешь наблюдать что и куда отправляется, какие заголовки, какие ответы. Ахуенно, в общем. Всегда его использовал для этого:

На этом, пожалуй, и всё. Если есть какие-либо вопросы - пиши в комментарии, постараюсь ответить.