编写自己的引导加载程序

admin 2024年7月15日16:51:11评论12 views字数 3496阅读11分39秒阅读模式

今天,我想向你展示开始编写自己的操作系统是多么容易。我们从引导加载程序开始。当典型的笔记本电脑启动时,它会从硬盘加载第一个扇区并执行它。这只是 512 个字节,因此您需要将一个例程塞入其中,以加载更多扇区并执行它们。

但是,让我们从小处着手,让我们从一个只输出“HELLO”的引导扇区开始。以下内容已在 Ubuntu Linux 22.04 上进行了测试。

linux

编写自己的引导加载程序

  • 编写自己的引导加载程序

  • 编写自己的引导加载程序

  • windows

  • 编写自己的引导加载程序

  • windows()

  • 编写自己的引导加载程序

  • USB()

  • 编写自己的引导加载程序

  • ()

  • 编写自己的引导加载程序

  • ios

  • 编写自己的引导加载程序

  • windbg

  • 编写自己的引导加载程序

  • ()

  • 编写自己的引导加载程序编写自己的引导加载程序编写自己的引导加载程序

  • 编写自己的引导加载程序

  • 编写自己的引导加载程序

  • 编写自己的引导加载程序

我们从文件开始

hello-1.s:

start:
; this should print H
mov ax, 0xe48
int 0x10
; E
mov ax, 0xe45
int 0x10
; L
mov ax, 0xe4C
int 0x10
; L
mov ax, 0xe4C
int 0x10
; O
mov ax, 0xe4F
int 0x10
.end:
jmp .end

编译它:

nasm -f bin hello-1.s

你得到的是一个 27 字节的机器代码文件 hello-1,它只是转换为机器代码的汇编程序。让我们用 hexdump 看一下:

$ hexdump -C hello-1
00000000 b8 48 0e bb 07 00 cd 10 b8 45 0e cd 10 b8 4c 0e |.H.......E....L.|
00000010 cd 10 b8 4c 0e cd 10 b8 4f 0e cd 10 eb fe |...L....O.....|

很漂亮,不是吗?在左侧,您可以找到十六进制表示法的字节。在右侧,您可以找到写入的字节(如果可写),否则为点。例如,48(十六进制)= 78 是 ASCII 字符“H”,依此类推。

使用 objdump 进行反汇编

下一步:让我们验证代码是否已从汇编程序正确转换为机器语言 — 让我们使用命令 objdump 再次反汇编文件 hello。请注意,当您的典型笔记本电脑启动时,它将处于 16 位模式,即英特尔 8086 处理器模式,因此我们使用以下命令:

$ objdump -b binary -D hello-1 -mi8086

hello: file format binary


Disassembly of section .data:

00000000 <.data>:
0: b8 48 0e mov $0xe48,%ax
3: cd 10 int $0x10
5: b8 45 0e mov $0xe45,%ax
8: cd 10 int $0x10
a: b8 4c 0e mov $0xe4c,%ax
d: cd 10 int $0x10
f: b8 4c 0e mov $0xe4c,%ax
12: cd 10 int $0x10
14: b8 4f 0e mov $0xe4f,%ax
17: cd 10 int $0x10
19: eb fe jmp 0x19

看起来不错,跳转到 .end 标签已被相对跳转到 -1(有符号十六进制表示法中的 fe)所取代。

创建虚拟引导磁盘

下一步是创建名为 bootdisk.img 的虚拟 bootdisk 映像。我们从 1 兆字节的空文件开始:

$ dd if=/dev/zero of=bootdisk.img seek=2K count=1 bs=512

现在将二进制文件 hello-1 放在虚拟引导磁盘的开头,而不截断文件:

$ dd if=hello-1 of=bootdisk.img conv=notrunc

使用十六进制编辑器

现在将魔术签名字节 55 AA 放在引导扇区的末尾。调用十六进制编辑器,例如 okteta:

$ okteta bootdisk.img

并将字节 511 和 512 设置为 55 AA:

编写自己的引导加载程序

Okteta 是一个很酷的 hexeditor 编写自己的引导加载程序

保存并退出 okteta。模拟计算机以从虚拟硬盘启动:

qemu-system-i386 bootdisk.img

结果如下:

编写自己的引导加载程序

从我们的引导扇区:)模拟机器引导

寄存器 AX、AH 和 AL

它比这更容易。hello 有 27 个字节,我们可以减少它。

了解寄存器 AX 由寄存器 AL(A Lower)和 AH(A Higher)组成,其方式为 AX=256*AH+AL。因此,AX 的高值字节是 AH,低值字节是 AL。通过将 AX 设置为 0xe48,我们设置了有效的 AH=0xe 和 AL=ox48。AH=0xe 触发 INT 0x10 输出 AL 的值。 AH 永远不会改变,只需要设置一次。只是 AL 需要为 HELLO 的每个字符进行更改,因此,这是更简单的版本:

hello-2.s:

start:
; this should print H
mov ax, 0xe48
int 0x10
; E
mov al, 0x45
int 0x10
; L
mov al, 0x4C
int 0x10
; L
mov al, 0x4C
int 0x10
; O
mov al, 0x4F
int 0x10
.end:
jmp .end

而现在,在用通常的 nasm -f bin hello.s 编译后,hello 只有 23 个字节。

恭喜,这是我们在汇编语言中的第一次优化。

分段冲突

我们可以在 Linux 下运行这个程序吗?不可以,因为 Linux 具有适当的安全手段,可以防止执行 BIOS 中断,例如 int 0x10。看看会发生什么:

$ nasm -felf64 hello-2.s
$ ld -o hello-2 hello-2.o
ld: warning: cannot find entry symbol _start; defaulting to 0000000000401000
$ ./hello-2
Segmentation fault (core dumped)

“分段错误”或“分段冲突”是指“您的程序位于内存段中,该段没有特权执行此类调用。

高架特权

现在我们已经了解到,在 i8086 模式下作为 bootsector/kernel/运行时,我们已经提升了权限,我们可以使用它来直接与设备交互。当然,现代操作系统不允许您将数据发送到打印机 - 打印机可能已经在打印,打印作业必须由操作系统协调。但作为内核,我们可以做到这一点。为了演示它,让我们听听键盘。要对设备执行 I/O,您需要知道其端口号。对于键盘,其端口为 60(十六进制)。让我们只读取传入的字节,并在它们发生变化时打印。为此,我们使用

hello-3.s:

start:
; this should print H
mov ax, 0xe48
int 0x10
; E
mov al, 0x45
int 0x10
; L
mov al, 0x4C
int 0x10
; L
mov al, 0x4C
int 0x10
; O
mov al, 0x4F
int 0x10
.end:
mov bl,al ;store the old value from the keyboard into bl
in al,0x60 ;read what's coming from the keyboard into al
cmp bl,al ;compare the old and the new value
je .end ;if the old and the new value are equal, jump to .end
int 0x10 ;print what has come from the keyboard, al
jmp .end

编译,构建到引导盘中,并使用以下命令模拟引导:

nasm -f bin hello-3.s && 
dd if=hello-3 of=bootdisk.img conv=notrunc &&
qemu-system-i386 bootdisk.img

当我输入“hello”时,它看起来像这样:

编写自己的引导加载程序

我输入了“你好”

对于我键入的每个键,都有两个字节。一个是按键时,另一个是松开键时。这样,计算机就可以理解我何时按下shift键以及何时松开它。实际字节与我按下的键不匹配,相反,它们代表键盘的扫描码——键所在的位置。

例如,英语和德语键盘混淆了“Y”和“Z”键(以及其他一些键)。但是,键盘处理器的工作方式相同,它只知道“按下 20 键”左右。它不知道此键是标记为 Y 还是 Z。这就是为什么你总是必须告诉你的操作系统你的键盘布局。呵呵,多亏了对汇编程序和机器语言的关心,你学到了一些东西!!

你学到了什么

  • 在汇编程序中编写“Hello World”程序

  • 将其编译为机器语言

  • 使用 hexdump 逐字节查看其内容

  • 使用 objdump 反汇编它

  • 汇编语言和机器语言的区别。在我们的示例中,jmp .end 被转换为 eb (jump) fe (-1)

  • 使用 DD 创建虚拟 Bootdisk

  • 使用 conv=notrunc 插入文件

  • 使用 HexEditor Okteta 修改二进制文件

  • 靴子的神奇数字 55AA(十六进制)

  • 使用 QEMU 模拟计算机启动

  • 寄存器 AX、AH 和 AL 的关系

  • 有关内存分段的引导过程的特权

  • 通过端口 60(十六进制)访问键盘

  • 为什么你必须告诉你的操作系统你的键盘布局

原文始发于微信公众号(安全狗的自我修养):编写自己的引导加载程序

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年7月15日16:51:11
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   编写自己的引导加载程序http://cn-sec.com/archives/2781820.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息