..--------------------------------------------------------..
..    Пример использования частично шифрованного кода     ..
..                на ассемблере (x86)                     ..
..________________________________________________________..


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

                                               (с) Сунь-цзы, Искусство войны


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

   Другой вариант - шифрование программы. Что, конечно, хорошо, но тоже не
   спасает от обычного дампа.

   Это  если   зашифрован  весь  код.  А  если попробовать защитить только
   определенные функции?  Те, которые  легко обнаружить  антивирусом,  или
   те,  которые  станут  доступны  только в определенном  случае (пример -
   шароварные программы)?  Вот этим мы сегодня и займемся =)

   Итак,   все  примеры   будут  написаны   на  ассемблере, т.к. этот язык
   однозначно рулит во всех направлениях. А для  начала разберемся  в том,
   что,  собственно, хотим получить.

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


.code
 ;----------------------------------------------
 Test1 proc
	 	pusha
	 	mov	eax, 1
	 	mov	ebx, 2
	 	xor	eax, ebx
	 	popa

	 	ret
 Test1_endp:
 endp
 ;----------------------------------------------
 Test2 proc
	 	pusha
	 	mov	eax, 2
	 	mov	ebx, 3
	 	xor	eax, ebx
	 	popa

	 	ret
 Test2_endp:
 endp
 ;----------------------------------------------
 Test3 proc
	 	pusha
	 	mov	eax, 3
	 	mov	ebx, 4
	 	xor	eax, ebx
	 	popa

	 	ret
 Test3_endp:
 endp


   Думаем  дальше. Чтобы знать КАКУЮ функцию нужно шифровать - нужно знать
   ее точный адрес в памяти и  размер.  И  вот  тут  начинают  проявляться
   многие  прелести  ассемблера.  Все  решается  банальным  offset, что не
   скажешь,  например,  о Си или дельфях, где гемороя больше на порядок. В
   Си получить смещение определенной функции в файле не проблема. Проблемы
   начнутся  тогда,  когда  вы  захотите  узнать  ее размер. Но об этом не
   сегодня =)

   Чтобы в любое время  иметь  прямой доступ к полученным нами значениям -
   хорошей идеей будет где-то их хранить.  Я выбрал следующий  вариант (вы
   же можете его улучшить/изменить по своему усмотрению):  в начале секции
   данных я размещал структуру следующего вида:

	Смещение	Размер		Описание
	   0		  2			Количество нужных нам функций
	   2		  4			Адрес первой функции
	   6		  4			Размер первой функции
	   ...       ...
	   n		  4			Адреc n-ой функции
	   n+4		  4			Размер n-ой функции

   О  предназначении  каждого поля я думаю  догадаетесь :) Заполняется эта
   структура  в процессе компиляции. В итоге - имеем доступ ко всей нужной
   нам информации.


.data
 Proc_Entries_Num	dw 3	; кол-во функций

 Proc_Entries:
     Test1_addr	dd offset Test1		            ; адрес первой функции
     Test1_len	dd Test1_endp - offset Test1	; её размер

     Test2_addr	dd offset Test2					; адрес второй функции
     Test2_len	dd Test2_endp - offset Test2	; её размер

     Test3_addr	dd offset Test3					; адрес третьей функции
     Test3_len	dd Test3_endp - offset Test3	; её размер

     curr_proc	    dd ?	; здесь будем хранить адрес текущей функции
     curr_proc_size	dd ?	; здесь - её размер
							; данные параметры необходимы для 
							; корректного выполненния...


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

   Что касается самого  криптования -  вот это и есть  самое интересное :)
   Здесь открывается неограниченное поле для наших возможностей.  Все дело
   в том, что  нам (т.е. вам) ничего не стоит на место процедуры CryptProc
   вставить  свой  алгоритм.  Будь  то  Blowfish, MD5  или же какой-нибудь
   DES  =)  Главное  -  это  позаботиться  о  соответствующей   корректной
   расшифровке. Я же в качестве примера использовал обычный xor.


DecryptProc:
    mov	  esi, eax
    mov	  edi, offset Proc_Entries
    movsx ecx, word ptr Proc_Entries_Num

search:
    mov   edx, dword ptr [edi]
    cmp   edx, esi
    jz    found
    add   edi, 8
    loop  search

    ret

found:
    mov   ecx, dword ptr [edi+4]			;ecx  - size of func
    mov   dword ptr [curr_proc], eax
    mov   dword ptr [curr_proc_size], ecx


decrypt:									; crypto algorithm..
    xor   byte ptr [esi], 66h
    inc   esi
    loop  decrypt

    ret

EncryptProc:
    mov   esi, dword ptr [curr_proc]		; offset of our function
    mov   ecx, dword ptr [curr_proc_size]	; its size

crypt:										; crypto algorithm
    xor   byte ptr [esi], 066h
    inc   esi
    loop  crypt

    ret


   Недостатки данного метода:
   а) Структуру нужно составлять руками
   б) Если  данная  структура  испортится  каким-либо  непонятным образом,
      то  восстановить  первоначальный  код   будет  проблематично.  Чтобы
      решить   данную   проблему,   можно   использовать,  например, некий
      CRC-алгоритм.  Применяемый или к структуре как таковой, или ко всему
      образу в целом.
   в) Проблематичность  использования  в  больших проектах. Хотя, конечно,
      адаптации   и   доводке   данный   спопоб  и  подлежит, но вот чтобы
      реализовать  на  его  основе  что-то  большое  -  в это мне с трудом
      верится :)

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

   В include/crypt_func вы найдете:

   crypt.asm - разбирает структуру в файле и шифрует заданные функции.
   test.asm - тестовая программа.