Средствам защиты программного обеспечения в ][ уже было посвящено несколько обзоров. В частности, в
Спеце 08 2005
была опубликована, IMHO, отличная статья, дающая общее представление об
упаковщиках и протекторах. Тем не менее, работа с рассматриваемыми в
обзоре программами требуют определенных навыков, и начинающим
разработчикам не всегда просто разобраться в том, как правильно
использовать протекторы.
Если проанализировать материалы специализированных форумов
(rusf.com, rsdn.ru, ixbt.com и др.), то среди рассматриваемого класса
программ сразу же можно выделить тройку лидеров. Это ASProtect,
Armadillo и EXECrypt. Именно эти три продукта обеспечивают достаточно
надежную защиту за разумную цену, что обеспечило им заслуженную
популярность среди разработчиков. В этой статье я попытаюсь рассказать
о первом из них.
С ASProtect я знаком более пяти лет. Созданный
выдающимся российским разработчиком Алексеем Солодовниковым, долгое
время он считался чуть ли не единственным протектором, для которого
практически невозможно было изготовить кейген.
Как вы понимаете, абсолютной защиты софта не бывает, да и быть не
может. Единственным объективным мерилом качества защиты может быть
соотношение стоимости продукта и стоимости его взлома. При
распространении программных продуктов разработчики терпят немалые
убытки из-за того, что в сети появляются серийные номера, ключи и
патчи. Но особые неприятности доставляют генераторы ключей, так
называемые кейгены. Наиболее эффективным методом избежать финансовых
потерь от появления серийных номеров программ является регулярный
выпуск обновлений, в которых добавляется функциональность программы, а
нелегальный номер или ключ вносится в «черный список». Зачастую
разработчики, с целью поднятия популярности своего продукта, сами
подкладывают «левые» ключи на популярные крэкерские ресурсы (в свое
время и я этим грешил
:)). Однако в том случае, если в сети появляется кейген, значительно
затрудняется возможность отличить легальные ключи от поддельных.
Следовательно, затрудняется не только блокировка нелегальных
пользователей защищаемого продукта, но и поддержка легальных, учитывая,
что владельцы продуктов могут и не иметь полного списка покупателей.
Прежде чем перейти к рассмотрению практических примеров, давайте разберем теорию работы c ASProtect. Итак, пакет, по утверждению создателя, позволяет реализовывать следующие функции:
Упаковка приложения;
Шифрование приложений;
Защита от снятия дампа с помощью таких средств как
ProcDump;
Контроль целостности приложения;
Защита от модификации памяти (to memory patching);
API для взаимодействия приложения с функциями защиты;
Создание и верификация регистрационных ключей с использованием открытых алгоритмов;
Ведение базы нелегальных ключей;
Возможность создавать триальные версии, которые ограничивают функциональность приложений по времени или количеству запусков;
Возможность генерации ключей с привязкой к «железу»;
Возможность создания on-line кейгенраторов, для таких регистрационных служб как RegSoft, RegNow!, ShareIt!,
SWREG.
Программист, использующий ASProtect, может
определить произвольное количество режимов, в которых может работать
его программа. Например, это может быть демонстрационный режим, в
котором программа ограничивается по функциональности и
зарегистрированный режим, где функциональность защищаемого продукта
становится полностью доступной зарегистрированному пользователю.
Основным отличием второй версии ASProtect от первой является использование обновленного API. ASProtect API предоставляет разработчику три группы функций:
Функции для получения информации о регистрации пользователя;
Функции для получения информации о параметрах Trial-режима;
Функции для управления зашифрованными фрагментами кода, которые исполняются только в зарегистрированном режиме.
Я использую термин «функция», в соответствии с документацией ASProtect,
несмотря на то, что Delphi Language предполагает разделение понятий
procedure и function. Некоторые из рассмотренных ниже подпрограмм
описаны как процедуры.
К первой группе относятся CheckKey (проверяет ключ для всех
поддерживаемых режимов), GetHardwareID (получает уникальный
идентификатор ПК), CheckKeyAndDecrypt (проверяет ключ для всех
поддерживаемых режимов и сохраняет его во внешнем конфигурационном
файле), GetRegistrationKeys (получает список ключей),
GetModeInformation (получает информацию о режиме, в котором программа
работает в данный момент, и обо всех остальных режимах),
GetRegistrationInformation (получает информацию о режиме, в котором
программа работает в данный момент).
Вторая группа содержит функции GetTrialDays (получает информацию о
параметрах триального периода) GetTrialExecs (проверяет соответствие
триального и текущего режимов), GetKeyDate (проверяет дату создания
ключа), GetExpirationDate (), GetKeyExpirationDate ().
К третьей группе относится одна-единственная функция - SetUserKey. Эта функция расшифровывает зашифрованные секции.
Кроме всего вышеперечисленного, ASProtect предоставляет описание структуры
TModeStatus:
Именно эта структура содержит информацию о режиме, в котором работает или потенциально может работать программа.
Отличительной чертой ASProtect’а является обилие примеров в комплекте поставки. При этом в примерах показано, как использовать ASProtect
для приложений, написанных в различных средах разработки. Наиболее
простым способом понять принцип работы программы является изучение
примеров. Попробуем посмотреть, как это делается в Delphi 2006 for
Win32.
Предположим, что у нас есть готовое приложение, и мы хотим его
защитить. Напишем тестовое приложение, содержащее простую форму с
кнопкой, нажатие на которую выдает сообщение «Hello world!». Далее
попробуем оснастить нашу программу элементарной защитой. Прежде всего
произведем некоторые манипуляции с кодом приложения.
В качестве базового примера возьмем Reg Trial. Соответственно, нас
интересует его Delphi-реализация.
Сразу же перепишем в директорию текущего проекта каталог Include.
Также нам понадобится подключить API, поэтому копируем файл
aspr_api.pas и прописываем его объявление в секции
Uses.
Обработаем метод OnShow() главной формы нашего приложения. В начале
обработчика вызовем процедуру GetRegistrationInformation(0, UserKey,
UserName) и тем самым узнаем, соответствует ли ключ и имя пользователя
какому-либо режиму работы. Как правило, триальный или
функционально-ограниченный режим работы не предполагают регистрации.
Поэтому дальше мы проверим, была ли она (регистрация) произведена
вообще и если да, то какому режиму она соответствует (функция
GetModeInformation).
Сразу замечаем, что, как только в нашей программе появился вызов
GetRegistrationInformation, программа перестанет запускаться, выдавая в
Event Log’e при запуске примерно следующее сообщение:
Module Load: UNKNOWN_MODULE_12. No Debug Info. Base Address: $00350000. Process Project2.exe (280)
Тем не менее, компиляция проходит успешно. Естественно, предварительно следует объявить константы:
Запустить же программу на исполнение мы сможем только тогда, когда .exe файл будет обработан ASProtect’oм.
Обратите внимание на то, что в примерах ASProtect
используются переопределяемые константы. По умолчанию в Delphi они
отключены и включаются директивой {$J+}. Если мы не включим эту
директиву, то для компиляции примера нам придется преобразовать
константы в переменные. Использование переопределяемых констант не
рекомендовано в старших версиях Delphi, однако автор, вероятно, оставил
их с целью совместимости.
Естественно, что обычная трассировка в нашем случае работать не
будет, поэтому для просмотра отладочной информации (если отслеживание
таковой потребуется) нам придется использовать
ShowMessage. Но обо всем по-порядку. GetRegistrationInformation
присвоит значения переменным UserKey, UserName только в том случае,
если будет обнаружен валидный ключ. В случае, если проверка прошла
успешно, и UserKey и UserName инициированы, вызываем функцию
GetModeInformation, для того чтобы понимать, в каком режиме запущена
наша программа.
Итак, наш код будет иметь следующий вид. При этом мы будем отслеживать
изменения всех задействованных переменных, а точнее констант.
procedure TForm2.Button1Click(Sender: TObject);
Var
ModeStatus : TModeStatus;
begin
GetRegistrationInformation( 0, UserKey, UserName );
ShowMessage(UserKey);
ShowMessage(UserName);
if (UserKey <> nil) AND (StrLen(UserKey) > 0) then
begin
{$I include\aspr_crypt_begin1.inc}
GetModeInformation( 0, ModeName, ModeStatus );
ShowMessage('Hello world!');
{$I include\aspr_crypt_end1.inc}
end
else ShowMessage('Register Please!');
end;
Предварительно скомпилировав проект, запускаем ASProtect.
Перед нами четыре закладки, на которых содержаться все необходимые для
работы настройки. На первой закладке устанавливаем пути к нашей
программе, прописываем информацию о ее версии, задаем уровень
компрессии и, при необходимости, производим дополнительные настройки.
Переходим к закладке Modes (Режимы). В нашем случае имеется два
варианта: либо программа зарегистрирована, либо нет. Соответственно,
создаем два режима, первый называем «Registered», второй – «Trial».
Делаем оба режима активными (нажимаем птичку «Is This Mode Active»).
Для режима «Registered» активируем «Is This Mode in Registered
State?» и «Use Activation Key». Смысл этих значений достаточно
прозрачен. Для первого (триального) режима установим количество дней, в
течение которых можно использовать нашу программу бесплатно (Expired
Days Number) и введем текст надписи, уведомляющей об окончании
ознакомительного периода (для этого нажимаем кнопку «Edit the message»,
находящуюся рядом).
После этого сразу перейдем к четвертой закладке (Protection). Если в
поле статистики мы видим надпись «Ready for protection...», то это
свидетельствует о том, что мы все сделали правильно. Нажимаем кнопку
“Protect” и смотрим статистику выполнения операций над исходным файлом.
File size: 420KB compressed to 494KB, Ratio: 117,6%
По окончании операции кнопка «Run» станет активной, и мы сможем
воспользоваться ей, для того чтобы запустить приложение. Если все
прошло нормально, то приложение запустится без проблем. Однако не
пытайтесь повторно защитить уже защищенный файл, ASProtect
не позволит этого сделать.
Запуск программы наглядно докажет, что защита работает. В качестве
имени пользователя и ключа будут показаны пустые значения, а вместо
надписи «Hello world!» вы увидите «Register Please». Если вы переведете
системное время вперед, то
программа откажется запускаться вообще. И, даже «вернув время», вам уже
не удастся запустить ее.
Теперь попробуем организовать регистрацию программы. Для этого
скопируем в папку нашего проекта форму RegForm из примера Reg Trial,
входящего в состав ASProtect, и добавим ее в проект. На главной форме разместим еще одну кнопку и реализуем вызов регистрационной формы:
frmRegKey.ShowModal;
Ниже приведен код регистрации программы.
procedure TfrmRegKey.btnRegOkClick(Sender: TObject);
begin
If CheckKeyAndDecrypt( PChar(eCode.Text), PChar(eName.Text), True ) then
begin
MessageBox(Handle, 'Thank you for your registration!', 'Registration', MB_ICONINFORMATION);
Close;
end else
MessageBox(Handle, 'Key is not valid, please contact manufacture!','Registration', MB_ICONWARNING);
end;
Как вы понимаете, валидность ключа проверяется функцией CheckKeyAndDecrypt.
Она проверяет ключ для всех реализованных режимов и сохраняет его во
внешний конфигурационный файл (в том случае, если последний параметр
функции - true). В том случае, если ключ соответствует имени, функция
возвращает true, в противном –
false.
Проверим работоспособность нашего кода. Вернемся в ASProtect
и перейдем на третью закладку, для того чтобы сгенерировать валидный
ключ. В списке режимов выберем второй (Registered), введем имя
пользователя в поле Name For Registration и нажмем кнопку Create. В
поле Registration key появится ключ. Нажимаем кнопку Copy, тем самым
скопируем ключ в буфер. Вставим значения ключа и имени пользователя в
регистрационную форму и увидим, что программа стала работать в
зарегистрированном режиме.
Сохраним проект ASProtect в файл проекта – Project1.aspr2. В этом файле сохраняется уникальная информация, которая послужит
ключом и сохранит настройки проекта.
Следующей задачей будет изготовление генератора ключей. Эта задача
является весьма важной, поскольку использовать непосредственно ASProtect
для изготовления ключа не всегда удобно и возможно. Значительно
практичней для этой цели иметь автономное приложение.
В очередной раз попытаемся разобраться с тем, как работает пример из
комплекта поставки. Открыв его (папка KeyGen), мы увидим достаточно
запутанный, на первый взгляд, код.
Function GenerateKey( var KeyStr, NameStr : String; Var Res : DWORD ): Boolean;
var
Params : TParams;
RegData : array[1..100] of Byte;
begin
FillChar(Params,SizeOf(Params),0);
With Params do
begin
// Registration name
RegName := PChar(NameStr);
// Mode number (1..15)
ModeID := 2;
// Expiration date
ExpirationDate.Day := 0;
ExpirationDate.Month := 0;
ExpirationDate.Year := 0;
// Constant for encryption
EncryptionConst := 699935518;
// HardwareID
HardwareID := '';
// Signature size in bits
SignatureBits := 96;
// Constants from project file
C1 :=
'AC08B45461D28EB';
C3 :=
'A93A5FB8C78D04F1';
C6 :=
'26D6BB434014649375231C68A655B81D314AEAF3CE32C6DC8BD'+
'CBCB9EEDC61EBA3C4A2119685E22205B782C88BFAF21FE98D4'+
'474FBDB1AFA539B05F09EF60DDF5BA8224161942B9582D37DD'+
'D624A97C373E5BB33BFC2335A674E4839D72253BA62B97F0B3'+
'6BCA5B4001353FED5D28FED31FA53C343F381BE121E106F669'+
'5B3CB';
C7 :=
'1726DAE1BD153379F1FB0F4F2E00C06D39A733B7715D108EF0C'+
'518A11DF528394E37F744E1CEE1B4B411FEAFD7DDE0E281042'+
'A728F85FEDB91C8C2ECECF31873C77D68053D550EB8549FDE2'+
'C1885F40B68E1F0FF3383B747882AA517EB6F9061E23650222'+
'85F1B1BEA91F83E2C4CD66BF0C636FC79B81A738D65D6F5BF8'+
'0234D7';
RegKey := @RegData[1]; // Buffer for registration key
RegKeySize := SizeOf(RegData); // Size of a buffer for reg. key
// Generating...
Res := RegistrationKeyGenerate( Params );
Result := (Res = kgERROR_SUCCESS);
If Result then SetString( KeyStr, RegKey, RealKeySize );
end;
end;
procedure TfrmMain.btnGenerateClick(Sender: TObject);
Var
KeyStr : String;
NameStr : String;
Res : DWORD;
ResStr : String;
begin
NameStr := eRegName.Text;
If not GenerateKey( KeyStr, NameStr, Res ) then
begin
case Res of
kgERROR_KG_ERROR : ResStr := 'Key generator error!';
kgERROR_MODE_UNKNOWN : ResStr := 'Unknown mode number!';
kgERROR_REGNAME_ERROR : ResStr := 'Incorrect registration name!';
kgERROR_SIGNATURE_ERROR : ResStr := 'Unknown mode signature!';
kgERROR_BUFFER_TOO_SMALL : ResStr := 'Result buffer too small!';
kgERROR_CONSTANTS_UNDEFINED : ResStr := 'Incorrect constants!';
kgERROR_HARDWAREID_ERROR : ResStr := 'Invalid HardwareID key!';
else
ResStr := 'Unknown error!';
end;
MessageBox(Handle,PChar(ResStr),'Error',0);
end else
eRegKey.Text := KeyStr;
end;
Из кода сразу же становится ясно, что основная функциональность
возлагается на GenerateKey. Но эту функцию нам придется писать самим.
Собственно говоря, в ней подготавливаются параметры для вызова другой
функции – RegistrationKeyGenerate, которая, в свою очередь, является
функцией ASProtect API. Каким же образом мы получаем
эти параметры? Все очень просто. Все они прописаны в файле проекта.
Если открыть его в обычном текстовом редакторе, то можно увидеть, что
все параметры описаны в явном виде. Однако, если вы уже приготовились к
изнурительной работе с использованием Copy-Paste –
расслабьтесь. В подкаталоге Templates присутствует файл KeyGen.exe, с
помощью которого мы сможем автоматически получить код функции
RegistrationKeyGenerate для своего проекта. Для этого откроем файл
проекта и выберем в списке режимов тот, для которого требуется ключ.
Нажатие кнопки «Generate Template» и код функции у нас на экране. А вот
теперь - Copy-Paste. Фактически, для изготовления собственного
генератора ключей нам нужно всего лишь заменить в примере код функции
GenerateKey на только что полученный.
Конечно, в статье я описал только простейшую схему «навешивания» защиты на программные продукты с помощью ASProtect.
На самом деле, пакет благодаря обилию возможностей позволяет
адаптировать защитные механизмы к самым различным схемам
распространения ПО.
В заключение хочу дать несколько рекомендаций. Прежде всего, защищайте с помощью ASProtect
фиксированные версии. Поскольку даже мелкая переделка защищенного
продукта может обернуться значительной головной болью. Как следствие,
не пренебрегайте созданием BAK-копий защищаемого
продукта и хорошо структурируйте фрагменты кода, содержащего вызовы ASProtect
API, для того чтобы при необходимости их легко можно было отключить.
Здесь я не буду пытаться высказывать свое мнение по поводу
целесообразности и практичности приобретения рассматриваемого пакета. Я
также не буду сравнивать его функциональность и надежность с
конкурентами. Вместо этого я прилагаю к статье выдержки из форумов, в
которых муссируется этот вопрос.
Если же вы захотите приобрести ASProtect, у вас
могут возникнуть некоторые трудности. Продукт распространяется в
основном через ShareIt (это такая служба регистрации), а предлагаемые
им формы оплаты не совсем удобны для отечественного покупателя. Так,
при попытке купить ASProtect, не была принята ни одна
из моих карточек Visa, хотя все они были абсолютно легальными, и я
неоднократно с их помощью совершал покупки в Интернете. Тем не менее,
выход нашелся достаточно быстро. Мне посоветовали написать в службу
поддержки на русском языке, что я незамедлительно и сделал.
Мое первое письмо успешно отсек антиспамовый фильтр (я отправил его со
своего ящика на Яндексе). Второе письмо, однако, дошло до адресата, и
буквально через сутки я приобрел искомый продукт за WMZ. Через сутки
пришло письмо с активацией моего аккаунта на форуме техподдержки. В нем
содержится масса практических советов по использованию ASProtect, но, увы, он доступен только для зарегистрированных пользователей.