VMProtect/help/ru/recommendations.htm

234 lines
11 KiB
HTML
Raw Normal View History

2023-05-14 16:21:09 +03:00
<!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>
</head>
<body>
<h1>Рекомендации по защите</h1>
<p>Программа VMProtect представляет собой надежный инструмент
защиты кода приложения от изучения и взлома. Однако его
эффективное использование возможно лишь в том случае, если
встроенные в приложение защитные механизмы спроектированы по
определенным правилам и не содержат типичных ошибок. Рассмотрим
некоторые моменты, связанные с созданием защитных механизмов
приложения.</p>
<h3>Процедура регистрации</h3>
<p>Типичной ошибкой программистов, разрабатывающих собственную
схему регистрации экземпляра приложения, является вынос процедуры
проверки корректности введенного регистрационного ключа в
отдельную функцию c понятным возвращаемым значением:</p>
<pre class="indent code">function CheckRegistration(const RegNumber: String): Boolean;
begin
if RegNumber='123' then
Result:=True
else
Result:=False;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
...
if not CheckRegistration(RegNumber) then
exit;
Application.CreateForm(TForm2, Form2);
Form2.ShowModal;
...
end;
</pre>
<p>При подобном подходе к проверке регистрационного кода
взломщику нет необходимости вникать в алгоритм проверки ключа, а
будет достаточно изменить код в начале процедуры проверки таким
образом, чтобы она всегда возвращала значение,
соответствующее корректному регистрационному ключу:</p>
<pre class="indent code">function CheckRegistration(const RegNumber: String): Boolean;
begin
Result:=True;
exit;
...
end;
</pre>
<p>Гораздо более эффективным способом является встраивание
проверки корректности регистрационного кода в логику работы
программы таким образом, чтобы невозможно было отделить алгоритм
проверки регистрационного кода от алгоритма работы вызывающей эту
проверку процедуры. Также рекомендуется "примешивать" логику
работы программы в процедуру проверки регистрационного кода так,
чтобы "обход" регистрационной процедуры приводил к ошибкам в
работе приложения. Для примера, приведенного выше, это можно
сделать следующим образом:</p>
<pre class="indent code">function CheckRegistration(const RegNumber: String): Boolean;
begin
if RegNumber='123' then
begin
Application.CreateForm(TForm2, Form2);
Result:=True
end
else
Result:=False;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
...
Form2:=nil;
if not CheckRegistration(RegNumber) then
exit;
Form2.ShowModal;
...
end;
</pre>
<p>При подобной реализации функции CheckRegistration для "обхода"
проверки регистрационного ключа взломщику будет необходимо
досконально разбирать работу всей функции проверки ключа. При
защите данного приложения с помощью VMProtect рекомендуется
выполнить виртуализацию не только функции CheckRegistration, но и
процедуры TForm1.Button1Click. Для еще большего усложнения
процесса взлома приложения можно воспользоваться <a href="project_functions.htm#CompilationTypes">режимом защиты
"Ультра"</a>, сочетающим мутацию кода приложения с его последующей
виртуализацией.</p>
<h3>Проверка регистрационных ключей</h3>
<p>Очень часто программисты допускают грубейшие ошибки при
реализации самой проверки правильности регистрационного ключа,
производя сравнение введенного ключа с его корректным значением.
При подобной реализации взломщик легко сможет подобрать
корректное значение ключа, просмотрев в процессе трассировки
аргументы, с которыми вызывается функция сравнения строк:</p>
<pre class="indent code">var ValidRegNumber: String;
...
function CheckRegistration(const RegNumber: String): Boolean;
begin
if RegNumber=ValidRegNumber then
Result:=True
else
Result:=False;
end;
</pre>
<p>Для исключения подобной ситуации при сравнении введенного
значения ключа с его допустимым значением рекомендуется
использовать их хеши, а не реальные значения. По своей сути
хеш-функция необратима, а значит, по результатам проверки хешей
невозможно определить значение допустимого ключа. При взломе
приложения будет необходимо потратить гораздо больше времени на
изучение программы, так как придется исследовать гораздо больше
участков кода, а не только процедуру проверки корректности
введенного ключа:</p>
<pre class="indent code">var
HashOfValidRegNumber: Longint;
...
// Пример использования обобщенного алгоритма хеширования Питера Вейнбергера (PJW)
function HashPJW(const Value: String): Longint;
var I:Integer;
G:Longint;
begin
Result:=0;
for I:=1 to Length(Value) do
begin
Result:=(Result shl 4)+Ord(Value[I]);
G:=Result and $F0000000;
if G&lt;&amp;gt0 then
Result:=(Result xor (G shr 24)) xor G;
end;
end;
function CheckRegistration(const RegNumber: String): Boolean;
begin
if HashPJW(RegNumber)=HashOfValidRegNumber then
Result:=True
else
Result:=False;
end;
...
initialization
HashOfValidRegNumber:=HashPJW(ValidRegNumber);
end.
</pre>
<p>При защите программы с помощью VMProtect целесообразно
защитить функции HashPJW и CheckRegistration для усложнения
работы взломщика.</p>
<h3>Хранение результата проверки регистрации</h3>
<p>Как правило, программисты, затратившие много времени на
реализацию самой процедуры регистрации, не уделяют достаточного
внимания защите самого результата регистрации программы. В
приведенном ниже примере перед вызовом функции проверки
корректности регистрационного номера контролируется состояние
глобальной переменной, хранящей результат проверки регистрации.
Для взломщика поиск глобальной переменной не представляет
трудностей - достаточно будет сравнить сегменты данных ДО и ПОСЛЕ
регистрации. Кстати, аналогичный принцип лежит в основе работы
известной программы ArtMoney.</p>
<pre class="indent code">var IsRegistered: Boolean;
...
procedure TForm1.Button1Click(Sender: TObject);
begin
...
if not IsRegistered then
IsRegistered:=CheckRegistration(RegNumber);
if not IsRegistered then
exit;
...
end;
</pre>
<p>Для исключения подобной ситуации рекомендуется результаты всех
проверок, отвечающих за регистрацию программы, хранить в
динамической памяти, так как в этом случае сканирование секций
данных на предмет изменения ячеек памяти ДО и ПОСЛЕ регистрации
окажется бесполезным. Простейший пример, демонстрирующий хранение
результата в динамически выделяемой памяти, приведен ниже.</p>
<pre class="indent code">type PBoolean = ^Boolean;
var IsRegistered: PBoolean;
...
procedure TForm1.Button1Click(Sender: TObject);
begin
...
if not IsRegistered^ then
IsRegistered^:=CheckRegistration(RegNumber);
if not IsRegistered^ then
exit;
...
end;
...
initialization
New(IsRegistered);
</pre>
<p>Выше приведены простейшие примеры реализации встроенных в
приложение механизмов защиты. Варианты реальной реализации
процедуры регистрации, функции проверки регистрационного ключа
или организации хранения результата проверки регистрационного
ключа ограничены только фантазией разработчика. Однако в любом
случае при разработке механизмов защиты приложения следует знать
о возможных ошибках и не повторять их.</p><br />
<br />
<br />
<br />
<br />
<hr noshade="noshade" size="1" />
<div align="center">
© 2006-2015 Copyright VMProtect Software
</div>
</body>
</html>