first commit

Version 3.x.x
This commit is contained in:
VNGhostMans
2023-05-14 20:21:09 +07:00
parent a3037a8db3
commit 5ec92ee05e
1166 changed files with 1036539 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="Stylesheet" type="text/css" href=
"../../default.css" />
<meta http-equiv="Content-Type" content=
"text/html; charset=utf-8" />
<title>Алгоритмы шифрования серийных номеров</title>
<style type="text/css">
</style>
</head>
<body>
<h1>Алгоритмы шифрования серийных номеров</h1>
<p>Надежность ключей системы лицензирования основывается на
применении асимметричных алгоритмов шифрования. В текущей версии
реализован алгоритм RSA с длиной ключа от 1024 до 16384 бит. В
будущих версиях планируется добавление других алгоритмов,
основанных на ECC, а также схем, совмещающих симметричные и
асимметричные шифры.</p>
<p>Используемый алгоритм уникален для каждого продукта. Ключи,
созданные с одним алгоритмом, не подойдут к другому, поэтому
смена алгоритмов после создания хоть одной лицензии не
допускается. Модуль защиты, располагающийся в защищаемой
программе, "знает" каким алгоритмом должен быть зашифрован
серийный номер и не примет ключ, созданный другим алгоритмом или
тем же алгоритмом, но с другими параметрами (скажем, с другой
длиной ключа).</p>
<h3>Алгоритм RSA</h3>
<p>Шифрование серийного номера алгоритмом RSA происходит по
следующей схеме:</p>
<ul>
<li><strong>Добавление случайных данных в начало
номера</strong> - метод основывается на RFC2313, однако
реализация немного отличается от описаной там. В начало
серийного номера добавляютя байты: 00 02 NN...NN 00, где NN..NN
- от 8 до 16 случайных ненулевых байт. Количество байт
выбирается случайным, однако принимается во внимание длина
серийного номера и максимальная вместительность ключа.</li>
<li><strong>Добавление случайных данных в конец номера</strong>
- окончательное количество байт в серийном номере должно
равняться количеству бит в ключах алгоритма, деленному на 8.
Серийный номер дополняется до этого числа байт случайными
данными. В результате получается следующий формат серийного
номера: 00 02 NN..NN 00 DD..DD MM..MM, где NN - набор случайных
ненулевых байтов, DD - исходный серийный номер, MM -набор
случайных байтов (в т.ч. и нулевых). Суммарная длина
последовательности должна равняться количеству бит,
применяемому в алгоритме, деленному на 8.</li>
<li><strong>Шифрование</strong> - производится по стандатной
схеме, реализованной во многих библиотеках, работающих с
большими числами. Генератор на PHP содержит всю необходимую
информацию в открытом виде.</li>
<li><strong>Упаковка</strong> - полученный после шифрования
набор байт кодируется в base-64 - это и есть серийный номер,
который отправляется покупателю.</li>
</ul><br />
<br />
<br />
<br />
<br />
<hr noshade="noshade" size="1" />
<div align="center">
© 2006-2015 Copyright VMProtect Software
</div>
</body>
</html>

View File

@@ -0,0 +1,121 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="Stylesheet" type="text/css" href=
"../../default.css" />
<meta http-equiv="Content-Type" content=
"text/html; charset=utf-8" />
<title>Генераторы ключей</title>
<style type="text/css">
</style>
</head>
<body>
<h1>Генераторы ключей</h1><strong>Зачем они нужны?</strong>
<p>Помимо VMProtect, серийные номера могут генерироваться другими
программами - генераторами. Это нужно для автоматизации выдачи
номеров без участия человека. Покупатель покупает продукт,
регистратор делает HTTP-запрос на сайт производителя, там
запускается программа-генератор и на основе данных о покупателе
формирует серийный номер. Номер отправляется покупателю и
производителю. Производитель вносит его в базу VMProtect вручную,
через окно импорта лицензий.</p><strong>Принцип работы</strong>
<p>Система лицензирования VMProtect построена на асимметричных
алгоритмах, поэтому для генерации серийных номеров требуется
секретный ключ продукта. Этот ключ можно экспортировать в окне
свойств продукта и передать программе-генератору подходящим для
нее способом.</p>
<p>Программа-генератор вызывается регистратором через
HTTP-запрос. Генератор на PHP может быть вызван напрямую,
генератор в виде DLL - косвенно, но алгоритм остается одним и тем
же:</p>
<ul>
<li>Получить данные о пользователе от регистратора</li>
<li>Добавить необходимую информацию, которую указал
производитель</li>
<li>Сформировать серийный номер</li>
<li>Зашифровать его одним из алгоритмов</li>
<li>Передать результат регистратору</li>
</ul><strong>Существуют ли готовые генераторы?</strong>
<p>С системой лицензирования поставляется три готовых генератора
серийных номеров <a href="keygen_dll.htm">в виде DLL</a>,
<a href="keygen_net.htm">для платформы .Net</a> и <a href=
"keygen_php.htm">реализованный на PHP</a>.</p><strong>Можно ли
сделать свой генератор?</strong>
<p>Можно. Формат серийного номера находится <a href=
"serial_format.htm">здесь</a>, алгоритмы шифрования серийного
номера описаны <a href=
"algorithms.htm">здесь</a>.</p><strong>Насколько это
безопасно?</strong>
<p>В общем случае - вполне безопасно. Однако есть несколько
рекомендаций, которым стоит следовать:</p>
<ul>
<li><strong>Используйте протокол HTTPS</strong> - если ваш
регистратор умеет делать запрос по HTTPS, а ваш хостинг
позволяет отвечать на такие запросы, то этот вариант
предпочтительнее обычного HTTP, т.к. поток данных будет
зашифрован и сгенерированный серийный номер сможет получить
только регистратор.</li>
<li><strong>"Спрячьте" генератор</strong> - убедитесь, что
генератор нельзя вызвать случайно. Адрес
www.site.com/keygen.php - плохая идея. А вот
www.site.com/abc123.php - уже лучше. Убедитесь, что на
генератор нет внешних ссылок, что он не показывается при
просмотре содержимого директории сайта и не помещайте его в
служебные файлы типа robots.txt. Чем меньше известно о
расположении генератора - тем лучше. Как вариант - можно
вынести генератор на другой сайт.</li>
<li><strong>Убедитесь, что вас вызывает регистратор</strong> -
в программе, обрабатывающей запрос от регистратора, необходимо
проверить IP-адрес вызывающей стороны. Обычно регистраторы
публикуют список адресов, с которых они могут вызвать
генератор. Найдите такой у вашего регистратора и добавьте
проверку. В случае несовпадения адреса, лучше не выдавать
понятных ошибок. Не возвращайте ничего или верните ошибку 404.
Не давайте повода понять, почему вызов оказался неудачен.</li>
<li><strong>Проверяйте входные параметры</strong> - в
настройках продукта у регистратора обычно дается возможность
указать строку запроса, который регистратор будет делать для
получения лицензии. Скажем, вы хотите получать имя
пользователя, адрес его электронной почты, дату продажи и номер
заказа. Убедитесь при получении запроса, что все параметры
переданы и что все они имеют корректный формат. Не отвечайте на
запросы с ошибками. О любых ошибках уведомляйте себя отправкой
e-mail со всеми параметрами, с которыми был вызван генератор.
Это поможет разобраться в причинах проблемы.</li>
<li><strong>Добавьте "пароль"</strong> - укажите в запросе,
посылаемом регистратором, дополнительный параметр - пароль. С
неочевидным именем и значением. Проверяйте его на принимающей
стороне. В случае несовпадения или отсутствия - не генерируйте
номер.</li>
</ul><br />
<br />
<br />
<br />
<br />
<hr noshade="noshade" size="1" />
<div align="center">
© 2006-2015 Copyright VMProtect Software
</div>
</body>
</html>

View File

@@ -0,0 +1,284 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="Stylesheet" type="text/css" href=
"../../default.css" />
<meta http-equiv="Content-Type" content=
"text/html; charset=utf-8" />
<title>Генератор ключей: Windows-версия</title>
</head>
<body>
<h1>Генераторы ключей: Windows-версия</h1>
<h3>Описание</h3>
<p>Генератор для Windows представляет собой DLL-файлы для
платформ x86 и x64, заголовочный файл языка C и lib-файл,
совместимый с MSVC. Таким образом, библиотека может быть как слинкована статически, так
и загружаться динамически.</p>
<p>Все файлы генератора находятся в каталоге
<strong>Keygen\DLL</strong>, там же находится тестовое
приложение, которое генерирует серийные номера.</p>
<h3>API генератора</h3>
<p>Генератор экспортирует всего две функции: одна непосредственно
генерирует серийный номер, вторая - освобождает память,
выделенную первой. Начнем с первой и основной функции:</p>
<pre class="code"><strong>VMProtectErrors</strong> __stdcall <strong>VMProtectGenerateSerialNumber</strong>(
<strong>VMProtectProductInfo *</strong> pProductInfo,
<strong>VMProtectSerialNumberInfo *</strong> pSerialInfo,
<strong>char **</strong> pSerialNumber
);
</pre>
<p>Первый параметр - указатель на структуру
<strong>VMProtectProductInfo</strong>, содержимое которой
выгружается в VMProtect (см. <a href=
"../licenses.htm#export">Экспорт параметров продукта</a>).
Структура содержит приватный ключ продукта, используемый алгоритм
и количество бит, а также идентификатор продукта. Подробнее о
заполнении этой структуры написано ниже.</p>
<p>Второй параметр - указатель на структуру
<strong>VMProtectSerialNumberInfo</strong>, содержимое которой
переносится в генерируемый серийный номер. Структура содержит все
поля серийного номера, а также битовую маску, определяющую какие
именно поля должны быть записаны в серийный номер.</p>
<pre class="code">struct <strong>VMProtectSerialNumberInfo</strong>
{
<strong>INT</strong> flags;
<strong>wchar_t *</strong> pUserName;
<strong>wchar_t *</strong> pEMail;
<strong>DWORD</strong> dwExpDate;
<strong>DWORD</strong> dwMaxBuildDate;
<strong>BYTE</strong> nRunningTimeLimit;
<strong>char *</strong> pHardwareID;
<strong>size_t</strong> nUserDataLength;
<strong>BYTE *</strong> pUserData;
};
</pre>
<p>Поле <strong>flags</strong> содержит битовые флаги из набора
<strong>VMProtectSerialNumberFlags</strong>, описанного перед
структурой:</p>
<ul>
<li><strong>HAS_USER_NAME</strong> - поместить в серийный номер
имя пользователя, находящееся в переменной
<strong>pUserName</strong>.</li>
<li><strong>HAS_EMAIL</strong> - поместить в серийный номер
e-mail пользователя, находящийся в переменной
<strong>pEMail</strong>.</li>
<li><strong>HAS_EXP_DATE</strong> - серийный номер перестанет
действовать поле окончания дня, указанного в переменной
<strong>dwExpDate</strong>.</li>
<li><strong>HAS_MAX_BUILD_DATE</strong> - серийный номер будет
подходить только к версиям продукта, созданным до даты,
указанной в переменной <strong>dwMaxBuildDate</strong>.</li>
<li><strong>HAS_TIME_LIMIT</strong> - программа перестанет
работать по истечении времени, указанного в переменной
<strong>nRunningTimeLimit</strong> (время указывается в минутах
и не может превышать 255).</li>
<li><strong>HAS_HARDWARE_ID</strong> - программа будет работать
только на оборудовании с идентификатором, указанным в
переменной <strong>pHardwareID</strong>.</li>
<li><strong>HAS_USER_DATA</strong> - поместить в серийный номер
пользовательские данные длинной
<strong>nUserDataLength</strong>, расположенные по адресу,
указанному в переменной <strong>pUserData</strong>.</li>
</ul>
<p>Третий параметр - указатель на указатель. Туда будет записан
адрес сгенерированного серийного номера. После генерации номер
необходимо скопировать, а адрес передать во вторую функцию API
генератора, которая освободит занимаемую серийным номером
память.</p>
<pre class="code"><strong>void</strong> __stdcall <strong>VMProtectFreeSerialNumberMemory</strong>(<strong>char *</strong> pSerialNumber);
</pre>
<p>Функция <strong>VMProtectGenerateSerialNumber</strong>
возвращает значение типа <strong>VMProtectErrors</strong>,
содержащее 0 в случае успешной генерации номера или код ошибки в
противном случае. Возможны следующие коды ошибок:</p>
<ul>
<li><strong>ALL_RIGHT</strong> - ошибок нет, номер сгенерирован
успешно.</li>
<li><strong>UNSUPPORTED_ALGORITHM</strong> - в первом параметре
функции передан некорректный алгоритм шифрования ключа.</li>
<li><strong>UNSUPPORTED_NUMBER_OF_BITS</strong> - в первом
параметре функции передано некорректное количество бит в
ключе.</li>
<li><strong>USER_NAME_IS_TOO_LONG</strong> - длина имени
пользователя в кодировке UTF-8 превысила 255 байт.</li>
<li><strong>EMAIL_IS_TOO_LONG</strong> - длина e-mail
пользователя в кодировке UTF-8 превысила 255 байт.</li>
<li><strong>USER_DATA_IS_TOO_LONG</strong> - длина
пользовательских данных превышает 255 байт.</li>
<li><strong>HWID_HAS_BAD_SIZE</strong> - идентификатор
оборудования имеет некорректный размер.</li>
<li><strong>PRODUCT_CODE_HAS_BAD_SIZE</strong> - идентификатор
продукта, переданный в первом параметре функции, имеет неверный
размер.</li>
<li><strong>SERIAL_NUMBER_TOO_LONG</strong> - серийный номер
слишком длинный и не помещается в указанное в алгоритме количество
бит.</li>
<li><strong>BAD_PRODUCT_INFO</strong> - первый параметр функции
некорректен или NULL.</li>
<li><strong>BAD_SERIAL_NUMBER_INFO</strong> - второй параметр
функции некорректен или NULL.</li>
<li><strong>BAD_SERIAL_NUMBER_CONTAINER</strong> - третий
параметр функции не указывает на ячейку памяти для записи
адреса серийного номера.</li>
<li><strong>NOT_EMPTY_SERIAL_NUMBER_CONTAINER</strong> - третий
параметр функции не указывает на пустую ячейку, в ячейке должно
быть значение NULL.</li>
<li><strong>BAD_PRIVATE_EXPONENT</strong> - значение приватной
экспоненты в первом параметре функции некорректно.</li>
<li><strong>BAD_MODULUS</strong> - значение модуля в первом
параметре функции некорректно.</li>
</ul>
<p>Ошибки делятся на две категории: те, что связаны с
некорректными параметрами или с содержимым первого параметра
функции, и все остальные. Ошибки первой категории редки и
являются признаком некорректной настройки структуры. Имеет смысл
выгрузить информацию о продукте повторно и убедиться, что
структура заполняется правильно. Пример правильного заполнения
приведен ниже.</p>
<p>Ошибки второй категории связаны с попыткой вместить в ключ
больше данных, чем это возможно ввиду его размера. В таком случае
разумно будет отправить регистратору вместо ключа текст "Ключ
будет выслан в течение суток", а всю необходимую информацию
отправить себе по почте. Ключ в таком случае генерируется в
VMProtect в ручном режиме, какие-то данные обрезаются, чтобы
уместить все необходимое в максимальный размер
ключа.</p>
<h3>Пример использования</h3>
<p>Ниже приведен пример кода, который вызывает две вышеописанные
функции и генерирует серийный номер. Обратите внимание на блок
кода, расположенный в самом начале. Пример не будет работать пока
вы не замените его на блок, выгруженный для продукта из
VMProtect:</p>
<pre class="code">//////////////////////////////////////////////////////////////////////////
// !!! this block should be generated by VMProtect !!! ///
//////////////////////////////////////////////////////////////////////////
<strong>VMProtectAlgorithms</strong> g_Algorithm = ALGORITHM_RSA;
<strong>size_t</strong> g_nBits = 0;
<strong>byte</strong> g_vModulus[1];
<strong>byte</strong> g_vPrivate[1];
<strong>byte</strong> g_vProductCode[1];
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
<strong>int</strong> _tmain(<strong>int</strong> argc, <strong>_TCHAR*</strong> argv[])
{
<strong>VMProtectProductInfo</strong> pi;
pi.algorithm = g_Algorithm;
pi.nBits = g_nBits;
pi.nModulusSize = sizeof(g_vModulus);
pi.pModulus = g_vModulus;
pi.nPrivateSize = sizeof(g_vPrivate);
pi.pPrivate = g_vPrivate;
pi.nProductCodeSize = sizeof(g_vProductCode);
pi.pProductCode = g_vProductCode;
<strong>VMProtectSerialNumberInfo</strong> si = {0};
si.flags = HAS_USER_NAME | HAS_EMAIL;
si.pUserName = L"John Doe";
si.pEMail = L"john@doe.com";
<strong>char *</strong> pBuf = NULL;
<strong>VMProtectErrors</strong> res = <strong>VMProtectGenerateSerialNumber</strong>(&amp;pi, &amp;si, &amp;pBuf);
<strong>if</strong> (res == ALL_RIGHT)
{
<strong>printf</strong>("Serial number:\n%s\n", pBuf);
<strong>VMProtectFreeSerialNumberMemory</strong>(pBuf);
}
else
{
<strong>printf</strong>("Error: %d\n", res);
}
<strong>return</strong> 0;
}
</pre>
<p>Этот пример с проектом для Microsoft Visual Studio находится в
каталоге <strong>Keygen\DLL\Example</strong>, ниже будут описаны
наиболее интересные места кода.</p>
<p>Первые строчки функции <strong>main</strong> заполняют
структуру <strong>VMProtectProductInfo</strong> данными,
выгруженными из VMProtect. Этот код стандартен, его не следует
менять во избежание появления ошибок. Далее создается структура
<strong>VMProtectSerialNumberInfo</strong>, в поле флагов
выставляется комбинация из битов имени пользователя и e-mail.
Строчкой ниже в соответствующие поля структуры заносятся значения
имени пользователя и пароля. Обратите внимание, что значения
принимаются в кодировке UNICODE. Генератор ключей внутри
преобразует их в кодировку UTF-8.</p>
<p>Далее описывается переменная-указатель, куда будет записан
адрес сгенерированного ключа, и вызывается функция
<strong>VMProtectGenerateSerialNumber</strong>, после чего
анализируется код возврата. Если ошибок нет, то сгенерированный
номер печатается на консоль и вызывается функция осовобождения
памяти.</p>
<h3>Остальные поля структуры VMprotectSerialNumberInfo</h3>
<p>Некоторые поля структуры требуют дополнительных поясненений. К
примеру, поля <strong>dwExpDate</strong> и
<strong>dwMaxBuildDate</strong> содержат даты в определенном
формате: <strong>0xYYYYMMDD</strong>, т.е. год хранится в старшем
слове, а месяц и день в старшем и младшем байтах младшего слова.
Для формирования такого числа описан макрос <strong>MAKEDATE(y,
m, d)</strong>, который можно вызвать так: <strong>MAKEDATE(2010,
05, 12)</strong>.</p>
<p>В поле <strong>pHardwareID</strong> должен быть записан
указатель на строку, которую вернул метод
<strong>VMProtectGetCurrentHWID</strong> из SDK
лицензирования.</p><br />
<br />
<br />
<br />
<br />
<hr noshade="noshade" size="1" />
<div align="center">
© 2006-2015 Copyright VMProtect Software
</div>
</body>
</html>

View File

@@ -0,0 +1,115 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="Stylesheet" type="text/css" href=
"../../default.css" />
<meta http-equiv="Content-Type" content=
"text/html; charset=utf-8" />
<title>Генератор ключей: .Net-версия</title>
<style type="text/css">
/*<![CDATA[*/
div.c2 {text-align: center}
p.c1 {color:red;}
/*]]>*/
</style>
</head>
<body>
<h1>Генераторы ключей: .Net-версия</h1>
<h3>Описание</h3>
<p>.Net-версия генератора ключей преставляет собой сборку,
содержащую все необходимое для генерации серийных номеров.
Исходные коды находятся в каталоге <strong>Keygen\Net</strong> в
виде двух проектов: KeyGen (непосредственно генератор серийных
номеров) и Usage (пример использования генератора).</p>
<p class="c1">Генератор поставляется в исходных кодах, чтобы
иметь возможность пересобрать его под нужную версию .Net
Framework, однако крайне не рекомендуется вносить изменения в его
код. В будущих версиях VMProtect возможно добавление новых
возможностей в генератор, что приведет к необходимости повторной
модификации кода. Также возможно появление трудноуловимых ошибок.
В случае нахождения ошибки в оригинальном коде генератора или
пожеланий по его улучшению - обращайтесь в службу
поддержки.</p>
<h3>Использование генератора</h3>
<p>Возьмите за основу код из проекта <strong>Usage</strong>,
добавьте в Ваш проект ссылку на сборку VMProtect.KeyGen.dll,
после чего Вы сможете генерировать серийные номера в вашем
приложении. Для нормальной работы генератор должен понимать,
к какому продукту Вы собираетесь генерировать серийные номера.
Для этого на закладке "Лицензии" VMProtect нажмите кнопку
«Экспорт» и в выпадающем списке выберите «Параметры для
KeyGen.Net». В текстовом поле ниже появятся несколько строк
текста, которые необходимо скопировать и перенести в Ваше
приложение в виде строковой константы.</p>
<p>Пример вызова генератора представлен ниже:</p>
<pre>
try
{
string data = @""; // put the exported data here
Generator g = new Generator(data);
g.UserName = "John Doe";
g.EMail = "john@doe.com";
g.ExpirationDate = DateTime.Now.AddMonths(1);
g.MaxBuildDate = DateTime.Now.AddYears(1);
g.RunningTimeLimit = 15;
g.HardwareID = "AQIDBAgHBgU=";
g.UserData = new byte[] { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
string serial = g.Generate();
Console.WriteLine("Serial number:\n{0}\n", serial);
}
catch (Exception ex)
{
Console.WriteLine("Error: {0}", ex);
}
</pre>
<p>Строку, скопированную в окне экспорта VMProtect, необходимо
поместить в переменную <strong>data</strong>, которая будет
передана параметром в конструктор класса генератора серийных
номеров. В случае каких-либо проблем с разбором данных продукта,
конструктор выбросит исключение с описанием проблемы. Если
конструктор отработал успешно, то генератор готов к созданию
серийных номеров.</p>
<p>Серийный номер может содержать различную информацию, которая
задается через свойства генератора. Пример выше показывает, как
заполнить абсолютно все поля серийного номера. Отдельные поля
имеют ограничения. Например, поля <strong>User Name</strong> и
<strong>E-Mail</strong> не могут принять строки, которые в
кодировке UTF-8 будут иметь длину, превышающую 255 символов. В
случае подачи некорректных данных, свойства выбрасывают исключения с
описанием проблемы.</p>
<p>По окончании настройки генератора вызывается метод
<strong>Generate()</strong>, который и создает серийный номер. На
этом этапе происходит компоновка всех данных номера, подсчет
контрольных сумм и, непосредственно, шифрование. В случае, если
объем данных превышает максимально допустимую длину, метод выбросит
исключение.</p>
<p>В случае, если необходимо сгенерировать несколько серийных
номеров, можно использовать класс генератора несколько раз, не
пересоздавая его. Чтобы "очистить" любое из свойств генератора,
ему нужно присвоить значение <strong>null</strong>.</p><br />
<br />
<br />
<br />
<br />
<hr noshade="noshade" size="1" />
<div align="center">
© 2006-2015 Copyright VMProtect Software
</div>
</body>
</html>

View File

@@ -0,0 +1,143 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="Stylesheet" type="text/css" href=
"../../default.css" />
<meta http-equiv="Content-Type" content=
"text/html; charset=utf-8" />
<title>Генератор ключей: UNIX-версия</title>
</head>
<body>
<h1>Генераторы ключей: UNIX-версия</h1>
<h3>Описание</h3>
<p>UNIX-версия генератора ключей преставляет собой PHP-файл,
содержащий все необходимое для генерации серийных номеров. Файл
находится в каталоге <strong>Keygen\PHP</strong>, ниже
рассмотрены основные моменты его
использования.</p>
<h3>Настройка генератора</h3>
<p>В самом начале PHP-файла располагается код настройки
генератора:</p>
<pre class="code">//////////////////////////////////////////////////////////////////////////////////////////////
// The following lines should be generated by VMProtect License Manager
$exported_algorithm = "RSA";
$exported_bits = 2048;
$exported_private = "PJvj4kEpoQMIpYK+9wEt......xKeiSZgzdiln8Q==";
$exported_modulus = "rOlny/3QgZb/VmGr3CmY......I6ESAUmtQ+RBqQ==";
$exported_product_code = "oLQdGUn8kVk=";
//////////////////////////////////////////////////////////////////////////////////////////////
</pre>
<p>Этот код генерируется автоматически в VMProtect (см. <a href=
"../licenses.htm#export">Экспорт параметров продукта</a>) и
уникален для каждого конкретного продукта. Очень важно
скопировать его без ошибок, в противном случае генератор будет
работать некорректно.</p><strong>Содержимое ключа</strong>
<p>Далее в генераторе задается содержимое серийного номера.
Содержимое задается массивом, ниже задаются все возможные
параметры ключа, однако в реальных условиях часть из них может
отсутствовать:</p>
<pre class="code">$params = <strong>array</strong>(
user_name =&gt; "John Doe", // UTF-8!
email =&gt; "john@doe.com",
hwid =&gt; "vHGMdMRvGCPjWcCQ", // Exactly as returned by VMProtectGetCurrentHWID
expire_date =&gt; <strong>array</strong>(year =&gt; 2009, month =&gt; 10, day =&gt; 1),
maxbuild_date =&gt; <strong>array</strong>(year =&gt; 2009, month =&gt; 10, day =&gt; 1),
time_limit =&gt; 10,
user_data =&gt; <strong>base64_decode</strong>("CGCvRvMWcPHGdMjQ"), // string of bytes
);
</pre>
<h3>Функция обработки успешной генерации</h3>
<p>Ниже расположена простейшая реализация функции, которая
вызывается при успешной генерации серийного номера. Единственным
параметром она получает строку с номером. Функция должна передать
номер вызывающей стороне (регистратору), обычно это делается при
помощи команды <strong>echo</strong>. Предварительно строка
разбивается на подстроки длиной 75 символов для удобства. Также
эта функция может отправлять сгенерированный номер по почте
производителю программы или заносить его в базу данных.</p>
<pre class="code">function <strong>OnSerialGenerated</strong>($serial)
{
$serial = <strong>wordwrap</strong>($serial, 75, "\n", true);
<strong>echo</strong> $serial;
}
</pre>
<h3>Функция обработки ошибок генерации</h3>
<p>Последний участок кода, требующий внимания - это функция,
которая вызывается в случае возникновения проблем. Функция
получает в виде параметра строку с текстом ошибки, а после ее
завершения вызывается функция <strong>die()</strong>. Функция
должна сделать две вещи: вернуть регистратору вместо ключа текст
о том, что ключ будет выслан вручную, а также выслать
производителю программы всю необходимую информацию для устранения
ошибки и ручной генерации ключа.</p>
<pre class="code">function <strong>OnSerialGenerationFailed</strong>($details)
{
<strong>echo</strong> "You will receive serial number in the next 24 hours"; // message to the customer
// mail("support@vendor.com", "Houston, we have a problem", $details); // message to vendor
}
</pre>
<p>Причин возникновения ошибки может быть несколько: некорректные
параметры алгоритма, некорректные параметры ключа, слишком
длинное имя пользователя или e-mail. Слишком длинный серийный
номер, который не влезает в количество бит, указанное в
алгоритме. Поэтому функция
<strong>OnSerialGenerationFailed</strong> должна отправить
производителю максимально подробную информацию о случившемся,
дабы тот мог вручную сгенерировать серийный номер и отправить его
покупателю.</p>
<h3>На что еще следует обратить внимание</h3>
<p>Генератор ключей в примерах максимально упрощен. Он не
учитывает рекомендации по разработке веб-генераторов, данные
<a href="index.htm">здесь</a>. Он не проверяет IP-адрес
вызывающей стороны и не анализирует входные параметры. Имейте это
в виду, когда будете создавать свой собственный генератор на
основе этого.</p>
<p>Имя пользователя и e-mail должны быть переданы в кодировке
UTF-8. Уточните у вашего регистратора, в какой кодировке он
передает информацию о покупателе и, при необходимости, проведите
конвертацию. Неправильная кодировка не приведет к
неработоспособности серийного номера, однако номер будет
содержать совершенно другое имя пользователя и он может быть
удивлен, если вы показываете его, скажем, в окне "О
Программе".</p>
<p>Асимметричное шифрование - достаточно сложный в математическом
плане процесс. Если он реализован на чистом PHP, без применения
сторонних библиотек, то генерация серийного номера может занять
десятки секунд. Генератор использует функции
<strong>gmp_powm</strong>, <strong>bi_powmod</strong>,
<strong>bcpowod</strong> в случае их наличия. Все эти функции
имеют разную скорость. Если генерация ключей на вашем хостинге
занимает слишком большое время, рекомендуется пообщаться со
службой поддержки на предмет подключения описаных выше функций. К
примеру, функция <strong>gmp_powm</strong> работает в десятки раз быстрее
функции <strong>bcpowmod</strong>.</p><br />
<br />
<br />
<br />
<br />
<hr noshade="noshade" size="1" />
<div align="center">
© 2006-2015 Copyright VMProtect Software
</div>
</body>
</html>

View File

@@ -0,0 +1,260 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="Stylesheet" type="text/css" href=
"../../default.css" />
<meta http-equiv="Content-Type" content=
"text/html; charset=utf-8" />
<title>Формат серийного номера</title>
<style type="text/css">
/*<![CDATA[*/
th {text-align:left;}
table {border-collapse:collapse; margin-top: 4px;}
td,th {border: 1px solid #B0B0B0; padding-left:10;padding-right:10;}
/*]]>*/
</style>
</head>
<body>
<h1>Формат серийного номера</h1>
<h3>Структура серийного номера</h3>
<p>Серийный номер состоит из блоков. Каждый блок
начинается с байта идентификатора, который определяет его
содержимое и, возможно, длину. Последним всегда идет блок с
идентификатором 255, содержащий контрольную сумму всего серийного
номера, за исключением последнего блока.</p>
<p>В зависимости от типа блока, он может иметь фиксированную или
переменную длину. Во втором случае обычно длина идет в следующем
за идентификатором блока байте. Подробно формат каждого блока
описан ниже.</p>
<h3>Формат блоков</h3>
<table border="1" cellspacing="0" cellpadding="2">
<tr>
<th>ID</th>
<th>Название</th>
<th>Размер (байт)</th>
<th>Описание</th>
<th>Пример</th>
</tr>
<tr>
<td>0x01</td>
<td>Версия</td>
<td>1</td>
<td>Блок содержит версию серийного номера размером в 1 байт.
Версия должна быть равна 1.</td>
<td>01 01</td>
</tr>
<tr>
<td>0x02</td>
<td>Имя пользователя</td>
<td>1 + N</td>
<td>Блок содержит имя пользователя в кодировке UTF-8. Перед
именем пользователя располагается 1 байт длины имени. Таким
образом, длина имени пользователя не может превышать 255
байт. Завершающий 0 не требуется.</td>
<td>02 04 4A 5F 48 4E</td>
</tr>
<tr>
<td>0x03</td>
<td>E-Mail</td>
<td>1 + N</td>
<td>Блок содержит e-mail пользователя в кодировке UTF-8.
Перед e-mail'ом располагается 1 байт длины. Таким образом,
длина e-mail не может превышать 255 байт. Завершающий 0 не
требуется.</td>
<td>03 07 61 40 62 2E 63 6F 6D</td>
</tr>
<tr>
<td>0x04</td>
<td>Идентификатор оборудования</td>
<td>1 + N</td>
<td>Блок содержит идентификатор оборудования, возвращенный
функцией <strong>VMProtectGetCurrentHWID()</strong>. Функция
возвращает строку в кодировке base-64, в серийный номер
помещается раскодированный вариант строки. Длина блока данных
должна быть кратна 4. Перед блоком данных располагается байт
длины. Максимальная длина блока - 32 байта.</td>
<td>04 08 E1 E2 E3 E4 A1 A2 A3 A4</td>
</tr>
<tr>
<td>0x05</td>
<td>Дата окончания лицензии</td>
<td>4</td>
<td>Блок содержит дату окончания действия серийного номера.
Формат хранения даты описан ниже.</td>
<td>05 01 0A 07 DA</td>
</tr>
<tr>
<td>0x06</td>
<td>Максимальное время работы</td>
<td>1</td>
<td>Блок содержит 1 байт с временем работы программы в
минутах. Максимальное время работы, таким образом, может
составлять 255 минут.</td>
<td>06 05</td>
</tr>
<tr>
<td>0x07</td>
<td>Код продукта</td>
<td>8</td>
<td>Блок содержит код продукта - 8 байт, которые создаются
VMProtect и выгружаются при экспорте параметров продукта.
Выгружаются они в кодировке base-64, перед помещением в
серийный номер строку необходимо преобразовать в массив байт.
Размер массива должен быть строго равен 8 байтам.
<strong>Этот блок является обязательным, без него защищенная
программа будет работать некорректно!</strong></td>
<td>07 01 02 03 04 05 06 07 08</td>
</tr>
<tr>
<td>0x08</td>
<td>Данные пользователя</td>
<td>1 + N</td>
<td>Блок содержит до 255 байт пользовательских данных.
Система лицензирования не анализирует эти данные и возвращает
их при вызове функции
<strong>VMProtectGetSerialNumberData()</strong>. Перед блоком
данных располагается байт, содержащий размер пользовательских
данных.</td>
<td>08 05 01 02 03 04 05</td>
</tr>
<tr>
<td>0x09</td>
<td>Максимальная дата сборки</td>
<td>4</td>
<td>Блок содержит максимальную дату сборки приложения, с
которым будет работать этот серийный номер. Формат хранения
даты описан ниже.</td>
<td>09 01 0A 07 DA</td>
</tr>
<tr>
<td>0xFF</td>
<td>Контрольная сумма</td>
<td>4</td>
<td>Блок содержит контрольную сумму серийного номера. Блок
располагается последним и контрольная сумма считается для
всех блоков, идущих перед этим. Подробнее о расчете
контрольной суммы написано ниже.</td>
<td>FF 01 02 03 04</td>
</tr>
</table>
<h3>Формат хранения даты</h3>
<p>Даты в серийном номере хранятся в виде двойного слова -
0xYYYYMMDD. Старшее слово содержит год, а младшее - день и месяц.
Байты расположены в порядке Little Endian - от младшего к
старшему. В случае, если имеется указатель на первый байт записи,
то дата может быть считана и записана следующим кодом:</p>
<pre class="code"><strong>byte *</strong>pDate = 0xNNNNNN; // адрес даты
<strong>*</strong>(<strong>DWORD *</strong>)pDate = (2010 &lt;&lt; 16) | (10 &lt;&lt; 8) | 1; // October 1, 2010
<strong>DWORD</strong> dwExp = *(<strong>DWORD *</strong>)pDate;
</pre>
<h3>Подсчет контрольной суммы</h3>
<p>Контрольная сумма серийного номера вычисляется при помощи
алгоритма хеширования SHA-1. Результатом работы алгоритма
являются пять 32-битных слов. Первое слово используется в
качестве контрольной суммы серийного номера. <strong>Обратите
внимание:</strong> слово записывается в формате Little Endian (от
младшего байта к старшему). В строковом представлении хеша SHA-1
используется формат Big Endian - числа записываются от старшего
байта к младшему, поэтому если SHA-1 генерируется функцией,
которая возвращает строку (как в PHP), то первые четыре байта
хеша необходимо предварительно
развернуть.</p>
<h3>Дополнительная информация</h3>
<p>Блоки с номерами, отличными от описанных выше, игнорируются
системой лицензирования. Возможно появление новых блоков в
будущих версиях. <strong>Не рекомендуется создавать свои блоки,
используя свободные идентификаторы!</strong> Во-первых, это может
привести к неработоспособности ключа с новыми версиями системы
лицензирования. А во-вторых, защищаемая программа все равно не
сможет прочитать значения этих блоков. Для хранения какой-либо
информации в ключе нужно использовать поле <strong>User
Data</strong>, которое предназначено именно для этого.</p>
<p>Серийный номер не содержит "соли" (SALT) - случайной
информации, предназначенной для того, чтобы ключи для одних и тех
же данных получались разными. Эта задача возлагается на алгоритм
шифрования. Если необходимы отличия на уровне серийных номеров,
скажем для серии ключей, проданных в организацию, можно записать
номер ключа строкой в поле имени пользователя (ООО "Компания",
ключ 1 из 10) или занести эту информацию в поле <strong>User
Data</strong> в любом удобном для разработчика формате.</p><br />
<br />
<br />
<br />
<br />
<hr noshade="noshade" size="1" />
<div align="center">
© 2006-2015 Copyright VMProtect Software
</div>
</body>
</html>