Общие рекомендации по защите программ от взлома

Issues related to VMProtect
Post Reply
Admin
Site Admin
Posts: 2566
Joined: Mon Aug 21, 2006 8:19 pm
Location: Russia, E-burg
Contact:

Общие рекомендации по защите программ от взлома

Post by Admin »

Вызов процедуры регистрации

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

Как делают большинство программистов:

Code: Select all

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;
При таком подходе к регистрации вашей программы крякер не будет разбираться с вашем алгоритмом проверки регистрационного кода (какой бы сложный он не был), а просто изменит код в самом начале CheckRegistration, который всегда будет возвращать True.

Code: Select all

function CheckRegistration(const RegNumber: String): Boolean;
begin
  Result:=True;
  exit;
  ...
end;
Рекомендуется "примешивать" логику работы программы в саму проверку регистрационного кода (показан простейший пример, конечная реализация зависит только от фантазии самого разработчика):

Code: Select all

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.
Admin
Site Admin
Posts: 2566
Joined: Mon Aug 21, 2006 8:19 pm
Location: Russia, E-burg
Contact:

Post by Admin »

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

Очень часто программисты допускают грубейшие ошибки при реализации самой проверки правильности регистрационного ключа:

Code: Select all

var ValidRegNumber: String;
...
function CheckRegistration(const RegNumber: String): Boolean;
begin
  if RegNumber=ValidRegNumber then 
   Result:=True
  else 
   Result:=False;
end;
Взломщики не составит труда в процессе трассировки этого участка кода получить значение валидного ключа (просто подсмотрев значение параметров в функции сравнения строк). Поэтому рекомендуется для сравнения использовать хеши ключей:

Code: Select all

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<>0 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.
Суть данного метода состоит в том, что данная хеш функция необратима и по результатам проверки хешей ключей невозможно определить какой должен быть правильный ключ. Взломщику придется потратить гораздо больше времени на нахождение правильного регистрационного ключа, исследуя гораздо больше участков вашей программы, а не только саму проверку правильности ключа.
Admin
Site Admin
Posts: 2566
Joined: Mon Aug 21, 2006 8:19 pm
Location: Russia, E-burg
Contact:

Post by Admin »

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

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

Code: Select all

var IsRegistered: Boolean;
...
procedure TForm1.Button1Click(Sender: TObject);
begin
...
  if not IsRegistered then
   IsRegistered:=CheckRegistration(RegNumber);
  if  not IsRegistered then
   exit;
...
end;
В данном случае перед проверкой проверяется глобальная переменная, хранящая результат проверки, которая может использоваться в различных частях программы.
Найти эту глобальную переменную не составит большого труда (просто просмотрев 2 сегмента данных на изменения ДО и ПОСЛЕ регистрации) - по такому принципу например работает всем известная программа ArtMoney. Рекомендуется все результаты проверок, отвечающих за регистрацию программы, хранить не в статической памяти, а в динамической. В случае хранения глобальных переменных в динамической памяти сканирование секций данных не предмет изменения ячеек памяти ДО и ПОСЛЕ регистрации ничего не даст крякеру.
Простейший пример хранения результата в динамически выделяемой памяти:

Code: Select all

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);
Игорь
Posts: 8
Joined: Fri May 18, 2012 9:22 am

Re: Общие рекомендации по защите программ от взлома

Post by Игорь »

Приветствую

Правильно ли я делаю что бы защитить dll?

В свойствах проекта выставил
Linker/Debugging/Generate Map File = Yes
Linker/Debugging/Generate Debug Info = No

Code: Select all

const char *ValidSerialNumber;

extern "C" __declspec(dllexport)
void __cdecl SetSerialNumber(PCHAR PO)
{    
	VMProtectBeginUltra("SetSerialNumber Marker");		
	ValidSerialNumber = PO;
	VMProtectEnd();
}

extern "C" __declspec(dllexport)
BOOL __cdecl SomeFunction(PVOID *P0, PVOID P1)
{
	VMProtectBeginUltra("SomeFunction Marker");
	//Проверяем серийный номер
	int Result = VMProtectSetSerialNumber(ValidSerialNumber);

	if (Result)
	{	
		return FALSE;
	}
	
	BOOL Status = SomeOtherFunction(P0, P1);

	VMProtectEnd();

	return Status;
}
При запуске программы вызывается SetSerialNumber передавая в dll серийный номер

Code: Select all

SetSerialNumber('jIFSY26szH49ZRupFk/HIb...
дальше в процессе работы будет постоянно вызываться SomeFunction

Что нужно сделать еще что бы максимально защитить dll?
Admin
Site Admin
Posts: 2566
Joined: Mon Aug 21, 2006 8:19 pm
Location: Russia, E-burg
Contact:

Re: Общие рекомендации по защите программ от взлома

Post by Admin »

1. Совершенно непонятно зачем вы включили MAP файл но при этом используете маркеры.
2. Зачем дергать каждый раз VMProtectSetSerialNumber (его нужно вызывать всего один раз) есть для этого есть VMProtectGetSerialNumberState. Соответственно вот эта штука ValidSerialNumber = PO; будет не нужна.
3. SomeOtherFunction можно привязать к ключу.
Игорь
Posts: 8
Joined: Fri May 18, 2012 9:22 am

Re: Общие рекомендации по защите программ от взлома

Post by Игорь »

Admin wrote:1. Совершенно непонятно зачем вы включили MAP файл но при этом используете маркеры.
2. Зачем дергать каждый раз VMProtectSetSerialNumber (его нужно вызывать всего один раз) есть для этого есть VMProtectGetSerialNumberState. Соответственно вот эта штука ValidSerialNumber = PO; будет не нужна.
3. SomeOtherFunction можно привязать к ключу.
Так правильно?

Code: Select all

extern "C" __declspec(dllexport)
void __cdecl SetSerialNumber(PCHAR PO)
{    		
	VMProtectSetSerialNumber(PO);
}

extern "C" __declspec(dllexport)
BOOL __cdecl SomeFunction(PVOID *P0, PVOID P1)
{
	int Result = VMProtectGetSerialNumberState();

	if (Result)
	{	
		return FALSE;
	}
	
	BOOL Status = SomeOtherFunction(P0, P1);

	return Status;
}
В VMProtect добавил две функции SetSerialNumber и SomeFunction в список

Маркеры используются для защиты отдельных участков кода, иcпользуя MAP файл мы защищаем всю функцию?
Admin
Site Admin
Posts: 2566
Joined: Mon Aug 21, 2006 8:19 pm
Location: Russia, E-burg
Contact:

Re: Общие рекомендации по защите программ от взлома

Post by Admin »

Ну я думаю снаружи ДЛЛ-ки будет интересно узнать результат VMProtectSetSerialNumber (как минимум можно будет показать сообщение о том, что серийник невалидный):

Code: Select all

extern "C" __declspec(dllexport)
int __cdecl SetSerialNumber(PCHAR PO)
{          
   return VMProtectSetSerialNumber(PO);
}
А также добавил бы SomeOtherFunction в проект и поставил бы у неё привязку к ключу.
Маркеры используются для защиты отдельных участков кода, иcпользуя MAP файл мы защищаем всю функцию?
Да, все правильно.
Игорь
Posts: 8
Joined: Fri May 18, 2012 9:22 am

Re: Общие рекомендации по защите программ от взлома

Post by Игорь »

Admin wrote:А также добавил бы SomeOtherFunction в проект и поставил бы у неё привязку к ключу.
Я поставил привязку к ключу, при неверном серийнике появляется сообщение "This code requires valid serial number to run", а менять текст сообщения можно?

В опциях->Сообщения->Необходим серийный номер меняю на свой текст, но все равно показывается "This code requires valid serial to run"
Admin
Site Admin
Posts: 2566
Joined: Mon Aug 21, 2006 8:19 pm
Location: Russia, E-burg
Contact:

Re: Общие рекомендации по защите программ от взлома

Post by Admin »

Я поставил привязку к ключу, при неверном серийнике появляется сообщение
Ну видимо привязали к ключу не ту функцию.

Все сообщения можно изменить в опциях.
postgres
Posts: 16
Joined: Tue Feb 03, 2015 4:09 am

Re: Общие рекомендации по защите программ от взлома

Post by postgres »

Можете выложить проект для дельфи с кодом рекомендаций?
Post Reply