mirror of
https://github.com/Obfuscator-Collections/VMProtect.git
synced 2024-12-26 16:29:34 +03:00
5ec92ee05e
Version 3.x.x
234 lines
11 KiB
HTML
234 lines
11 KiB
HTML
<!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<&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>
|