x86汇编从实模式到保护模式实验

实验一

实验环境:MacOS High Sierra

安装nasm

nasm是一个开源的汇编语言编译器,可以将汇编代码编程成x86平台可以执行的二进制代码

1
2
3
4
# 安装homebrew
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" < /dev/null 2> /dev/null
# 安装nasm
brewinstall nasm

Vim 编辑器–用于编辑代码

xxd 命令可以以二进制或者十六进制的形式查看二进制文件,配合vim可以当做十六进制查看器使用。

1
2
3
vim test.bin
# 输入 :%!xxd 可以十六进制形式查看
# xxd -r 可以写二进制文件

写入代码,并编译

1
2
3
vim test.asm
# 粘贴写入代码
nasm -f bin -o test.bin test.asm # 编译生成二进制文件

创建虚拟机镜像,并启动虚拟机查看实验结果

1
2
3
# 创建软盘镜像
dd if=./test.bin of=os.img count=1 bs=1440k
# bs:一个块多少个字节 count:多少个块 软盘大小1.44MB  

打开virtualbox->创建虚拟机->操作系统类型:Other->Other unknow -> 不要硬盘。

设置虚拟机->存储->注册->选中上边创建的os.img。开启虚拟机。引导设置为软盘

此外可以通过以下方式将img转换为vdi的硬盘

1
2
3
4
qemu-img convert -f raw -O vpc ./test.img ./test.vdi
# 此时需要将虚拟机的引导方式修改为硬盘启动
# 也可以通过以下命令转换,但是磁盘文件太小是会出错
VBoxManage convertdd test.img os.vdi # convertdd指将raw硬盘转换为vdi硬盘

![image-20200114204303405](/Users/kevin/Library/Application Support/typora-user-images/image-20200114204303405.png)

创建一个虚拟硬盘,并写入MBR

1
2
3
dd if=./test.bin of=./os.img bs=512 count=1
dd if=/dev/zero of=./os.img seek=1 bs=512 count=2000000 # 创建了一个大小为1G的硬盘,seek是跳过多少块再开始写
VBoxManage convertdd os.img os.vdi

然后将此硬盘镜像设置为虚拟机的启动硬盘,可以得到和上边实验相同的结果。

Bochs

bochs是一个开源的x86模拟器,可以单步调试,可以查看CPU的寄存器值。各个版本的下载链接

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 下载Bochs代码
wget https://sourceforge.net/projects/bochs/files/bochs/2.6.11/bochs-2.6.11.tar.gz/download
# 解压并进入代码目录
tar -zxvf ./download && cd ./bochs-2.6.11
# 配置编译环境
brew install sdl # 依赖项
chmod u+x ./configure
./configure --enable-ne2000 \
            --enable-all-optimizations \
            --enable-cpu-level=6 \
            --enable-x86-64 \
            --enable-vmx=2 \
            --enable-pci \
            --enable-usb \
            --enable-usb-ohci \
            --enable-e1000 \
            --enable-debugger \
            --enable-disasm \
            --disable-debugger-gui \
            --with-sdl \
            --prefix=$HOME/opt/bochs
# 编译安装
# 报错 eth_socket.cc:98:10: fatal error: 'linux/types.h' file not found

参考1参考2

bochs在macos上编译的时候遇到fatal error: 'linux/types.h' file not found错误,暂时不知道怎么解决。

代码解释

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
;代码清单5-1

mov ax,0xb800 ;指向文本模式的显示缓冲区
mov es,ax

;以下显示字符串"Label offset:"

mov byte [es:0x00],'L'
mov byte [es:0x01],0x07		;黑底白字
mov byte [es:0x02],'a'
mov byte [es:0x03],0x07
mov byte [es:0x04],'b'
mov byte [es:0x05],0x07
mov byte [es:0x06],'e'
mov byte [es:0x07],0x07
mov byte [es:0x08],'l'
mov byte [es:0x09],0x07
mov byte [es:0x0a],' '
mov byte [es:0x0b],0x07
mov byte [es:0x0c],"o"
mov byte [es:0x0d],0x07
mov byte [es:0x0e],'f'
mov byte [es:0x0f],0x07
mov byte [es:0x10],'f'
mov byte [es:0x11],0x07
mov byte [es:0x12],'s'
mov byte [es:0x13],0x07
mov byte [es:0x14],'e'
mov byte [es:0x15],0x07
mov byte [es:0x16],'t'
mov byte [es:0x17],0x07
mov byte [es:0x18],':'
mov byte [es:0x19],0x07

mov ax,number 			;取得标号number的偏移地址
mov bx,10 			; 除数

;设置数据段的基地址
mov cx,cs
mov ds,cx

;求个位上的数字
mov dx,0 											; 被除数是DX:AX 32
div bx												; 除数是BX 16位, 商在AX,余数在DX
mov [0x7c00+number+0x00],dl 	; dl中保存的是余数的低8位 ;保存个位上的数字

;求十位上的数字
xor dx,dx 										; 清空dx寄存器的值
div bx
mov [0x7c00+number+0x01],dl 	;保存十位上的数字

;求百位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x02],dl		;保存百位上的数字

;求千位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x03],dl		;保存千位上的数字

;求万位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x04],dl		;保存万位上的数字

;以下用十进制显示标号的偏移地址

mov al,[0x7c00+number+0x04]
add al,0x30										; 要显示数字的ASCII
mov [es:0x1a],al
mov byte [es:0x1b],0x04

mov al,[0x7c00+number+0x03]
add al,0x30
mov [es:0x1c],al
mov byte [es:0x1d],0x04				; 黑底红字,无闪烁,无加亮

mov al,[0x7c00+number+0x02]
add al,0x30
mov [es:0x1e],al
mov byte [es:0x1f],0x04				; 设置显示模式

mov al,[0x7c00+number+0x01]
add al,0x30
mov [es:0x20],al
mov byte [es:0x21],0x04

mov al,[0x7c00+number+0x00]
add al,0x30
mov [es:0x22],al
mov byte [es:0x23],0x04

mov byte [es:0x24],'D'
mov byte [es:0x25],0x07

;无限循环
infi:
jmp near infi

number db 0,0,0,0,0
times 203 db 0 			; 重复定义2030
db 0x55,0xaa

div指令:

被除数是32位时存储在 (DX:AX) 除数是16位的跟在div指令后,商存在AX,余数在DX中

被除数是16位时存储在 AX中,除数是8位的跟在div指令后,商在AL,余数在AH中

DB是伪指令,即没有对应的机器码,只是给编译器用的。DB表示声明一个字节数据,此外还有DW(两个字),DD(双字),DQ(四字)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
;代码清单6-1

jmp near start
mytext db 'L',0x07,'a',0x07,'b',0x07,'e',0x07,'l',0x07,' ',0x07,'o',0x07,\
'f',0x07,'f',0x07,'s',0x07,'e',0x07,'t',0x07,':',0x07
number db 0,0,0,0,0

start:
	mov ax,0x7c0
	mov ds,ax			;设置数据段基地址

	mov ax,0xb800
	mov es,ax			;设置附加段基地址

	cld						; 清除DF位DF=0表示movsw为正方向 std指令设置DF=1 传送方向为从高地址到低地址

	mov si,mytext			; movsw的源source
	mov di,0					; movsw的目的dist
	mov cx,(number-mytext)/2	; (numbner-mytext)/2是需要movsw的次数cx是计数寄存器cx==0rep结束 13
	rep movsw					; 重复movsw,每次mov一个word及两个字节 起始DS:SI 目的ES:DI


	mov ax,number			;得到标号所代表的偏移地址

	;计算各个数位
	mov bx,ax
	mov cx,5
	mov si,10				; 除数
digit:
	xor dx,dx				; 清空dx寄存器的值
	div si
	mov [bx],dl			; 保存数位,保存位置为number
	inc bx					; bx值增加1
	loop digit			; cx != 0 就跳转回digit标签处

	;显示各个数位
	mov bx,number
	mov si,4				; 显示位数
show:
	mov al,[bx+si]
	add al,0x30
	mov ah,0x04
	mov [es:di],ax

	add di,2
	dec si				; si 自减1
	jns show			; SF=0则跳转 SFSign Flag)如果前边dec执行后最高位为0则SF=0,否则置1


	mov word [es:di],0x0744 ; 0x07 表示黑底白字 0x44是字母'D'
	jmp near $			; 死循环

	times 510-($-$$) db 0 		; 一个块512B,减去55aa还有510个块, $代表行首地址,$$代表当前段的起始地址
	db 0x55,0xaa			; MBR分区标记

movsw指令

movsw和movsb指令执行时,原始数据串的地址由DS:SI指定,要传送的目的地址为ES:DI,传送次数为CX,movsb每次传送一个字节,movsw每次传送两个字节。

正向传送时,没传送一个字节(movsb)或者一个字(movsw)SI和DI加1或者2;

反向传送时,没传送一个字节(movsb)或者一个字(movsw)SI和DI减1或者2;

传送方向由标志寄存器(FLAGS)的DF位指定,0为正,1为负。cls清零DF位,std将DF位置1.

单纯的movsw或者movsb指令只能执行1此,需要加上rep前缀,只要CX不为0就一直执行movsw或者movsb。