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

Рассмотрим листинг:

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
}

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

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

;------------------------------------
; CODE SECURE V. 1.0 BY SLON
;(+) AntiDizasm
;(+) AntiDebug
;(+) Crypt 8 bit XOR
;(+) Armoured by SEH
;
;------------------------------------
.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 GetTickCount:PROC
extrn printf:PROC
extrn GetCommandLineA:PROC
extrn lstrcpyA:PROC

.data 
start:
;------------------------------------
call delta ;
delta: ; Получаем дельта смещение
pop ebp ;
sub ebp,offset delta ;

push offset message2
call printf

call Work_command

call infect
;------------------------------------
End_it:
push 0
call ExitProcess
;------------------------------------

infect:

lea esi,[ebp + name12] ; esi = имени файла

OpenFile:
push 0
push 00000080h
push 3
push 0
push 00000001h OR 00000002h
push 40000000h OR 80000000h
push esi
call CreateFileA ; Открытие файла 
; для чтения/записи

mov [ebp + offset fHnd],eax ; Сохранение хэндла файла
cmp eax,-1
je InfectionError
;------------------------------------
push 0
push eax
call GetFileSize 

mov [ebp + offset filesize], eax ; Сохраняем размер файла
add eax, finish - start + 1000h ; filesize + decryptor
mov [ebp + offset memory], eax ; +workspace=memory
;------------------------------------

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

mov [ebp + offset mHnd],eax
or eax,eax
jz CloseFile 

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

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

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

OkGo:
mov ebx,dword ptr [esi + 3ch] 
cmp ebx,200h
ja UnmapView
add ebx,esi
cmp word ptr [ebx],'EP' ; Это PE файл ?
jne UnmapView

mov [ebp + offset PEheader],ebx ; сохраняем PE заголовок
mov esi,ebx
mov eax,[esi + 28h]
mov [ebp + offset oldip],eax ; Сохраняем старую точку 
; входа
mov eax,[esi + 34h]
mov [ebp + offset imagebase],eax ; Сохраняем
; виртуальный адрес 
; начала программы
call find_code 

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

LocateBeginOfLastSection:

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

ChangeLastSectionHeader:

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

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

VirtualSizeCheck:

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

VirtualSizeIsVirtual:

add edi,finish-decrypt
mov eax,edi

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

NewAlignedImageSize:

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

NewAlignedFileSize:

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

CalculateNewIp:

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

CopyDecryptorToEndOfFile:

mov edi,dword ptr [esi+10h] ; Новый физический 
; размер 
sub edi,finish-decrypt 
add edi,[ebp + offset mapaddress] ; mapaddress
add edi,[esi + 14h] ; добавляем смещение 
; потоковых данных
lea esi,[ebp + decrypt] ; 
mov ecx,(finish-decrypt)/4 + 4
cld
rep movsd

UpdatePEHeaderWithChanges:

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

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

CloseMap:
push dword ptr [ebp + offset mHnd] ;
call CloseHandle ; Закрываем хэндл

push 0
push 0
push dword ptr [ebp + offset filesize] ;
push dword ptr [ebp + offset fHnd] ; Переходим в конец 
; файла
call SetFilePointer ;

push dword ptr [ebp + offset fHnd] ;
call SetEndOfFile ; Устанавливаем символ 
; конца файла

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

CloseFile:

push dword ptr [ebp + offset fHnd] ; 
call CloseHandle ; Закрываем файл

InfectionError:

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

;------------------------------------
find_code:
pusha

mov esi,ebx ; Теперь esi указывает 
; на PE
mov edi,esi ;
mov ebx,[esi + 74h] ; 
shl ebx,3 ; Получаем 
xor eax,eax 
mov ax,word ptr [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,[ebp+oldip] ; В eax точку входа

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

cmp edx,[ebp+oldip] ; Проверяем
jl find2 ; Если меньше ищем 
; дальше

mov edx,dword ptr[esi+0ch] ; Далее вычисляем 
; физическое
mov eax,[ebp+oldip] ; смещение кода в файле
sub eax,edx ;

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

mov [ebp+start_code],eax ; Сохраняем начало кода

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

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

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

;------------------------------------
crypt: ; Процедура криптования 
; Исполнимого кода
mov esi,[ebp+start_code]
mov edi,esi
xor ecx,ecx

call GetTickCount
and eax,12345678h
mov [ebp+key],al

crypt1:
lodsb
xor al,[ebp+key]
stosb
inc ecx
cmp ecx,[ebp+size_code]
jl crypt1

ret
;------------------------------------
Work_command:
; Процедура получения аргументов
; командной строки 
; (на C это argv[0], argv[1])
call GetCommandLineA 
push eax 
push offset cmd_line 

call lstrcpyA 

lea esi,cmd_line
lea edi,host_name 
test1:
lodsb
cmp al,20h
jne test1
push esi 

mov ecx,esi
sub ecx,offset cmd_line
sub esi,ecx
rep movsb
test2:
lodsb
cmp al,0h
jne test2

pop ecx
lea edi,name12
sub esi,ecx
xchg ecx,esi
rep movsb

ret
;------------------------------------
decrypt: ; Декриптор с антиотладочными
pop ebx ; и антидизассемблерными
call setup_SEH ; Трюками
mov esp,[esp+8]
;------------------------------------
mov eax,04ebh
jmp $-4
f_diz:

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

cmp eax,0
jne final

call supa_delta

;------------------------------------
supa_delta:
pop ebp
sub ebp,offset supa_delta
mov eax,dword ptr [ebp + offset oldip] 

; Восстанавливаем старую точку входа

add eax,dword ptr [ebp + offset imagebase]

; и виртуальный адрес начала

push eax

mov esi,eax
mov edi,esi
xor ecx,ecx
decrypt1:
lodsb
xor al,[ebp+key]
stosb
inc ecx
cmp ecx,[ebp+size_code]
jl decrypt1
final: 
ret
;------------------------------------
setup_SEH:
push dword ptr fs:[0] ; Push'им оригинальный 
; обработчик SEH
mov fs:[0],esp ; И помещаем новый (который
; находится после первого call)
; Пытаемся писать в ядро (что
mov eax,012345678h ; вызовет исключение)
xchg eax,[ebx]

;------------------------------------
oldip dd 0h 

imagebase dd 0h 
size_code dd 0h
key db 0h

finish:

message2 db 79 dup("-")
db 10
db "[ c0de secure program by sl0n]",10
db "Usage: secure.exe [your_program.exe]",10 
db 79 dup ("-")
db 10,0

no_file db "Program can not open this file",10,0
host_name db 100 dup(?)
cmd_line db 200 dup(?)

name12 db 100 dup(?) ; Место для имени файла
start_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
Fhandle dd 0h
;------------------------------------
.code
nop
;------------------------------------
end start
;------------------------------------

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

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

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

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

C:\secure\secure.exe your_program.exe

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

Check Also

Утекшие недавно личные данные граждан Болгарии уже появились на хакерских форумах

Личная информация граждан Болгарии, похищенная ранее на этой неделе, уже просочилась в отк…

Оставить мнение