吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 10693|回复: 89
收起左侧

[系统底层] 从0到-1写一个操作系统-0x01-BIOS以及MBR

  [复制链接]
peiwithhao 发表于 2022-12-26 21:26
本帖最后由 peiwithhao 于 2023-3-2 14:00 编辑

这里写个往期推荐,这样可以来回跳跃(狗头
0x00-环境准备
0x01-BIOS以及MBR
0x02-MBR支持显卡
0x03-MBR操作硬盘以及Loader
0x04-进入保护模式

0x00 开机

大家都知道,开机时咱们得首先得将操作系统载入内存RAM区才可以继续我们的工作,但是咱们操作系统都没运行,那我们是如何实现加载这一过程呢,难道说是妥妥的灵异事件?
其实并非如此,咱们是不是忘了还有个磁盘呢,而且不要认为没操作系统就无法运行程序,实际上操作系统只是给咱们提供了一个方便运行程序的环境,如果没有操作系统程序也是可以执行的,因为程序执行只需要简单的两个元素,指令序列以及CPU按序列执行即可,只不过没操作系统你可能实现函数十分繁琐,也就比如说虚拟地址与物理地址的映射,但是这种情况在早期没有保护模式这一概念时,程序员们也确实是直接运用物理地址进行编程。何况还有更早的程序员打纸孔带呢(


现在来回答刚刚的问题,那就是开机如何加载呢,首先给出一个答案,开机后咱们地一个需要运行的程序是位于磁盘的BIOS程序:

0x01 软件接力第一棒,BIOS

BIOS全称为 Base Input & Output System,也即基本输入输出系统,他的主要工作那就是基本输入输出了(手动狗头

1. 实模式

实模式是什么呢,简单粗暴来讲那就是哥们只用物理地址的模式,因为最开始开机时还不存在页表,更不存在页映射一说,所以咱们最开始的地址只能通过物理地址来进行编程,并且此时编程也只能用汇编。
而在Intel 8086时期只有20条地址线,也就是说若按字节寻址的话咱们的发挥空间就只有1MB,用16进制表示就是从0x00000到0xFFFFF,以下我先给出实模式下的地址分布:

开始地址 结束地址 大小 用途
FFFF0 FFFFF 16B BIOS入口,这么小的一个位置实际上仅仅是一个跳转指令
F0000 FFFEF 64KB-16B 系统BIOS
C8000 EFFFF 160KB 映射硬件适配器的ROM或内存映射式I/O
C0000 C7FFF 32KB 显示适配器BIOS
B8000 BFFFF 32KB 文本显示适配器
B0000 B7FFF 32KB 黑白显示适配器
A0000 AFFFF 64KB 彩色显示适配器
9FC00 9FFFF 1KB EBDA
7E00 9FBFF 约608KB 可用
7C00 7DFF 512B MBR加载地址
500 7BFF 约30KB 可用
400 4ff 256B BIOS数据区域
000 3FF 1KB 中断向量表

2. BIOS

从上表也可以直观的看出来,在0xF0000~0xFFFFF这儿的64KB就保存的BIOS代码,而BIOS的功能就是检测初始化硬件。但是具体是如何初始化呢,硬件自身会实现一些初始化的功能调用,这里BIOS直接调用即可,就跟咱们高级程序调库类似,但是BIOS面向的是硬件,而咱们面向的是操作系统或者说是程序员自己实现的库。
除了上述功能BIOS还做了一个伟大的事,那就是建立中断向量表,这样咱们就可以通过"int 中断号"来进行硬件调用(每次看到这个int我都想到int 0x80哈哈哈,来自pwner奇奇怪怪的直觉)
但是我们这里还得清楚一件事情,那就是BIOS是放哪儿的,这里直接说答案,他是存放在内存ROM区中,学过机组的伙伴可能知道主存一般分为RAM和ROM,其中RAM大多由DRAM这种存储器构成,但是他是断电就消失,并且他要保持数据必须一定时间内还要不断刷新行,所以咱们的BIOS不能放到rAM中,但是ROM断电是不会消失的,就像刻光盘一样刻上面了,所以咱们的BIOS是放在ROM区中的。

而BIOS其实也是指令流,也是个程序,所以肯定也得有入口地址,这个入口地址便是0xFFFF0,这时候就得考虑如何找到这个地址了,这里有个既定规则,那就是实模式下寄存器宽度为16位,而程序一般都是通过分段机制来进行寻址,分段机制需要用到两个寄存器,那就是cs,ip,相信会汇编的同学知道,所以咱们寻址都是通过cs:ip来进行寻址,但是如何通过两个16位宽度的寄存器来表示20位的地址呢,这里已经有前人作答,咱们站在巨人肩膀上了已经,那就是通过将cs的值左移四位,然后再加上ip地址值,这样就刚好是20位了,我话个图:


这里图中所表示的也就是真实情况下cs:ip的值,但是你可能会疑惑明明有其他的方案为什么只有这个是真实的呢,你说的确实对,但是这是人家规定了的,没有理由可言,就相当与为什么负数小于正书,这是人为规定的。还有个至于为什么cs会设定为0xf000,这也是加点他自动变的,没有为什么。

在这里也给大家说清楚,这里BIOS的入口地址为啥是0xFFFF0,此时留给BIOS的大小只有16字节了,16字节能干啥呢,没错啥也干不了,这里的16字节就只是一个jmp指令,也就是说你先执行BIOS时,你得先到0xFFF0,然后通过这里的跳转指令再跳到别处(过于滑稽,而这里的指令具体就是jmp far f000:e05b,也就是远眺指令到0xfe05b的物理地址,而这里才是真正BIOS代码开始的地方

而接下来BIOS所做的事情就是各种检测内存显卡等外设信息,然后在0x000到0x3ff处建立中断向量表。


在完成上述工作之后,BIOS的工作告一段落,之后就是大名鼎鼎的MBR了。

0x02 主引导记录MBR

BIOS最后的工作就是检验0盘0道1扇区的内容,这里历史遗留问题所以说这里扇区是从1开始,大家不用刻意管这个,只需要记住BIOS最后检验的是地一个磁盘扇区即可,在检验过程中,若BIOS检验出该磁盘末尾两个字节是0x55和0xaa则认定其为活动区,便加载到物理地址0x7c00,然后跳转过去,即可开始执行MBR。

这里的0x55,0xaa是一对魔数,就跟java字节码文件开头的0xcafebaby一样,没啥实际意义,而0x7c00,跟上面一样,规定为MBR起始地址。(这里我看了大象书有详细的解释,但是我觉得意义不大,就没写出来
MBR为咱们地一个在编写操作系统中自行构造的程序,理论上现在咱们无所不能(中二起来惹 , 这里还有几个规定,我在这里一一说出:

  1. MBR大小为512字节
  2. 地511以及512字节必须是0xaa,0x55,这是由于咱们模拟的是x86平台,所以采用小端序
  3. 凑行(笑死

1. 汇编编程基础

咱们本次的汇编采用NASM编译器,所以采用他的编译规则,其中比较常用的符号这里得讲讲:

  • $:表示当前汇编代码行地址
  • $$:表示本section地址
  • section: 汇编代码中的节,这是程序员自行设定的,声明自己这个是个干什么的节

    2. NASM简单用法

    咱们不用掌握全,知道目前需要用到的即可

    nasm -f <format><filename> [-o <output>]

其中-f就是指定输出文件格式。若要知道有多少格式,咱们可用nasm -hf来查看,具体我借个图如下:

由于代码较繁琐,就不贴这儿了,不过我还是会讲解。

3.编写思路

虽然这里不会贴代码,但是我会给出思路,大家可以自己尝试,然后去github上clone我的源码查看即可,里面注释也比较详细

  1. 首先清屏,这里利用了BIOS所建立的中断向量表,用0x06号功能,即int 0x10,而我们实现系统调用的操作只需我们将功能号送入ah(注意是ah,也就是说向ax中传入0x600)寄存器,然后执行int 0x10即可。

  2. 而在我们编写的section后面加上vstart=0x7c00表示告诉编译器把我这个起始地址编译为0x7c00

  3. 然后我们再通过中断3号功能来获取光标位置

  4. 然后我们来实现打印字符串,此刻由于我们未使用IO知识,所以我们还是用中断向量来实现(现在知道BIOS的伟大之处了),运用13号子功能

  5. 打印结束记得填充,nasm中有自带的填充语句,即为

    times 指令

  6. 最后两字节填充0xaa55

4.代码实现

这里由于代码不多所以直接贴这儿,具体代码可以去我的github上面拷下来看,由于之后操作系统的编写代码就不止这么少,所以我之后很少会贴,毕竟十分看起来水贴

;主引导程序
;------------------------------------------
SECTION MBR vstart=0x7c00   ; 向编译器表示咱们这儿起始地址应为0x7c00
  mov ax,cs
  mov dx,ax                 ; 由于BIOS通过 0:0x7c00跳转MBR,所以此时cs为0,因此借他来初始化寄存器
  mov es,ax
  mov ss,ax
  mov fs,ax
  mov sp,0x7c00
; 清屏利用0x06号子功能,上卷全部行即可清屏
;-------------------------------------------
;INT 0x10    功能号:0x06    功能:上卷窗口
;-------------------------------------------
;输入:
;AH 功能号:0x06
;AL = 上卷行数(若为0则表示全部行,太适合我们辣)
;BH = 上卷行属性
;(CL,CH) = 窗口左上角的(X,Y)位置
;(DL,DH) = 窗口右下角的(X,Y)位置
;无返回值
  mov ax,0x600
  mov bx,0x700
  mov cx,0          ; 左上角(0,0)
  mov dx,0x184f     ; 右下角(0x4f,0x18),在VGA文本模式中一行只能容纳80字符
  int 0x10

;;;;;;;; 下面三行获取光标位置 ;;;;;;;;;;;;;;;
; ,get_cursor获取光标位置,并在光标位置打印字符
  mov ah,3          ;输入: 3号子功能即为获取当前光标位置
  mov bh,0          ;bh 寄存器存储的是待获取光标的页号

  int 0x10          ;输出 ch=光标开始行,cl =光标结束行
                    ;dh=光标所在行号,dl=光标所在页号
;;;;;;;; 获取光标结束 ;;;;;;;;;;;;;;

;;;;;;;;;;; 打印字符串 ;;;;;;;;;;;;;;;;
;依旧用中断的0x13号子功能
  mov ax, message       ;文件末会声明此字符串
  mov bp, ax            ;es:bp为串首地址,es此时由于最开始的初始化,同cs一致
;光标位置需要用到dx寄存器内容,cx中的光标位置可忽略
  mov cx,0xa              ;cx 为串长度,不包括结束副\x00
  mov ax,0x1301         ;ah=13为显示字符功能号
                        ;al = 01 表示该功能的模式,具体模式有以下几种
                        ;(1)0,显示字符串,光标返回起始位置
                        ;(2)1,显示字符串,光标跟随到新位置
                        ;(3)2,显示字符串以及属性,光标返回起始位置
                        ;(4)3,显示字符串以及属性,光标跟随到新位置
  mov bx,0x2            ;bh存储要显示的页号,此时为0页,bl中是字符属性,bl=02h表示黑底绿字
  int 0x10              ; 执行BIOS 0x10中断
;;;;;;;;;; 打印字符串结束 ;;;;;;;;;;;;;;;
  jmp $                 ;悬停指针

  message  db "peiwithhao"
  times 510-($-$$) db 0     ;$-$$表示该指令行距离section起始地址的偏移,这里也可表示为目前指令大小
  db 0x55,0xaa

5.汇编

运用我们之前的知识

nasm -o mbr.bin mbr.S

之后咱们用ls查看下是否512字节,利用ls即可验证

-rw-rw-r-- 1 dawn dawn 512 Dec 26 07:41 mbr.bin

咱们会发现确实无误,这样咱们就可以做接下里的步骤,那就是把咱们的MBR程序给拷到磁盘上,而Linux本身提供了一个dd命令,他被成为穿甲弹,他可以深入磁盘任何一个扇区,这里给出几个选项的示意:

  • of=FILE 指定要读的文件
  • bs=BYTES 指定块大小
  • count=BLOCKS 指定块数
  • seek=BLOCKS 制定当我们把块输出到文件是要跳过多少块
  • conv=CONVS 指定如何转换文件

介绍结束,之后我们使用这个命令将mbr.bin打入相应磁盘扇区,也就是第一快512扇区,还记得之前那个0,0,1吗,就是BIOS结束的工作所找寻的快

dd if=/你的路径/mbr.bin of=/你的路径/bochs/hd60M.img bs=512 count=1 conv=notrunc

执行完出现以下提示即表示成功打入

然后咱们就可以开始测试了

0x03 测试代码

激动的心,颤抖的手,这是咱们地一个自己实现的代码,还是跟之前一样

bin/bochs -f bochsrc.disk

我们在最开始执行的指令也可以发现这个就是jmp,跟我们最开始说BIOS执行的第一条指令的论述是完全一致的!

一切如咱所愿,大成功!!

0x04 总结

本结不难,大伙可能会遇到些什么功能号,上卷窗口类似的感觉很杂,但是不需担心,这并不是我们写OS的重点,所以稍微了解即可
这次得源码依旧在github上保持更新,分支名为BIOS,欢迎大家指正

传送门

免费评分

参与人数 39吾爱币 +45 热心值 +34 收起 理由
Longczx + 1 + 1 用心讨论,共获提升!
SINCERLY + 1 + 1 我很赞同!
a8500202 + 1 + 1 用心讨论,共获提升!
YousYu + 1 + 1 谢谢@Thanks!
不挂科夫斯基 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
kongxiaofang + 1 + 1 谢谢@Thanks!
allspark + 1 + 1 用心讨论,共获提升!
小兔一样的小白 + 1 + 1 我很赞同!
willJ + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
superworker2022 + 1 + 1 我很赞同!
vaycore + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
lht64877586 + 1 + 1 纯bios已经不那么流行了,希望楼主做个uefi bios的浅析!
hxdfree + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
word11 + 1 用心讨论,共获提升!
qpm + 1 + 1 我很赞同!
redyan9985 + 1 + 1 用心讨论,共获提升!
qq3bot + 1 + 1 谢谢@Thanks!
Lishi8 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
maxiao123 + 1 用心讨论,共获提升!
kanglehao + 1 + 1 我很赞同!
林小锋 + 1 难度太高
黑小龙 + 1 + 1 我很赞同!
hobby + 1 + 1 我很赞同!
xinkui + 1 + 1 谢谢@Thanks!
逆向学习 + 1 + 1 我很赞同!
wbzb + 1 + 1 热心回复!
wangtian666 + 1 我很赞同!
冬天冷了多穿点 + 1 + 1 我很赞同!
sbham + 1 + 1 谢谢@Thanks!
SYQ2333333 + 1 我很赞同!
Liki + 1 + 1 我很赞同!
Codeman + 2 + 1 我很赞同!
takami + 1 + 1 谢谢@Thanks!
saintorange + 1 + 1 我很赞同!
唔射不亦精乎 + 1 + 1 我很赞同!
ggfly131 + 1 我很赞同!
hkq666 + 1 + 1 谢谢@Thanks!
cjcmxc + 1 + 1 谢谢@Thanks!
思念曹操 + 1 + 1 用心讨论,共获提升!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

pwdobwq 发表于 2022-12-26 22:17
十多年前使用过一个大神写的操作系统,现在还在开发后续版本哦

http://www.os-z.com/
hnaylpy 发表于 2022-12-27 08:10
先收藏,补充点基础知识再学习学习。谢谢分享!
rzxcs 发表于 2022-12-26 21:50
红客联盟红哥 发表于 2022-12-26 21:53
封装试试看
 楼主| peiwithhao 发表于 2022-12-26 21:54

这还远未达到操作系统的要求,仅仅是个开头的引导程序,到全部写完了封装试试
红客联盟红哥 发表于 2022-12-26 22:04
peiwithhao 发表于 2022-12-26 21:54
这还远未达到操作系统的要求,仅仅是个开头的引导程序,到全部写完了封装试试

好用的话可以开公司,我有tg大学联盟可介绍他们体验
wl823 发表于 2022-12-26 22:12
哈点研究一下
li645944229 发表于 2022-12-26 22:24
请继续教我
anandier 发表于 2022-12-26 23:06
很专业额
qiaolei0128 发表于 2022-12-27 00:03
很专业!!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-21 16:36

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表