在16位实模式下定位独立代码,启动加载/软盘读取

时间:2016-08-08 21:53:52

标签: assembly nasm bootloader 16-bit x86-16

以下源文件分别组装(到原始二进制文件中)并分别加载到虚拟软盘的扇区1和2上。然后,此软盘将用作qemu-system-i386 VM的引导介质。

“bootloader”从软盘的扇区2读取“第一个程序”,然后跳转到包含刚刚读取的代码的内存。以下代码按预期工作(即打印“第一个程序”欢迎消息),但我必须在“第一个程序”的源代码中指定// call it once $result = time_elapsed_string($ptime); // then use: $result['date']; $result['time']; $result['difference']; (通过在十六进制编辑器中检查引导加载程序的代码获得) 。 0x001E是ORG 0x001E缓冲区的偏移量,它保存从软盘读取的代码。

“引导程序”:

temp

“第一个程序”:

BITS 16

bootloader_main:
    mov bx, 0x07C0      ; Set data segment to bootloader's default segment
    mov ds, bx


    mov ah, 0x02        ; BIOS int13h "read sector" function
    mov al, 1           ; Number of sectors to read
    mov cl, 2           ; Sector to read
    mov ch, 0           ; Cylinder/track
    mov dh, 0           ; Head
    mov dl, 0           ; Disk number (here, the floppy disk)
    mov bx, 0x07C0      ; Segment containing the destination buffer
    mov es, bx
    mov bx, temp        ; Destination buffer offset
    int 0x13

    jmp temp

    ret
;end bootloader_main




temp: times 60 db 17

times 510-($-$$) db 0       ; Pad rest of sector and add bootloader   
dw 0xAA55                      signature

或者,我可以使用BITS 16 ORG 0x001E ; Assume that this code will be located 0x001E bytes after start of bootloader (in RAM) mov bx, string ; Print a welcome string mov ah, 0x0E print_loop: mov al, byte [bx] int 0x10 inc bx cmp byte [bx], 0 jne print_loop ;end print_loop string: db "This is the first program.", 0 ORG 0x200作为缓冲区而不是0x200(即在引导加载程序之后将程序加载到RAM中),但这些黑客攻击时似乎都不可行它涉及创建有用的操作系统。如何避免这种地址硬编码?

2 个答案:

答案 0 :(得分:1)

您可以使用细分来避免地址的硬编码。加载"第一个程序"在地址为16的倍数并加载DS与相应的段(地址/ 16),然后远程跳转到segment:0,其中segment是您加载程序的地方。在加载的程序中使用ORG 0

例如:

BITS 16

bootloader_main:
    mov ax, 0x07C0      ; Set data segment to bootloader's default segment
    mov ds, ax

    mov ah, 0x02        ; BIOS int13h "read sector" function
    mov al, 1           ; Number of sectors to read
    mov cl, 2           ; Sector to read
    mov ch, 0           ; Cylinder/track
    mov dh, 0           ; Head
    mov bx, program_seg ; load program at program_seg:0
    mov es, bx
    xor bx, bx
    int 0x13

    mov ax, program_seg
    mov ds, ax
    mov ss, ax          ; set stack to end of program_seg
    mov sp, 0
    jmp program_seg:0

bootloader_end:
program_seg equ (bootloader_end - bootloader_main + 0x7c00 + 15) / 16

times 510-($-$$) db 0       ; Pad rest of sector and add bootloader   
dw 0xAA55                   ;   signature
BITS 16
ORG 0

mov bx, string      ; Print a welcome string
mov ah, 0x0E
print_loop:
    mov al, byte [bx]
    int 0x10
    inc bx
    cmp byte [bx], 0
    jne print_loop
;end print_loop


string: db "This is the first program.", 0

我已删除了mov dl, 0指令,因为您不应该对此值进行硬编码。 BIOS将在DL中传递引导设备的驱动器号,因此您无需更改它。

答案 1 :(得分:0)

此示例允许您在两者之间输入任意数量的代码 jmp Load_Buffer& Load_Buffer,只要它不会超过最后4个字节的引导扇区。

<强> BOOTLOADER

    BOOTSEG     equ 0x7c0
    LOW_MEM     equ 18
    DISKIO      equ 19

确定可以使用多少4k页并将其转换为段 地址。就我而言,它是0x8FC0。

xor     cx, cx
mov     cl, 64
int     LOW_MEM             ; Get # of 4k segments to top of memory
sub     ax, cx
shl     ax, 6                       ; 

修改堆栈指针时始终禁用中断。这使得堆栈处于一个非常安全的位置,位于内存最低64k

之上
cli
mov     ss, ax
xor     sp, sp
sti

此时CS可能为0或者可能是0x7c0,具体取决于bios供应商,但这并不重要,因为此时只有ES:BX很重要

mov     ax, Load_Buffer
shr     ax, 4
add     ax, BOOTSEG
mov     es, ax  
mov     ax, 0x201                   ; Read one sector
mov     cx, 2                       ; Starting @ cylinder 0, sector 2
xor     dh, dh                  ; Head 0 and BIOS has already passed device.

这里的另一种选择是在示例中加载带有0x7c0的ES和带有Load_Buffer的BX,但在我们的例子中是ES = BOOTSEG + Load_Buffer / 16.

xor     bx, bx
int     DISKIO                  ; Read sector 2
jmp     Load_Buffer

页面边界上的对齐将保证加载器将始终工作,无论在Load_Buffer上方添加多少,除非它碰巧超过了引导扇区的最后4个字节。对齐只需要在页面边界上,因此16也可以正常工作。

align   32
Load_Buffer:

用NOP的代码运行来填充扇区可能是一个好主意,不存在段错误。

times   508 - ($-$$)    db  144     ; NOP's 0x90
int     25                      ; Incase codes runs away we'll reboot
dw      0xAA55                  ; Boot sig (no required by some emulators)

第一个程序

要使用LODSB,这不是比你的方式更正确,但只使用更少的代码。

mov     si, string
push    es
pop     ds                      ; DS:SI now points to string
mov     ah, 14                  ; TTY output

Loop:
lodsb                           ; Read from DS:SI
test    al, 255             ; Test if these any of these bits are on.
jz      .Done
int     16
jmp     Loop

标签之前的时段只是用于声明本地标签的NASM功能

.Done:
hlt
jmp     $

string  db  'This is the first program', 0