以下源文件分别组装(到原始二进制文件中)并分别加载到虚拟软盘的扇区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中),但这些黑客攻击时似乎都不可行它涉及创建有用的操作系统。如何避免这种地址硬编码?
答案 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