首页
社区
课程
招聘
[翻译]X86 汇编:真的是“恐怖”的存在吗?
2018-10-15 21:01 6818

[翻译]X86 汇编:真的是“恐怖”的存在吗?

2018-10-15 21:01
6818

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:

[BITS 16]  ;表明为 16 位汇编代码
[ORG 0x7C00];起始地址,告知汇编器将代码加载到内存的位置

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

 

希望这是进入 Assembly Language 的有趣开端,更多精彩,敬请期待 ......


磁盘 - 磁盘扇区写入参数定义

 

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 置位时有效 )

原文:https://blog.benjojo.co.uk/post/interactive-x86-bootloader-tutorial(文章略有删改)
翻译: 看雪翻译小组 StrokMitream
校对 :


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2019-2-2 15:35 被admin编辑 ,原因: 图片本地化
收藏
点赞1
打赏
分享
打赏 + 1.00雪花
打赏次数 1 雪花 + 1.00
 
赞赏  junkboy   +1.00 2018/10/15
最新回复 (11)
雪    币: 11716
活跃值: (133)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
junkboy 2018-10-15 21:09
2
0
谢谢分享
雪    币: 3373
活跃值: (3407)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
fengyunabc 1 2018-10-16 10:01
3
0
第2张图片里的"AI0"是什么意思? 难道不该是"A20"么?
雪    币: 965
活跃值: (89)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ielts 2018-10-16 15:39
4
0
谢谢分享 
雪    币: 32
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
吴华文 2018-10-17 15:52
5
0
感谢分享
雪    币: 0
活跃值: (130)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
whitelen 2018-10-18 14:25
6
0
感谢分享
雪    币: 2730
活跃值: (781)
能力值: ( LV9,RANK:310 )
在线值:
发帖
回帖
粉丝
StrokMitream 3 2018-10-19 23:17
7
0
fengyunabc 第2张图片里的"AI0"是什么意思? 难道不该是"A20"么?
这个地方是 AIO(Asynchronous I/O,异步 I/O),不是 A “10” 
感谢大家的支持,欢迎大家对帖子内容提出质疑!
雪    币: 288
活跃值: (901)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
青史无疆 1 2018-10-22 18:41
8
0

mov al,1 //这里应该翻译为:将要读取的扇区数量

参考手册 原文:

DISK - READ SECTOR(S) INTO MEMORY
AH = 02h
AL = number of sectors to read (must be nonzero)
CH = low eight bits of cylinder number
CL = sector number 1-63 (bits 0-5)
high two bits of cylinder (bits 6-7, hard disk only)
DH = head number
DL = drive number (bit 7 set for hard disk)
ES:BX -> data buffer
雪    币: 310
活跃值: (1917)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
niuzuoquan 2018-10-22 21:49
9
0
mark
雪    币: 204
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zhizhengfu 2018-10-23 14:29
10
0
thank you share
雪    币: 2730
活跃值: (781)
能力值: ( LV9,RANK:310 )
在线值:
发帖
回帖
粉丝
StrokMitream 3 2018-10-27 09:11
11
0
青史无疆 # mov al,1 //这里应该翻译为:将要读取的扇区数量 参考手册 原文: --- ``` DISK - READ SECTOR(S) INTO MEMORY AH = 02h ...
多谢指正,已对原翻译进行修改!
雪    币: 5352
活跃值: (11770)
能力值: ( LV12,RANK:312 )
在线值:
发帖
回帖
粉丝
一半人生 5 2018-11-17 09:17
12
0
谢谢分享 有机会研究一下 
原来看早版本Linux,好像0.11的内核 加电后由硬件强制cs:ip指向0xFFFF0地址 也就是BIOS 
0x00000~0x003FF是中断向量表
0x00400~0x004FF是BIOS数据区
借助int 19(0xE6F2), 然后读取软盘中的0盘面0磁道1扇区的512B 第一个启动扇区 加载到0x7C00处 然后复制到0x9000 
后面借助 int 13加载后面的扇区 从16位到32位启动的也是一个很奇妙的过程(实模式到保护模式)他会废除中断向量表 (将数据覆盖0x00000) 建立新GDTR IDTR等   A20也是标志性的地址(开启后将没有类似于回滚地址机制(16位)) 很多知识有些模糊 有机会在深入研究
不得不说真是艺术 多一个字节空闲的内存都没有
最后于 2018-11-17 09:25 被一半人生编辑 ,原因:
游客
登录 | 注册 方可回帖
返回