VMProtect/help/ru/recommendations.htm

234 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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>