Page 1 of 1

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

Posted: Thu May 10, 2007 4:33 am
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.

Posted: Sun Sep 16, 2007 1:03 pm
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.
Суть данного метода состоит в том, что данная хеш функция необратима и по результатам проверки хешей ключей невозможно определить какой должен быть правильный ключ. Взломщику придется потратить гораздо больше времени на нахождение правильного регистрационного ключа, исследуя гораздо больше участков вашей программы, а не только саму проверку правильности ключа.

Posted: Sun Sep 16, 2007 1:22 pm
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);

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

Posted: Sun Jul 29, 2012 7:25 pm
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?

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

Posted: Sun Jul 29, 2012 8:25 pm
by Admin
1. Совершенно непонятно зачем вы включили MAP файл но при этом используете маркеры.
2. Зачем дергать каждый раз VMProtectSetSerialNumber (его нужно вызывать всего один раз) есть для этого есть VMProtectGetSerialNumberState. Соответственно вот эта штука ValidSerialNumber = PO; будет не нужна.
3. SomeOtherFunction можно привязать к ключу.

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

Posted: Mon Jul 30, 2012 6:38 am
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 файл мы защищаем всю функцию?

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

Posted: Mon Jul 30, 2012 8:44 am
by Admin
Ну я думаю снаружи ДЛЛ-ки будет интересно узнать результат VMProtectSetSerialNumber (как минимум можно будет показать сообщение о том, что серийник невалидный):

Code: Select all

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

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

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

В опциях->Сообщения->Необходим серийный номер меняю на свой текст, но все равно показывается "This code requires valid serial to run"

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

Posted: Mon Jul 30, 2012 1:38 pm
by Admin
Я поставил привязку к ключу, при неверном серийнике появляется сообщение
Ну видимо привязали к ключу не ту функцию.

Все сообщения можно изменить в опциях.

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

Posted: Thu Jun 13, 2019 3:37 pm
by postgres
Можете выложить проект для дельфи с кодом рекомендаций?