# Рекомендации по защите

Программа VMProtect представляет собой надежный инструмент защиты кода приложения от изучения и взлома. Однако его эффективное использование возможно лишь в том случае, если встроенные в приложение защитные механизмы спроектированы по определенным правилам и не содержат типичных ошибок. Рассмотрим некоторые моменты, связанные с созданием защитных механизмов приложения.

### Процедура регистрации

Типичной ошибкой программистов, разрабатывающих собственную схему регистрации экземпляра приложения, является вынос процедуры проверки корректности введенного регистрационного ключа в отдельную функцию c понятным возвращаемым значением:

    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;

При подобном подходе к проверке регистрационного кода взломщику нет необходимости вникать в алгоритм проверки ключа, а будет достаточно изменить код в начале процедуры проверки таким образом, чтобы она всегда возвращала значение, соответствующее корректному регистрационному ключу:

    function CheckRegistration(const RegNumber: String): Boolean;
    begin
      Result:=True;
      exit;
      ...
    end;

Гораздо более эффективным способом является встраивание проверки корректности регистрационного кода в логику работы программы таким образом, чтобы невозможно было отделить алгоритм проверки регистрационного кода от алгоритма работы вызывающей эту проверку процедуры. Также рекомендуется "примешивать" логику работы программы в процедуру проверки регистрационного кода так, чтобы "обход" регистрационной процедуры приводил к ошибкам в работе приложения. Для примера, приведенного выше, это можно сделать следующим образом:

    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;

При подобной реализации функции CheckRegistration для "обхода" проверки регистрационного ключа взломщику будет необходимо досконально разбирать работу всей функции проверки ключа. При защите данного приложения с помощью VMProtect рекомендуется выполнить виртуализацию не только функции CheckRegistration, но и процедуры TForm1.Button1Click. Для еще большего усложнения процесса взлома приложения можно воспользоваться режимом защиты "Ультра", сочетающим мутацию кода приложения с его последующей виртуализацией.

### Проверка регистрационных ключей

Очень часто программисты допускают грубейшие ошибки при реализации самой проверки правильности регистрационного ключа, производя сравнение введенного ключа с его корректным значением. При подобной реализации взломщик легко сможет подобрать корректное значение ключа, просмотрев в процессе трассировки аргументы, с которыми вызывается функция сравнения строк:

    var ValidRegNumber: String;
    ...
    function CheckRegistration(const RegNumber: String): Boolean;
    begin
      if RegNumber=ValidRegNumber then
       Result:=True
      else
       Result:=False;
    end;

Для исключения подобной ситуации при сравнении введенного значения ключа с его допустимым значением рекомендуется использовать их хеши, а не реальные значения. По своей сути хеш-функция необратима, а значит, по результатам проверки хешей невозможно определить значение допустимого ключа. При взломе приложения будет необходимо потратить гораздо больше времени на изучение программы, так как придется исследовать гораздо больше участков кода, а не только процедуру проверки корректности введенного ключа:

    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.

При защите программы с помощью VMProtect целесообразно защитить функции HashPJW и CheckRegistration для усложнения работы взломщика.

### Хранение результата проверки регистрации

Как правило, программисты, затратившие много времени на реализацию самой процедуры регистрации, не уделяют достаточного внимания защите самого результата регистрации программы. В приведенном ниже примере перед вызовом функции проверки корректности регистрационного номера контролируется состояние глобальной переменной, хранящей результат проверки регистрации. Для взломщика поиск глобальной переменной не представляет трудностей - достаточно будет сравнить сегменты данных ДО и ПОСЛЕ регистрации. Кстати, аналогичный принцип лежит в основе работы известной программы ArtMoney.

    var IsRegistered: Boolean;
    ...
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      ...
      if not IsRegistered then
       IsRegistered:=CheckRegistration(RegNumber);
      if  not IsRegistered then
       exit;
      ...
    end;

Для исключения подобной ситуации рекомендуется результаты всех проверок, отвечающих за регистрацию программы, хранить в динамической памяти, так как в этом случае сканирование секций данных на предмет изменения ячеек памяти ДО и ПОСЛЕ регистрации окажется бесполезным. Простейший пример, демонстрирующий хранение результата в динамически выделяемой памяти, приведен ниже.

    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);

Выше приведены простейшие примеры реализации встроенных в приложение механизмов защиты. Варианты реальной реализации процедуры регистрации, функции проверки регистрационного ключа или организации хранения результата проверки регистрационного ключа ограничены только фантазией разработчика. Однако в любом случае при разработке механизмов защиты приложения следует знать о возможных ошибках и не повторять их.

© 2006-2015 Copyright VMProtect Software