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

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

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

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

Для начала данные выравниваются с помощью нулевых битов таким образом, чтобы их длина L удовлетворяла формуле L = 512 * N + 448, где N - любое натуральное число. Почему, можете спросить вы, 448? Потому что в остальные 64 бита записывается длина исходных данных - и поскольку мы оперируем с достаточно коротким по длине MAC-адресом, сейчас не будем останавливаться на том, что делать, если длина исходных данных не влезает в эти 64 бита. Затем буфер, используемый в дальнейшем для вычислений, инициализируется с помощью специфических исходных значений, а также выводится специальная таблица констант, состоящая из 64 элементов, каждый из которых вычисляется по формуле T[i] = int(4294967296 * |sin(i)|) (число, стоящее в качестве коэффициента перед синусом - это два в тридцать второй степени). После этого идут, собственно, вычисления - они выполняются итерационно, работа идет с блоками по 512 бит. Каждая итерация разбита на четыре "раунда", во время которых по соответствующим формулам считаются значения, записываемые в буфер. Итоговый хэш, таким образом, также оказывается записанным в буфер.

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

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

Что ж, для начала остановимся подробнее на первом варианте. Самый простой способ его реализации (и самый неудобный для конечного пользователя вашего программного продукта) - это просто вывести сгенерированный ключ на экран и заставить его самостоятельно отослать вам по почте. Вариант, конечно, имеет право на жизнь, но от него издалека пахнет большим количеством потерянных в результате такого отношения к пользователю продаж. Да и ключ при таком раскладе должен быть достаточно коротким, что не всех разработчиков устраивает. Поэтому давайте лучше воспользуемся интерфейсом MAPI, чтобы самостоятельно сформировать текст послания и передать его используемому в системе по умолчанию e-mail-клиенту - пользователю при таком раскладе останется только нажать кнопку "Отправить", что вряд ли его особенно напряжет.

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

Листинг 1

function SendEmail(const RecipName, RecipAddress,
 Subject, Attachment: string): Boolean;
var
 MapiMessage: TMapiMessage;
 MapiFileDesc: TMapiFileDesc;
 MapiRecipDesc: TMapiRecipDesc;
 i: integer;
 s: string;
begin
 with MapiRecipDesc do
 begin
  ulRecerved:= 0;
  ulRecipClass:= MAPI_TO;
  lpszName:= PChar(RecipName);
  lpszAddress:= PChar(RecipAddress);
  ulEIDSize:= 0;
  lpEntryID:= nil;
 end;
 with MapiFileDesc do
 begin
  ulReserved:= 0;
  flFlags:= 0;
  nPosition:= 0;
  lpszPathName:= PChar(Attachment);
  lpszFileName:= nil;
  lpFileType:= nil;
 end;
 with MapiMessage do
 begin
  ulReserved := 0;
  lpszSubject := nil;
  lpszNoteText := PChar(Subject);
  lpszMessageType := nil;
  lpszDateReceived := nil;
  lpszConversationID := nil;
  flFlags := 0;
  lpOriginator := nil;
  nRecipCount := 1;
  lpRecips := @MapiRecipDesc;
  if length(Attachment) > 0 then
  begin
   nFileCount:= 1;
   lpFiles := @MapiFileDesc;
  end
  else
  begin
   nFileCount:= 0;
   lpFiles:= nil;
  end;
 end;
 Result:= MapiSendMail(0, 0, MapiMessage, MAPI_DIALOG or
  MAPI_LOGON_UI or MAPI_NEW_SESSION, 0) = SUCCESS_SUCCESS;
end;

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

Вариант с отправкой сообщений через MAPI достаточно неплох, но, согласитесь, есть некоторые минусы. Например, у пользователя может банально не оказаться учетной записи электронной почты или же на компьютере нет почтовых программ (и, соответственно, не будет среди них и программы по умолчанию), это создаст неудобства. И не просто неудобства - для вас этот пользователь с вероятностью 99% будет уже потерян. Поэтому, если уже выбор сделан именно в пользу электронной почты, то гораздо правильнее будет отправлять все данные с её помощью незаметно для пользователя, используя SMTP-сервер самого разработчика.

Для работы с SMTP-сервером при программировании на Delphi (а именно этот язык мы с вами условились использовать при разговоре о защите программных продуктов) удобно применять компонент TIdSMTP из библиотеки компонентов Indy, имеющейся в стандартной поставке Delphi. Работа с этим компонентом для отправки писем непосредственно через SMTP-сервер без привлечения почтового клиента по умолчанию продемонстрирована в листинге 2.

Листинг 2

procedure SendMessageThroughSMTP;
var Msg:TIdMessage;
begin
 Msg := TIdMessage.Create(Nil);
 try
  Msg.Body.Append('Registration code generated for...');
  Msg.Recipients.EMailAddresses := 'developer@company.com';
  Msg.From.Text := 'user@home.org';
  Msg.ContentType := 'text/html';
  IdSMTP1.Connect;
  Idsmtp1.Send(Msg);
  IdSMTP1.Disconnect;
 finally
  Msg.Free;
 end;
end;

В данном коде компонент TIdSMTP имеет имя IdSMTP1. Как видите, все, что от нас требуется в данном случае, это создать само сообщение, что реализуется при помощи экземпляра класса TidMessage, и заполнить все соответствующие поля. Нужно, конечно, заполнить и свойства самого компонента, включая адрес SMTP-сервера, порт, логин и пароль для авторизации. Когда все это настроено, можно использовать приведенный в листинге 2 код для отправки сообщений.

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

Вадим СТАНКЕВИЧ,
dreamdrusch@tut.by

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

Номер: 

42 за 2010 год

Рубрика: 

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