Search code examples
assemblyx86interruptbootloaderbios

BIOS interrupt replaced with far-call NOT working


Important Note: Code in this question can render media unbootable!!

So I was trying for my stack to never overflow, but here I am with a question.

I tried to do a far call to the interrupt vector table address for INT 0x13 (in my case 0000f000 at 0x4C) after I pushed flags, from a bootloader. Int 0x13 (that writes to disk 200h starting with 0x0 address) didn't work. This makes no sense as wiki says interrupts are interchangeable in real mode with far calls preceded by a flags push: https://en.wikipedia.org/wiki/INT_(x86_instruction) . So it's crucial for this to work. Just in case, I tried the call with 4 variations of the address (F0 in every byte), to no avail.

[bits 16]
[org 0x7c00]
xor ax, ax
cli
mov es, ax
mov al, 0x01
mov bx, 0x7c00
mov cx, 0x0004
mov dl, 0x80
xor dh, dh
mov ah, 0x03
pushf
call 0xf000:0x0000
;int 0x13
times 510 - ($ - $$) db 0
dw 0xaa55

I used this to write IVT to sector 4 of the same drive this boatloader boots from:

[bits 16]
[org 0x7c00]
xor ax, ax
cli
mov es, ax
mov al, 0x01
mov bx, 0x0
mov cx, 0x0004
mov dl, 0x80
xor dh, dh
mov ah, 0x03
int 0x13
times 510 - ($ - $$) db 0
dw 0xaa55

For Russian speakers I duplicated this question in Russian: БИОС прерывание INT, подмененное на CALL, не срабатывает

As it states in the beginning, please use media without an operating system on it, as this code overwrites the bootloader and a sector behind it - which would make the media unbootable. It would also erase the partition information for any partitions on it. Use empty or unused media.


Solution

  • The following unusual behavior of BIOS has been uncovered that explains why the far call didn't work.

    When you execute the code in question with just INT 0x13, intending to write your interrupt vector table from es:bx of 0x0:0x0 (replace mov bx, 0x7c00 with mov bx, 0x0) to a sector on your drive, you may not get an exact RAM memory imprint. In my case, I was getting zeros, I was getting ASCII, then not legit addresses. Even when I tried far calling to the seemingly correct looking address I saw at some point, it didn't execute the interrupt code, which illustrates a fake IVT I was getting.

    Reading and displaying raw bits to the screen buffer, not using interrupts does seem to display the real IVT, certainly the data in complete discordance with the output of interrupt 13h. I believe the negative ramifications of this are obvious.

    I quickly jotted tonight the awesombler code to print the first 20 entries of the interrupt vector table to screen without using interrupts whatsoever:

    [bits 16]
    [org 0x7C00]
    
    xor ax, ax
    cli
    mov ax, 0xb800
    mov es, ax
    mov di, 0
    mov ax, 0
    mov ds, ax
    mov si, 0x0     ;starting RAM address, 0 for IVT
    mov cx, 0
    mov dl, 20      ;how many interrupt vectors to print (<25 for vga)
    moreints:
    mov bh, 4       ;counter for bytes of each interrupt vector entry to print
    morebytes:      
    mov ah, [ds:si]
    mov bl, 8       ;counter for bits of each byte to print
    morebits:
    shl ah, 1
    mov al, 48
    jnc zero
    mov al, 49
    zero:
    mov [es:di], al
    inc di
    inc di
    dec bl
    jnz morebits
    mov al, 32
    mov [es:di], al
    inc di
    inc di
    inc si
    dec bh
    jnz morebytes
    add cx, 80*2
    mov di, cx
    dec dl
    jnz moreints
    times 510 - ($ - $$) db 0
    dw 0xaa55
    

    Now you can find your exact interrupt routines' addresses and dissasemble them for educational purposes. If you get weird readings it may point to presence of a BIOS rootkit, among other things. You may even find meaningful offsets/segments that read "F0FA" as a phonetic transcript of Vova, which is Vladimir Putin's first name.

    A convenient reminder on how to read ivt addresses: say, you get 0x12345678 as the IVT entry for an interrupt. Your segment and offset will be 0x7856:0x3412.

    And using the address acquired via this method for the far call after pushf will exhibit the expected behavior and invoke the interrupt code. I just tested it, and it works.