Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MBR Booting from DOS

For a project I would like to invoke the MBR on the first harddisk directly from DOS. I've written a small assembler program that loads the MBR in memory at 0:7c00h an does a far jump to it. I've put my util on a (DOS) bootable floppy. The disk (HD0, 0x80) i'm trying to boot has a TrueCrypt boot loader on it. When I run the tool in this setup, it shows up the TrueCrypt screen, but after entering the password it crashes the system. When I run my little utlility (w00t.com) on a normal WinXP machine it seems to crash immediately.

Apparently I'm forgetting some crucial stuff the BIOS normally does, my guess is it's something trivial. Can someone with better bare-metal DOS and BIOS experience help me out?

Heres my code:

.MODEL tiny
.386
_TEXT SEGMENT USE16

INCLUDE BootDefs.i

ORG 100h

start:
    ; http://vxheavens.com/lib/vbw05.html
    ; Before DOS has booted the BIOS stores the amount of usable lower memory 
    ; in a word located at 0:413h in memory. We going to erase this value because
    ; we have booted dos before loading the bootsector, and dos is fat (and ugly).

    ; fake free memory  
    ;push ds
    ;push   0
    ;pop        ds
    ;mov        ax, TC_BOOT_LOADER_SEGMENT / 1024 * 16 + TC_BOOT_MEMORY_REQUIRED
    ;mov    word ptr ds:[413h], ax  ;ax = memory in K
    ;pop ds
    ;lea si, memory_patched_msg
    ;call print

    ;mov ax, cs
    mov ax, 0
    mov es, ax

    ; read first sector to es:7c00h (== cs:7c00)
    mov  dl, 80h
    mov  cl, 1
    mov  al, 1
    mov  bx, 7c00h ;load sector to es:bx
    call read_sectors

    lea si, mbr_loaded_msg
    call print

    lea si, jmp_to_mbr_msg
    call print

    ;Set BIOS default values in environment
    cli
    mov dl, 80h ;(drive C)
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0ffffh
    sti

    push es
    push 7c00h
    retf            ;Jump to MBR code at 0:7c00h


    ; Print string
print:
    xor bx, bx
    mov ah, 0eh
    cld

@@: lodsb
    test al, al
    jz print_end

    int 10h
    jmp @B

print_end:
    ret

    ; Read sectors of the first cylinder
read_sectors:
    mov ch, 0           ; Cylinder
    mov dh, 0           ; Head
                        ; DL = drive number passed from BIOS
    mov ah, 2
    int 13h
    jnc read_ok

    lea si, disk_error_msg
    call print
read_ok:
    ret

memory_patched_msg      db 'Memory patched', 13, 10, 7, 0
mbr_loaded_msg          db 'MBR loaded', 13, 10, 7, 0
jmp_to_mbr_msg          db 'Jumping to MBR code', 13, 10, 7, 0
disk_error_msg          db 'Disk error', 13, 10, 7, 0

_TEXT ENDS
END start
like image 703
Rogier Avatar asked Apr 14 '10 16:04

Rogier


2 Answers

Edited -- new answer:

OK, seems like I first misunderstood your question. The only further advice I can give is this:

  • Check that you don't load either HIMEM.SYS and/or EMM386.EXE (nor any other memory manager). The CPU must be in Real Mode when the bootloader executes.

  • Have a look at Ralf Brown's interrupt list. If I remember correctly, there's some technical info somewhere in there about the boot process. It might give you a hint.

  • Look at the source code of other loader utilities, e.g. loadlin. (It doesn't do exactly the same thing as your utility, but might give you some insight, nevertheless.)


Previous answer:

Is ORG 100h really the correct thing to do in a boot loader?

I thought this was just relevant for DOS .com executables, because DOS will initialize the first 256 bytes with the Program Segment Prefix (PSP). If you write a boot loader, there is no DOS, and no such thing as a PSP. I'd suppose this has to be ORG 0.

like image 126
stakx - no longer contributing Avatar answered Oct 02 '22 05:10

stakx - no longer contributing


Ok my DOS knowledge is very rusty and I haven't had the time to test/validate my answer, but I guess your problem is as follows:

When booting DOS or any other OS, they will change the interrupt table. DOS will change the interrupt table so - for example - interrupt 20 can be used to send commands to the DOS "kernel". They do this by saving the original interrupt handler, replacing it by their own handler and afterwards, as a default fallback, chaining to the original interrupt handler if they do not know how to handle the interrupt. This way they "add" new functionality to the already existing bios funcitionality, and every program running under DOS can use the system call by just setting some registers and then calling the interrupt.

However, when you boot a new operating system, this new operating system will assume that a) all interrupts are handled by the bios and b) all memory is free/unused unless reported in use by that bios.

So the new os will overwrite the memory currently in use by your old OS and then it will at some point call one of the interrupts, and will execute something in invalid memory, and your computer will crash.

So, reset your interrupt table to the original bios version and you should be fine...

like image 39
Peter Avatar answered Oct 02 '22 06:10

Peter