Защитить свой продукт

Продолжим разговор о написании защит для различных приложений под Windows, начатый нами на страницах "Компьютерных вестей" несколько номеров назад. Сегодня обсудим проблемы создания системы активации пользовательских копий приложения.


Краткое содержание предыдущих серий

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

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

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


Постановка задачи

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

Реализовывать такой протокол, конечно, можно по-разному, но наилучшим вариантом в данном случае будет использование банального TCP/IP, потому что он обеспечивает весь необходимый набор функций для реализации онлайновой активации, и при этом пользователь не будет пугаться такого подключения, если его попытается заблокировать файрвол (кстати говоря, достаточно неплохой идеей будет предупреждать о том, что приложение осуществляет подключение через определенные порты на случай, если у пользователя установлен файрвол, который действительно будет спрашивать у пользователя, можно ли разрешить нашему приложению осуществить подключение к серверу активации).

Можно, конечно, вместо простого текстового протокола использовать XML - тот же, в общем-то, текст, только содержащий некоторую избыточную информацию, хотя и позволяющий более гибко изменять протокол, оставляя старые версии приложения совместимыми с новыми версиями данного протокола. Но мы с вами сейчас рассмотрим более простой вариант, чтобы не усложнять примеры к статье рассмотрениями работы с XML-парсером. Если вы все-таки решите остановиться на XML-реализации, то не забудьте, что данные, пересылаемые в формате XML, тоже имеет смысл защищать и шифровать.


Реализация

Итак, давайте посмотрим, как можно реализовать данный протокол средствами Delphi. Поможет нам в этом, конечно же, та самая библиотека компонентов, которая уже выручала при работе с SMTP, - Indy. В её составе есть как серверный, так и клиентский компонент, которые пригодятся нам и при создании активационной подсистемы в самом приложении, и при написании сервера активации. Сначала рассмотрим работу клиентской части, а затем уже перейдем к разговору о сервере. В общем-то, и клиентская, и серверная часть будут достаточно простыми, потому что всю действительно сложную работу за нас уже выполнили создатели библиотеки Indy, и в этом, в общем-то, вся прелесть работы с ней.

Что ж, давайте вместе посмотрим на листинг 1, в котором продемонстрирована реализация клиента для нашего протокола активации.

Листинг 1

procedure SendActivationInformation;
begin
 TCPClient.Host := '100.100.100.100';
 TCPClient.Connect;
 TCPClient.SendCmd('Some activation-related information...');
 TCPClient.Disconnect;
end;

Для этого мы воспользовались компонентом TIdTCPClient. Также не забудем положить на форму компонент TIdAntiFreeze, который позволяет избежать "заморозки" работы приложения при обработке TCP/IP запросов. Как написано в справке по данному компоненту, "компонент работает, анализируя запросы из стека протокола TCP/IP и посылая сообщения приложению во время задержки при возникновении блокировки внешних соединений, что создает иллюзию работающего кода. Поскольку воздействие компонента осуществляется на блокированные соединения только для главного процесса, использование TIdAntiFreeze во вторичных процессах приложения не требуется. Необходимо помнить, что компонент TIdAntiFreeze замедляет работу соединений, поскольку работа главного процесса периодически прерывается для обработки сообщений. Отсюда следует, что надо заботиться о том, чтобы разрабатываемое приложение не тратило слишком много времени на обработку сообщений, включая OnClick, OnPaint, OnResize и др. В какой-то степени этим можно управлять через свойства класса TIdAntiFreeze. Использование данного компонента не является обязательным, но позволяет решить проблему синхронизации соединений с визуальным интерфейсом приложения". Нужно также поменять у клиента и сервера порт - лучше брать какое-нибудь не используемое распространенными приложениями число, например, 1026.

Итак, давайте внимательнее посмотрим на то, что же именно написано в листинге 1. В общем-то, думаю, большей частью всё достаточно понятно и без подробных пояснений. В первой строчке мы указываем адрес нашего удаленного сервера активации, с которым программа будет устанавливать соединение и с которым будет далее работать. Затем мы к нему подключаемся. По-хорошему, конечно, нужно было бы обернуть процесс подключения в конструкцию try...except или try...finally, но, думаю, что с этим большая часть наших читателей справится и вполне самостоятельно, поэтому можно не обращать внимания на отсутствие такой полезной, но вместе с тем достаточно простой конструкции. В последней строке, как видите, послав серверу информацию об активации (о том, какую именно информацию можно и даже нужно посылать, можно прочитать в предыдущих статьях цикла "Защитить свой продукт"), обрываем подключение. Хотя если вы используете связь с сервером не только для активации, но и для какого-либо другого общения между сервером и установленной у конечного пользователя копией программного продукта, то, безусловно, имеет смысл не обрывать подключение, а поддерживать его в постоянном режиме, чтобы иметь возможность оперативно обмениваться информацией между клиентом (приложением) и активационным сервером.

Давайте сейчас посмотрим на код, предложенный вашему вниманию в листинге 2, - в нем вы можете увидеть, как сервер TIdTCPServer принимает информацию, на основе которой позже можно сгенерировать код, необходимый приложению для собственной активации. Этот код является обработчиком события OnExecute указанного компонента, являющегося сервером нашего TCP-протокола.

Листинг 2

procedure TActivationServer.TCPSrvExecute (AContext: TIdContext);
var
 p: TIdBytes;
 i: integer;
 s: string;
begin
//. Receive string from user's side
 AContext.Binding.Receive(p);
 s := '';
 for i := 0 to Length(p) - 1 do
 begin
  s := s + Chr(p[i]);
 end;
 GenerateActivationCode(s);
end;

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

Что ж, в силу ограниченного объёма газетной статьи, думаю, пока приостановим наш разговор до выхода в свет следующих номеров "КВ".

Вадим СТАНКЕВИЧ,
[email protected]

Версия для печатиВерсия для печати

Номер: 

44 за 2010 год

Рубрика: 

Software
Заметили ошибку? Выделите ее мышкой и нажмите Ctrl+Enter!