;This is a simple boot sector that will load either MS-DOS or PC-DOS. It is not
;self-reproducing, 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 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 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                   ;This is needed because the jump will get coded as 2 bytes

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 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

;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                    ;and transfer control to the first system file


;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


SYSFILE_1:      DB      'IBMBIO  COM'                   ;PC DOS System file
SYSFILE_2:      DB      'IO      SYS'                   ;MS DOS System file

                ORG     7DFDH

DRIVE:          DB      0                               ;Drive number, used in disk reads, etc.
BOOT_ID:        DW      0AA55H                          ;Boot sector ID word


MAIN            ENDS


                END START
