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

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

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 - программа, которую вы хотите усилить.
В данной статье были рассмотрены основные приёмы защиты программного кода и их
применение для разработки программы, которая оберегает код от взлома.

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии