X86 汇编:真的是“恐怖”的存在吗?
Assembly Language(汇编语言),对于哪些从未深入了解其中的人来说,可谓是高深莫测之极。由于我们的计算机是以汇编语言为基础开发出来的,了解这些内容将会极大地有助于我们弄清CPU的工作原理。
X86 计算机对 IBM PC 原生的完全后向兼容-从系统顶层到加电启动甚至底层硬件电路,实在是令人印象深刻。 这意味着几乎所有的 PC 都能运行1983年以后开发的软件;不过,需要为此付出的代价也是非常之高的。X86 架构由于它的 edge case 和复杂性而“名声大振”。
要明白一台现代 X86 计算机的启动过程,我们需要了解 X86 架构的发展历史,启动过程在 99%的情况下是由 bootloader 处理的。
在我们的 demo 中将专注于16位“实模式”-DOS系统和其他变种操作系统的运行环境。在该模式下,CPU并不支持现代操作系统所具有的各种安全保护措施。运行于其上的程序可以访问系统各个角落甚至修改系统。这使得 DOS 病毒大量泛滥(尽管这些病毒大都不具破坏性,但却很令人厌烦)。
在实模式下,与硬件交互的模型也极其简单。PC 通过外部丰富的软中断与各式各样的硬件交互。
这让使得最初的阶段的系统开发工作容易起来:因为开发者只需知道少许软中断即可完成绝大多数 I/O 操作。
了解这些之后,我们开始我们的第一个 demo:
[16 位汇编代码
[ ];起始地址,告知汇编器将代码加载到内存的位置
mov ah, 0x0A ; 设置 BIOS 调用类型
mov al, 66 ; 待打印的字母
mov cx, 1 ; 打印的次数
mov bh, 0 ; 页号
int 0x10 ; 调用 BIOS 打印字母
hlt ; 暂停,处理器不执行任何操作
TIMES 510 - ($ - $$) db 0 ;用 0 填充该扇区的剩余空间
DW 0xAA55 ; 扇区结束标识
] ;表明为 上面的代码什么意思呢? 其中mov
指令是用来把数据从一个位置传送到另一个地方。在此,我们只是用来在寄存器间传送数据,并未涉及内存空
间。int
指令指代一系列软中断,在这段代码中是 16号中断。
接下来,既然我们已经执行完我们的任务,那么调用hlt
指令停止 CPU 运行。
到现阶段,是不是有点疑惑,我们是怎么就执行到这一步了呢?计算机刚才又是如何执行这些指令的?
实际上,系统启动的第一阶段是启动 BIOS - 用于配置和检测系统硬件。同时, BIOS 输出系统信息给接下来启动的操作系统程序,以便于接下来的硬件检测工作。
假定 BIOS 知道它是从何种设备上启动,那么,它首先会加载该设备
的第一个扇区512字节到内存中的指定位置,然后跳转到此处将控制权移交给下一个启动程序。
在上面的 demo 中,程序仅读取了512字节的有效载荷。如果我们需要执行的程序超过512字节大小,怎么办呢?
有幸的是,BIOS 可以通过调用 13号 int 中断,来访问系统中的磁盘驱动器。
[BITS 16]
org 0x7C00
start:
; This section of code is added based on Michael Petch's bootloader tips
xor ax,ax ; 为 DS 置 0 准备
mov ds,ax
mov bx,0x8000 ; 栈段可以是可用内存的任意一段
mov ss,bx ; 栈顶位于 0x80000.
mov sp,ax ; 设置 SP=0 ,使栈底位于 0x90000 下
cld ; 设置 DF 位为正向
mov ah, 0x02
mov al, 1
mov ch, 0
mov cl, 2
mov dh, 0
mov bx, new
mov es, bx
xor bx, bx
int 0x13
jmp new:0
data:
new equ 0x0500
times 510-($-$$) db 0
dw 0xaa55
sect2:
mov ax, cs
mov ds, ax ; 设置 CS=DS. CS=0x0500, 因此 DS=0x500
; 如果变量已经在代码中设置,则要求
; 正确地引用其内存地址
mov ax, 0xB800
mov es, ax
mov byte [es:420], 'H'
mov byte [es:421], 0x48
mov byte [es:422], 'E'
mov byte [es:423], 0x68
mov byte [es:424], 'L'
mov byte [es:425], 0x28
mov byte [es:426], 'L'
mov byte [es:427], 0x38
mov byte [es:428], 'O'
mov byte [es:429], 0x18
mov byte [es:430], '!'
mov byte [es:431], 0x58
hlt
上述代码,加载虚拟磁盘的下一扇区 ,然后跳转到该处。13号 int 中断 API十分简洁,透露出计算机存储器工作的底层细节(尽管flsh存储器广泛使用,众多的数值依旧保留以保持兼容)。
BIOS 提供的服务极其的丰富,这有一个非常棒的参考资料:
http://www.ctyme.com/intr/rb-0608.htm
附
磁盘 - 磁盘扇区写入参数定义
AH = 03h
AL = 写入的扇区数 (必须非零)
CH = 柱面数低8位
CL = 1-63扇区号 (bits 0-5)
柱面数的高2位 (6-7位, 只用于硬盘hard disk only)
DH = 磁头数DL = 驱动器编号 (磁盘为硬盘驱动器时第7位置位)
ES:BX -> 数据缓冲区
返回值:
CF 写入错误
时置位
CF 写入成功时清除置位
AH = 磁盘状态
AL = 传输的扇区数
(仅当某些BIOS的 CF 置位时有效 )
- End -
看雪ID:StrokMitream
https://bbs.pediy.com/user-747320.htm
本文由看雪翻译小组 StrokMitream 编译
来源Ben Cox @blog.benjojo.co.uk
转载请注明来自看雪社区
好书推荐:
戳
热门技术文章推荐:
公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com