Гошко С.В., Защита исполнимого кода, Argc&Argv, январь 2003, стр.43-48
			     Защита исполнимого кода

Данная статья посвящена защите исполнимого кода от отладки. Для чего эта защита
необходима? Рассмотрим самый распространённый пример. Вы разработчик
программного обеспечения написали какой-то продукт пользующийся спросом на 
рынке, защитили его традиционной комбинацией - имя пользователя, 
идентификационный код. Но уже через неделю - месяц вы будете удивлены 
количеством программ в Интернете, написанных с целью снятия вашей защиты, 
причём довольно успешно работающих. Чтобы такая ситуация с вами не случилась 
необходимо предусмотреть защиту от отладки и дизассемблирования ваших программ,
тогда снятие защиты с вашей программы очень сильно усложнится. 
И цель данной статьи защитить ваши программы от взлома.

В начале мы рассмотрим простейшую атакуемую программу и варианты защиты её
от отладчиков.

			Рассмотрим пример атакуемой программы:

//-----------------------------------------------------------------------------
#include 		// Подключаемые библиотеки
#include 
#include 

bool test(char *name1,char *pass1) // Функция проверки корректности пароля
{
   int a,i,z;
   char str1[300]="";
   char valid_pass[300]="";

   a=strlen(name1);
   for (i=0;i<=a-1;i++)
   {
      z=(int)name1[i];
      z=z+33;
      itoa(z,str1,10);
      strcat(valid_pass,str1);
   }
   if (strcmp(pass1,valid_pass)==0) // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   return true;
   return false;
}
void main()				// Основная функция
{
   char name[1024];
   char pass[1024];

   printf("Enter user name: ");
   scanf("%50s",name);
   printf("Enter registration code: ");
   scanf("%250s",pass);

   if (!test(name,pass))  // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   	{
      	printf("Invalid user name or registration code :( \n");
        exit(0);
   	}
   printf("Registration succesfull ;) \n");

}
//-----------------------------------------------------------------------------						

Данная программа в начале получает от пользователя имя пользователя и 
регистрационный код. Затем в функции test() происходит преобразование имени
пользователя по определённому алгоритму (к значению символа прибавляется 33).
После преобразования получается строка, которая содержит корректный 
регистрационный ключ для данного имени пользователя, в конце функции 
проверяется, соответствует ли введёный пользователем регистрационный код только
что сгенерированному, если да, то функция возвращает 1 иначе 0. От результата
функции зависит финальное сообщение, успешно ли прошла регистрация или нет. 

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

Какие существуют возможности защиты от отладчика? Первый и самый простой
способ использовать предоставленную разработчиками Windows функцию.
В операционных системах Windows, начиная с Windows NT существует функция
IsDebuggerPresent. Данная функция аргументов не получает, а в качестве 
результата возвращает 1, если отладчик обнаружен и 0 в противном случае.

			Использовать её довольно легко:

//-----------------------------------------------------------------------------
        if (!IsDebuggerPresent()) goto no_debugger
        //........................................
no_debugger:
//-----------------------------------------------------------------------------

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

;------------------------------------------------------------------------------
       		mov 	eax,fs:[018h]
       		mov 	eax,[eax+30h]
     		movzx 	eax,byte ptr [eax+02]
		ret
;------------------------------------------------------------------------------

Для нас существенны только первые три, мы можем из них построить свою функцию,
аналогичную IsDebuggerPresent.

				Перейдём к листингу:
							   
//-----------------------------------------------------------------------------
#include 
#include 
#include 
#include 

int debug1()				  // Функция проверки на присутствие
{					  // Отладчика, она возвращает 1 если
	      	asm	mov 	eax,fs:[018h] // отладчик есть и 0, если его нет
       		asm	mov 	eax,[eax+30h]
		asm	movzx 	eax,byte ptr [eax+02]
}
bool test(char *name1,char *pass1)
{
   int a,i,z;
   char str1[300]="";
   char valid_pass[300]="";

   a=strlen(name1);

   for (i=0;i<=a-1;i++)
   {
      z=(int)name1[i];
      z=z+33;
      itoa(z,str1,10);
      strcat(valid_pass,str1);
   }

   if ((strcmp(pass1,valid_pass)==0))
   return true;
   return false;
}
void main()
{
   char name[1024];
   char pass[1024];
   int a;

   printf("Enter user name: ");
   scanf("%50s",name);
   printf("Enter registration code: ");
   scanf("%250s",pass);

   if (debug1()||!test(name,pass))
   	{
      	 printf("Invalid user name or registration code :( \n");
         exit(0);
   	}
   printf("Registration succesfull ;) \n");

}
//-----------------------------------------------------------------------------

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

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

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

Рассмотрим сущность перекрывающегося кода и применим его для защиты 
нашей программы.

				Рассмотрим листинг:
								
;------------------------------------------------------------------------------
		mov	eax,04ebh
		jmp	$-4
next:
;------------------------------------------------------------------------------

Что же делает выделенная восклицательными знаками часть кода?

1) В eax помещается значение 04ebh(это опкод команды jmp $+4)

2) jmp $-4, переходит на значение 04ebh

3) jmp $+4, переходит на метку next

Таким образом, можно строить сколь угодно сложный перекрывающийся код. Ещё в 
перекрывающемся коде есть один большой плюс, с его помощью можно прятать не 
приятные для кодоанализаторов инструкции.

Теперь напишем функцию для нашей программы, которая использует все прелести
перекрывающегося кода.
				Рассмотрим листинг:
								
//-----------------------------------------------------------------------------
int a_dizasm()
{
		asm mov	eax,04ebh
   		asm jmp	$-4
}
//-----------------------------------------------------------------------------

Благодаря применению антиотладочных и антидизассемблерных трюков наша программа
стала гораздо более защищённой. Но всё равно этого недостаточно. Поэтому я
рекомендую каждому программисту разработать свою внешнюю защиту, которая будет
сочетать трюки, охраняющие от отладки и дизассемблирования. А так же 
использовать полное криптование программного кода.

Для демонстрации всех описанных выше возможностей, мной была написана 
следующая программа:

;-----------------------------------------------------------------------------;
;                                                                             ;
;		      -= SECURE CODE  V. 1.3 BY SLON =-                       ;
; (+) AntiDizasm                                                              ;
; (+) AntiDebug                                                               ;
; (+) Crypt 32 bit XOR (basic code)                                           ;
; (+) Armoured by SEH                                                         ;
; (+) Armoured by CRC                                                         ;
; (+) Polymorphizm (PGS 0.4)                                                  ;
; (+) Vampirizm                                                               ;
; (+) Anti Virus Capability                                                   ;
; (+) EPO                                                                     ;
;                                                                             ;
;-----------------------------------------------------------------------------;
;                                                                             ;
;                      Usage: secure.exe [your_program.exe]                   ;
;                                                                             ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;

.386
.model flat

extrn       	ExitProcess:PROC
extrn       	GetFileSize:PROC
extrn 		CreateFileA:PROC
extrn 		CreateFileMappingA:PROC
extrn 		MapViewOfFile:PROC
extrn 		UnmapViewOfFile:PROC
extrn 		CloseHandle:PROC
extrn 		SetFilePointer:PROC
extrn 		SetEndOfFile:PROC
extrn		printf:PROC
extrn		GetTickCount:PROC
extrn		GetCommandLineA:PROC
extrn		Sleep:PROC
extrn		CopyFileA:PROC
extrn		lstrcatA:PROC
extrn		scanf:PROC
extrn		GetComputerNameA:PROC
extrn		WriteFile:PROC
extrn		ReadFile:PROC

.data                                                    
start:
;-----------------------------------------------------------------------------;
		push	offset message2         ; Выводим сообщение об
		call	printf                  ; использовании программы
		pop	eax

		call	commandline             ; Получаем argv[1]

	        call    attach                  ; Усиливаем файл
;-----------------------------------------------------------------------------;
End_it:
		cmp	eax,-1                  ; Если была ошибка
		jne	armoured                ; то

		push	offset message3         ; Выводим сообщение об ошибке
		call	printf                  ; и завершаем программу
		jmp	zakat                   ;
armoured:
		push	offset message4         ; Если всё хорошо, то выводим
		call	printf                  ; другое сообщение и завершаем
zakat:                                          ; программу
		push	0                       ; 
		call	ExitProcess             ; Завершение процесса
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;			     ATTACH SUBROUTINE		      	              ;
;-----------------------------------------------------------------------------;
;			          [ IN ]				      ;
;                                                                             ;
;          		    NO INPUT IN SUBROTINE                             ;
;-----------------------------------------------------------------------------;
;			          [ OUT ]				      ;
;						 			      ;
;               	    NO OUTPUT IN SUBROTINE 	                      ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;

attach:                                            ; Подпрограмма модификации
                                                   ; исполнимого файла
OpenFile:
		push    0
		push    00000080h
		push    3
		push    0
		push    00000001h OR 00000002h
		push    40000000h OR 80000000h
		push    esi
		call	CreateFileA          	   ; Открытие файла 
        	                           	   ; для чтения/записи
		mov 	fHnd,eax  		   ; Сохранение хэндла файла
		cmp 	eax,-1
		je	InfectionError

		push	offset message1
		call	printf
		pop	eax

		call	backup_file

;-----------------------------------------------------------------------------;
		push	0                          ;
		push	fHnd                       ; Получаем размер файла
		call	GetFileSize		   ;

		mov 	filesize,eax 		   ; Сохраняем размер файла
		add 	eax,finish-decrypt+10000h  ; filesize + decryptor
		mov 	memory,eax   		   ; +workspace=memory
;-----------------------------------------------------------------------------;

CreateFileMapping:
						   ; выделяем память
		push 	0			   ; имя файла хэндл = 0
		push 	memory			   ; макс. размер = memory
		push 	0			   ; минимальный размер = 0
		push 	4			   ; доступ чтение/запись
		push 	0			   ; 
		push 	fHnd
		call	CreateFileMappingA
						   ; eax = хэндл

		mov 	mHnd,eax
		or 	eax,eax
		jz 	CloseFile

MapViewOfFile1:
		push 	memory			   ; количество памяти 
						   ; для работы
		push 	0			   ; 
		push 	0			   ; 
		push 	2			   ; Режим записи
		push 	eax			   ; хэндл
		call	MapViewOfFile		   ; Вызываем функцию

		or 	eax,eax
		jz 	CloseMap
		mov 	esi,eax			   ; 
		mov 	mapaddress,esi	           ; Сохраняем базу памяти

DoSomeChecks:
		cmp 	word ptr [esi],'ZM'	   ; Это EXE файл?
		jne 	UnmapView	
		cmp 	word ptr [esi + 38h],'nf'  ; Уже усиленный файл?
		je 	UnmapView

OkGo:
		mov 	ebx,[esi + 3ch]
		cmp 	ebx,200h
		ja 	UnmapView
		add 	ebx,esi
		cmp 	word ptr [ebx],'EP'	   ; Это PE файл ?
		jne 	UnmapView
		
		mov 	PEheader,ebx		   ; сохраняем PE заголовок
		mov 	esi,ebx
		mov 	eax,[esi + 28h]
		mov 	oldip,eax	           ; Сохраняем старую точку 
						   ; входа
		mov 	eax,[esi + 34h]
		mov 	imagebase,eax		   ; Сохраняем
                                                   ; виртуальный адрес 
						   ; начала программы

		call	find_code	           ; Вызов подпрограммы поиска
		                                   ; и усиления кода

		call	epo                        ; Вызываем функцию их 
						   ; подмены
;-----------------------------------------------------------------------------;
                lea	eax,decrypt                ;
                mov	edi,offset buff2           ; Заполняем параметры и
                			           ; вызываем полиморфный
                mov	ecx,finish-decrypt         ; генератор PGS
                call	morph                      ;
		mov	mega_size,ecx
		mov	mega_offset,edi
;-----------------------------------------------------------------------------;

LocateBeginOfLastSection:

		mov 	ebx,[esi + 74h]            ; 	
		shl 	ebx,3			   ; 
		xor 	eax,eax					
		mov 	ax,[esi + 6h]	   	   ; Количество объектов
		dec 	eax			   ; (нам нужен последний-1
		mov 	ecx,28h			   ; заголовок секции)
		mul 	ecx			   ; * размер заголовка
		add 	esi,78h			   ; теперь esi указывает 
						   ; на начало последнего
		add 	esi,ebx			   ; заголовка секции
		add 	esi,eax

ChangeLastSectionHeader:

		or 	[esi + 24h],00000020h or 20000000h or 80000000h 

NewPhysicalSize:
		mov 	eax,[esi+10h]	   	   ; Старый физический 
						   ; размер
		add 	eax,mega_size
		mov 	[esi+10h],eax		   ; Сохраняем его

VirtualSizeCheck:

		mov 	edi,[esi + 8h]		   ; Получаем старый 
		cmp 	eax, edi		   ; виртуальный размер
	    	jge 	NewVirtualSize

VirtualSizeIsVirtual:

		add 	edi,mega_size
		mov 	eax,edi

NewVirtualSize:
		mov 	ecx,PEheader
		mov 	ecx,[ecx + 38h]
		div 	ecx			   ; и выравниваем к
		inc 	eax			   ; секции выравнивания
		mul 	ecx
		mov 	[esi + 8h],eax		   ; Сохраняем новое 
						   ; значение

NewAlignedImageSize:

		mov 	eax,[esi + 0ch]		   ; получаем виртуальное 
						   ; смещение	
		add 	eax,[esi + 8h]		   ; + новый виртуальный 
						   ; размер
		mov 	imagesize,eax		   ; = новый виртуальный 
						   ; размер

NewAlignedFileSize:

		mov 	eax,[esi+10h]		   ; получаем новый 
						   ; физический размер
		add 	eax,[esi + 14h]		   ; добавляем смещение 
						   ; физического
		mov 	filesize,eax		   ; размера = размер файла

CalculateNewIp:

		mov 	eax,[esi+10h]		   ; новый физический 
						   ; размер	
		add 	eax,[esi + 0ch]	           ; + виртуальное смещение
		sub 	eax,mega_size		   ; - размер декриптора
		mov 	newip,eax		   ; новая точка входа

		call	epo2                       ; Вызываем функцию их 
						   ; подмены

CopyDecryptorToEndOfFile:

		mov 	edi,[esi+10h]		   ; Новый физический 
						   ; размер	
		sub 	edi,mega_size	
		add 	edi,mapaddress		   ; mapaddress
		add 	edi,[esi + 14h]		   ; добавляем смещение 
						   ; потоковых данных
		mov	esi,mega_offset
		mov	ecx,mega_size
		cld                                ; Переносим декриптор
		rep 	movsb                      ; в файл

UpdatePEHeaderWithChanges:

		mov 	esi,mapaddress	
		mov 	word ptr [esi + 38h],'nf'  ; Устанавливаем метку 
		mov 	esi,PEheader		   ; изменённости	
		mov 	eax,imagesize		   ; 
		mov 	[esi + 50h],eax		   ; Устанавливаем новый 
                                                   ; виртуальный размер

UnmapView:
		push 	mapaddress 		   ;
		call	UnmapViewOfFile		   ; Закончиваем изменение 
						   ; файла в памяти и ложим
						   ; его обратно

CloseMap:
		push 	mHnd			   ;
		call	CloseHandle		   ; Закрываем хэндл

	  	push 	0
		push 	0
		push 	filesize 		   ;
		push 	fHnd     		   ; Переходим в конец 
						   ; файла
		call	SetFilePointer		   ;

		push 	fHnd			   ;
		call	SetEndOfFile		   ; Устанавливаем символ 
						   ; конца файла

;-----------------------------------------------------------------------------;

CloseFile:
	
		push 	fHnd			   ; 
		call	CloseHandle		   ; Закрываем файл

InfectionError:

		ret                                ; Выходим из процедуры

;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;			     FIND CODE SUBROUTINE	      	              ;
;-----------------------------------------------------------------------------;
;			          [ IN ]				      ;
;                                                                             ;
;          		    NO INPUT IN SUBROTINE                             ;
;-----------------------------------------------------------------------------;
;			          [ OUT ]				      ;
;						 			      ;
;               	    NO OUTPUT IN SUBROTINE 	                      ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
                                                   
find_code:                                         ; Подпрограмма поиска и
						   ; усиления кодовой секции
						   ;
		pusha

		mov	esi,ebx			   ; Теперь esi указывает 
						   ; на PE
		mov	edi,esi			   ;
		mov 	ebx,[esi + 74h]		   ; 	
		shl 	ebx,3			   ; Получаем 
		xor 	eax,eax				
		mov 	ax,[esi + 6h]	           ; Количество объектов

find2:
		mov	esi,edi
		dec	eax
		push	eax
		
		mov 	ecx,28h			   ; 
		mul 	ecx			   ; 
		add 	esi,78h			   ; теперь esi указывает 
						   ; на начало последнего
		add 	esi,ebx			   ; заголовка секции
		add 	esi,eax			   ; 

		mov	eax,oldip		   ; В eax точку входа

		mov	edx,[esi+0ch]	           ; В edx адрес куда будет 
						   ; мапиться
						   ; текущая секция
		cmp	edx,eax			   ; Проверяем
		pop	eax			   ; Вынимаем из стэка eax
		jg	find2			   ; Если больше ищем дальше
		add	edx,[esi+08h]	           ; Добавляем виртуальный 
						   ; размер секции 
							
		cmp	edx,oldip		   ; Проверяем
		jl	find2			   ; Если меньше ищем 
						   ; дальше
		xor	eax,eax

		push	esi
		mov	edx,[esi+0ch]	           ; Далее вычисляем 
		add	edx,imagebase		   ; физическое
		mov	start_code,edx

		add	eax,[esi+14h]	   	   ;
		add	eax,mapaddress	   	   ; И добавляем базу 
						   ; памяти

		mov	crypt_code,eax	   	   ; Сохраняем начало кода

		mov	eax,[esi+10h]	   	   ; Сохраняем размер кода
		mov	size_code,eax        	   ;

        	or 	[esi + 24h],00000020h or 20000000h or 80000000h 
					           ; Меняем аттрибуты 
					           ; кодовой секции
		

		mov	esi,offset decrypt         ; Подсчитываем
		mov	ecx,final-decrypt          ; контрольную сумму
		call	calc_CRC                   ; декриптора

		call	random			   ; Вызываем генератор 
						   ; псевдослучайного числа
						   ; Это число будет 
						   ; использоваться в
						   ; качестве части ключа 
		mov	key,eax                    ; сохраняем часть ключа 
		add	edx,eax                    ; добавляем CRC

		mov	esi,crypt_code             ;
		mov	ecx,size_code              ;
		call	crypt                      ; Криптуем кодовую секцию

		pop	esi

		mov	epo_st,esi		   ; Сохраняем указатель на
		                                   ; заголовок секции

		mov	eax,oldip                  ; Вычисляем расположение
		mov	edx,epo_st                 ; первых пяти байт кода
		sub	eax,[edx+0ch]              ; усиливаемой программы
		add	eax,crypt_code             ;

		mov	z_bytes,eax

		popa
		ret				   ; Возврат из процедуры
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;			     EPO SUBROUTINE	      	                      ;
;-----------------------------------------------------------------------------;
;			          [ IN ]				      ;
;                                                                             ;
;          		    NO INPUT IN SUBROTINE                             ;
;-----------------------------------------------------------------------------;
;			          [ OUT ]				      ;
;						 			      ;
;               	    NO OUTPUT IN SUBROTINE 	                      ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
epo:
		pushad

		mov	esi,z_bytes		   ; Сохраняем
		lea	edi,s_bytes		   ; оригинальные 11
		mov	ecx,11			   ; байт
		rep	movsb			   ;
		                                   ;
		popad

		ret				   ; Возврат из процедуры
;-----------------------------------------------------------------------------;
epo2:
		pushad

		mov	esi,z_bytes		   ; Сохраняем
		                                   ;
                mov	eax,newip                  ;
		add	eax,imagebase

		mov	newip1,eax                 ;
		                                   ; Записываем на их
		mov	edi,esi                    ; место переход на
		lea	esi,lichina                ; наш декриптор
		mov	ecx,end_lichina-lichina    ;
		rep	movsb                      ;

		popad

		ret				   ; Возврат из процедуры

;-----------------------------------------------------------------------------;
lichina:
		push	0

		db	0b8h
newip1		dd	0

		mov	[esp],eax
		ret
end_lichina:
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;		CODE ENCRYPT SUBROUTINE ( 32 BIT XOR ENCRYPTION)              ;
;-----------------------------------------------------------------------------;
;			          [ IN ]				      ;
;                                                                             ;
;			start of code       -> esi                            ;
;                	size of code        -> ecx                            ;
;          		key for encryption  -> edx                            ;
;-----------------------------------------------------------------------------;
;			          [ OUT ]				      ;
;									      ;
;               	   NO OUTPUT IN SUBROTINE                             ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;

crypt:                                             ; Подпрограмма криптования
		pusha				   ; кодовой секции
		mov	edi,esi                    ; 
cr_work:	                                   ;
		lodsd                              ; Шифруем сразу по двойному
		xor	eax,edx                    ; слову 32 битным XOR'ом
		stosd                              ; Всю кодовую секцию
		                                   ;
		sub	ecx,3                      ;
		loop	cr_work	                   ;
		popa
		ret                                ; Возврат из подпрограммы

;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;			   COMMAND LINE SUBROUTINE		      	      ;
;-----------------------------------------------------------------------------;
;			          [ IN ]				      ;
;                                                                             ;
;          		    NO INPUT IN SUBROTINE                             ;
;-----------------------------------------------------------------------------;
;			          [ OUT ]				      ;
;									      ;
;               	       argv[1] -> esi	                              ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;

commandline:
						   ; Процедура получения 
						   ; аргумента
						   ; командной строки 
						   ; (на C - argv[1])

	       call    GetCommandLineA             ; Получаем всю коммандную
	                                           ; строку
	       mov 	esi,eax                    ; Помещаем указатель на
						   ; командную строку в esi
test1:                                             ;
	       lodsb                               ; Проверяем все символы на
	       cmp 	al,20h                     ; равенство пробелу
	       jne 	test1                      ; Если нашли пробел, то 
						   ; теперь esi указывает на
						   ; argv[1]

	       ret

;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;			RANDOM NUMBER GENERATOR SUBROUTINE		      ;
;-----------------------------------------------------------------------------;
;			           [ IN ]				      ;
;                                                                             ;
;          		     NO INPUT IN SUBROTINE                            ;
;-----------------------------------------------------------------------------;
;			           [ OUT ]				      ;
;									      ;
;               	     random number -> eax                             ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;

random:
						   ; Процедура получения 
						   ; псевдослучайного числа

		push edx ecx                                

		db 0fh,31h
						   ; Получаем случайное число
		pop	ecx edx
		ret

;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;		             BACKUP SUBROUTINE                                ;
;-----------------------------------------------------------------------------;
;			          [ IN ]				      ;
;                                                                             ;
;			name of file       -> esi                             ;
;-----------------------------------------------------------------------------;
;			          [ OUT ]				      ;
;									      ;
;               	   NO OUTPUT IN SUBROTINE                             ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
backup_file:                                       ; Подпрограмма сохранения
						   ; изменяемого файла
		push	esi                        ;
		lea	edi,backup_name            ;
finder:                                            ;
		lodsb                              ; Имя изменяемого файла 
		stosb                              ; сохраняем в переменной 
		cmp	al,0                       ; backup_name
		jne	finder                     ;
		dec	edi                        ;

		lea	esi,back                   ; Подготавливаем имя *.bak
		mov	ecx,5                      ; файла
		rep	movsb                      ;
		pop	esi                        ;
		                                   
		push	0                          ; Копируем изменяемый файл
		push	offset backup_name         ; в резервную копию (*.bak)
		push	esi                        ;
		call	CopyFileA                  ;

		ret                                ; Возврат из процедуры
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;			     DECRYPT SUBROUTINE		      	              ;
;-----------------------------------------------------------------------------;
;			          [ IN ]				      ;
;                                                                             ;
;          		    NO INPUT IN SUBROTINE                             ;
;-----------------------------------------------------------------------------;
;			          [ OUT ]				      ;
;						 			      ;
;               	    NO OUTPUT IN SUBROTINE 	                      ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;

decrypt:					   ; Декриптор с антиотладочными
                                                   ; и антидизассемблерными
		                                   ; Трюками
	    	call    setup_SEH       	
		mov     esp,[esp+8]
;-----------------------------------------------------------------------------;
		mov	eax,04ebh                  ; Перекрывающийся код - это
		jmp	$-4                        ; трюк против дизассемблера
f_diz:                                             ;
                                                   ;
		cld
	       	mov 	eax,fs:[018h]              ; А это содержимое функции
	       	mov 	eax,[eax+30h]              ; WIN API 
	     	movzx 	eax,byte ptr [eax+02]      ; IsDebuggerPresent

		cmp	eax,0                      ; Если отладчик есть, то
		jne	final                      ; не расшифровываем код


		push	ss			   ; Трюк против Soft Ice
		pop	ss                         ;

		call	supa_delta                 ; Вычисляем

;-----------------------------------------------------------------------------;
supa_delta:                                        ;
		pop	ebp                        ; дельта смещение
		sub	ebp,offset supa_delta      ;

		mov 	eax,[ebp + oldip]	   ; Восстанавливаем старую 
						   ; точку входа
		add 	eax,[ebp + imagebase]      ; и виртуальный адрес начала

		push	eax                        ; Кладём в стэк адрес
		                                   ; возврата

		call	restore                    ; Восстановление 
		                                   ; оригинальных 11 байт

		lea	esi,[ebp+decrypt]          ; Вычисляем контрольную 
		mov	ecx,final-decrypt          ; контрольную сумму
		call	calc_CRC                   ; декриптора
		                                   
		call	vampirizm		   ; Вызов подпрограммы
mesto:                                             ; вампиризма
		mov	ecx,12345678h		
		nop
					           ; И ей расшифровываем ключ
		add	ecx,edx                    ; для декриптования кодовой
		xchg	ecx,edx                    ; секции

		mov	esi,[ebp+start_code]
		mov	ecx,[ebp+size_code]        ;
		mov	edi,esi                    ;
decrypt1:                                          ; Не посредственно сам
		lodsd                              ; цикл декриптования кодовой
		xor	eax,edx                    ; секции
		stosd                              ;
		                                   ;
		sub	ecx,3                      ;
		loop	decrypt1                   ;
		
		ret

;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;			     SETUP SEH SUBROUTINE	      	              ;
;-----------------------------------------------------------------------------;
;			          [ IN ]				      ;
;                                                                             ;
;          		    NO INPUT IN SUBROTINE                             ;
;-----------------------------------------------------------------------------;
;			          [ OUT ]				      ;
;						 			      ;
;               	    NO OUTPUT IN SUBROTINE 	                      ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
setup_SEH:
	        push    dword ptr fs:[0]      	   ; Push'им оригинальный 
       	        	                   	   ; обработчик SEH
	        mov     fs:[0],esp            	   ; И помещаем новый (который
                	                           ; находится после первого
						   ; call)
                                                   ; Пытаемся писать в ядро (что
                mov     eax,012345678h             ; вызовет исключение)
;-----------------------------------------------------------------------------;
vamp1:                                             ; 
		mov	ecx,[ebp+key]              ; Настоящая команда
;-----------------------------------------------------------------------------;
		xor	ecx,ecx
	        xchg    eax,[ecx]

;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;			     VAMPIRIZM SUBROUTINE	      	              ;
;-----------------------------------------------------------------------------;
;			          [ IN ]				      ;
;                                                                             ;
;          		    NO INPUT IN SUBROTINE                             ;
;-----------------------------------------------------------------------------;
;			          [ OUT ]				      ;
;						 			      ;
;               	    NO OUTPUT IN SUBROTINE 	                      ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
vampirizm:
		lea	edi,[ebp+mesto]            ; Перемещаем настоящую
		lea	esi,[ebp+vamp1]            ; команду на место
		movsd                              ; поддельной
		movsw                              ;
		ret                                ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;			  CALCULATE CRC SUBROUTINE               	      ;
;-----------------------------------------------------------------------------;
;			          [ IN ]				      ;
;                                                                             ;
;			start of code       -> esi                            ;
;                	size of code        -> ecx                            ;
;-----------------------------------------------------------------------------;
;			          [ OUT ]				      ;
;									      ;
;               	         CRC -> edx                                   ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
calc_CRC:                                          ; Подпрограмма вычисления
						   ; контрольной суммы 
						   ; декриптора

		xor	edx,edx                    ; Обнуляем edx 
		xor	eax,eax                    ; и eax
calc1:
		lodsb                              ;
		add	edx,eax                    ; Вычисление CRC
		                                   ;
		loop	calc1                      ;

		ret

;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;			     RESTORE SUBROUTINE	      	                      ;
;-----------------------------------------------------------------------------;
;			          [ IN ]				      ;
;                                                                             ;
;          		    NO INPUT IN SUBROTINE                             ;
;-----------------------------------------------------------------------------;
;			          [ OUT ]				      ;
;						 			      ;
;               	    NO OUTPUT IN SUBROTINE 	                      ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
restore:
						   ; Восстанавливаем
		mov	edi,eax			   ; оригинальные пять
		lea	esi,[ebp+s_bytes]	   ; байт
		mov	ecx,11			   ;
		rep	movsb			   ;
		ret				   ; Возврат из процедуры
;-----------------------------------------------------------------------------;

final:

oldip 		dd     0h		                    
imagebase       dd     0h 
size_code	dd     0h
start_code	dd     0h
key		dd     0h

s_bytes		db     11 dup(0)					
;*****************************************************************************;
finish:

dot_dot		db     ".",0
message1	db     "WORKING ",0
message2	db     79 dup("-")
		db     10
		db     "[ SECURE CODE V. 1.3 BY SL0N ]",10
		db     "Usage: secure.exe [your_program.exe]",10	
		db     79 dup ("-")
		db     10,0
message3	db     10,"[ ERROR - PROGRAM WAS TERMINATED !!! ]",10,0
message4	db     10,"[ PROGRAM WAS ARMOURED SUCCESSFULLY !!! ]",10,0
crypt_code	dd     0h
size2read	dd     0h                          
fHnd           	dd     0h                         
mHnd		dd     0h                         
memory		dd     0h                         
mapaddress 	dd     0h                         
PEheader	dd     0h
filesize       	dd     0h
imagesize	dd     0h
newip		dd     0h
epo_st		dd     0h

name1		db	'hello.exe',0

mega_size	dd     0h
mega_offset	dd     0h

backup_name	db     100 dup(0)
back		db     '.bak',0
z_bytes		dd     0

include		pgs2.asm                           ; Подключается PGS
include         rnd32.asm
include         garbage32.asm

buff2		db     400000 dup(?)               ; И буфер для готового
						   ; декриптора
;-----------------------------------------------------------------------------;
.code
		nop
;-----------------------------------------------------------------------------;
        end     start
;-----------------------------------------------------------------------------;

Теперь рассмотрим как работает данная навесная защита. 
Вот так выглядит программа до изменения её Secure cod'ом:

			|-----------------------------------|
			|	     MZ заголовок           |
			|-----------------------------------|
		|-------|            PE заголовок	    |
		|	|-----------------------------------|									|
		|	|	  !!!!!!!!!!!!!!!!!!! 	    |
		|	|-----------------------------------|
		|------>|  	   Кодовая секция           |
			|-----------------------------------|
			|	   Последняя секция	    |
			|-----------------------------------|
				
После же того, как на программу была установлена защита Secure code, она будет
выглядеть следующим образом:	

			|-----------------------------------|
			|	     MZ заголовок	    |
			|-----------------------------------|
		|-------|   	     PE заголовок           |
		|	|-----------------------------------|									|
		|	|	  !!!!!!!!!!!!!!!!!!!       |
		|	|-----------------------------------|
		|	|           Закриптованная          |<----------|
		|       |           Кодовая секция	    |		|
		|	|-----------------------------------|		|
		|	|	   Последняя секция	    |		|
		|	|----------------|	            |		|
		|------>|    Декриптор 	 |	 	    |		|
			|----|-----------|------------------|		|
			     |------------------------------------------|

Декриптор был усилен всеми описанными выше приёмами, а так же в нём 
использовался один из мощнейших антиотладочных трюков с применением SEH'a.
Данный приём заключается в том, что в качестве SEH обработчика у нас 
устанавливается наш декриптор. Затем мы намерено вызываем ошибку, попыткой 
записи в ядро. При обработке этой ошибки управление передаётся на наш декриптор
при нормальном исполнении программы и завершается в контекстах отладчика и кодо 
эмулятора.

Для использования программы после компоновки следует запустить программу 
следующим образом:

C:\secure\secure.exe your_program.exe

Где your_program.exe - программа, которую вы хотите усилить.
В данной статье были рассмотрены основные приёмы защиты программного кода и их
применение для разработки программы, которая оберегает код от взлома.

;-------------------------------------------------------------[slon (2002)]----;
... Dis Is Cheat of Mind ...