在Intel的386CPU中, 增加了一系列的指令用于支持保护模式的操作, 前面的代码其实我们已经使用了不少, 另外还有些针对段描述符的指令也是非常有用的..

由于大部分人不会写自己的操作系统, 所以一般也比较少使用特权指令, 甚至有一部分在3环可以使用的指令也并不了解, 不过话说回来, 类似于sgdt, sidt这样的指令, 就算是获取到了这些值也们没有什么用.  所以对这些指令也就比较陌生了..

但是对于写保护模式程序来说, 很多指令是非常有用的, 类似lsl(装载段界限)就非常好用, 如果自己使用mov指令那么就比较麻烦了.. 所以遇到这种指令也不用去记, 当需要使用的时候, 当很麻烦的时候自然就会想起这种指令的..

这一篇估计是保护模式里面最轻松的一篇, 除了介绍一点常用的指令, 没有做任何事情. 唯一的亮点是从16位保护模式段跳入32位保护模式段, 然后在32位保护模式段中返回. 前面我们写代码在这种地方一般是使用Jmp, 这里使用了CALL.

用CALL指令也不是不可以, 只是在返回的时候需要自己修正堆栈中的CS:IP, 因为16位的段压栈是压16位值. 而32位返回的时候需要堆栈的信息是32位的, 所以需要自己手动修正. 前面关于这个问题我做了不少的实验, 一直都没有成功, 不过随着现在保护模式的代码敲得多了, 对保护模式有了一些感觉了.

我说说这个代码的逻辑功能, 首先在实模式下初始化一些该初始化的东西, 显示了实模式下的GDT, IDT, 然后跳入保护模式, 用于从实模式跳入保护模式的段, 一般是使用16位段的, 用于作为缓冲段, 因为无法从32位段中返回实模式,然后我们从保护模式的16位段跳入保护模式的32位段, 这里使用的时CALL指令, 以前一直没有使用这个方法, 需要注意下, 不过正常来说,对于这种16-32这种跨度的段还是使用Jmp会好点.

跳入保护模式以后, 演示了一些只有在保护模式才能够使用的特权指令, 包括str,sldt, cr0, cr3, dr7什么的, 这些都只能在保护模式下使用的指令.. 然后还使用了类似于arpl, verr这些指令, 都是非常有用的, 也非常简单, 跟着代码的注释我想很容易就弄懂了..

做完这些事情, 然后就ret从32位代码段返回到16位代码段, 然后退回实模式.还是和以前一样, 有图有真相..

        Priviin.rar

  

   

  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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
;============================================================================
	;演示特权指令的使用
	;MASM9 + LINK5 
	;编译选项请参见 makefile  TAB = 8
;============================================================================
	.686p
	Include pm.inc
	option casemap:none
Stack_Len	equ	1024		;堆栈大小
;============================================================================
GdtSeg		Segment use32		;全局描述符
;					;段基址	;段界限			;属性
Dummy:			Descriptor	0, 	0, 			0		;空的描述符
Normal:			Descriptor	0, 	0ffffh,			DA_DRW 		;规范段描述符
g_CodeTempDesc:		Descriptor 	0,	0ffffh, 		DA_C		;非一致代码
g_StackDesc:		Descriptor	0,	Stack_Len-1,		DA_DRW or 4000h	;32位堆栈段
g_DataDesc:		Descriptor	0,	0fffffh, 		DA_DRWG		;全局4G数据段
g_DemoCodeDesc:		Descriptor	0,	DemoCodeSegLen-1,	DA_C or DA_32	;32位演示代码段描述符
g_VideoDesc:		Descriptor	0b8000h,0ffffh,			DA_DRW		;全局视频操作段

g_TestDesc:		Descriptor	1111h, 	0ffffh, 		DA_DRW		;测试描述符

;----------------------------------------------------------------------------
NormalSelector		equ	Normal 			- GdtSeg			;规范段选择子
g_DataSelector		equ	g_DataDesc		- GdtSeg			;全局数据段
g_VideoSelector		equ	g_VideoDesc		- GdtSeg			;全局视频段选择子
g_DemoCodeSelector	equ	g_DemoCodeDesc		- GdtSeg			;演示代码段选择子
g_StackSelector		equ	g_StackDesc		- GdtSeg			;0环堆栈段
g_CodeTempSelector	equ	g_CodeTempDesc 		- GdtSeg			;代码段选择子

g_TestSelector		equ	g_TestDesc		- GdtSeg			;测试选择子
g_TestRSelector		equ	g_TestDesc		- GdtSeg + SA_RPL3		;测试选择子
;----------------------------------------------------------------------------
GDTLen			equ	$ - GdtSeg		;GDT长度
_RegSp		word	?				;用于保存SS:SP
_RegSs		word	?
;----------------------------------------------------------------------------
SzGdtMsg	byte	"RealMode GDT: 0x"
SzIdtMsg	byte	"RealMode IDT: 0x"
SzMswMsg	byte	"RealMode Msm: 0x"
SzCr0Msg	byte	"ProtectedMode CR0: 0x"
SzCr3Msg	byte	0,"Cr3: 0x"
SzDr7Msg	byte	0,"Dr7: 0x"
SzTrMsg		byte	"Tr:  0x"
SzLdtrMsg	byte	" Ldtr:0x"
SzLimitMsg	byte	0, "Limit:"
GdtSeg		Ends	
;============================================================================
Stack0Seg	Segment use32
	byte	Stack_Len dup(?)
Stack0Seg	Ends
;============================================================================
g_DemoCodeSeg	Segment use32				;演示代码段
;============================================================================
        ;换行, _dwValue传递当前的行列号, 计算好在Eax中返回下一行开始
PrintLn        Proc    uses ebx  _dwValue:dword
        mov     eax, _dwValue
        mov     bl, 160
        div     bl
        and     eax, 0FFh
        inc     eax
        mov     bl, 160
        mul     bl
        ret
PrintLn        Endp
;============================================================================
;;将al中的数字转成ASCII加上显示属性并在EAX中返回
HexToAscii     Proc     uses ebx
 
        mov     bl, al
 
        and     al, 0fh
        add     al, 90h
        daa
        adc     al, 40h
        daa
        mov     ah, 0ch
        shl     eax, 16                                 ;转换低位          
 
        mov     al, bl
        shr     al, 4
        and     al, 0fh
        add     al, 90h
        daa
        adc     al, 40h                                 ;将高位转成ASCII
        daa
        mov     ah, 0ch                                 ;属性
        ret
HexToAscii     Endp
;----------------------------------------------------------------------------
	;es:si-->字符串返回缓冲区, eax要转换的dword
DwordToHex	Proc	uses ebx 			;dword转成字符串, 
	
	mov	ebx, eax
	shr	eax, 16 + 8
	call	HexToAscii
	stosd
	mov	eax, ebx
	shr	eax, 16
	call	HexToAscii
	stosd
	mov	eax, ebx
	shr	eax, 8
	call	HexToAscii
	stosd
	mov	eax, ebx
	call	HexToAscii
	stosd
	ret
DwordToHex 	Endp
;============================================================================
	;ds->si要显示的字符串, es->di要显示的位置 
ShowMsg	Proc					;显示信息到屏幕上		
@@:	lodsb
	mov	ah, 0ah
	stosw
	loop	@b
	ret	
ShowMsg 	Endp
;============================================================================
	;显示控制寄存器值
ShowControlReg	Proc
	local	_lpRealAddress:dword
	local	wBuf:word
	
;----------------------------------------------------------------------------
	;实模式下的字符串地址, 换算成保护模式下的, 方法
;----------------------------------------------------------------------------
	mov	esi, GdtSeg
	shl	esi, 4
	mov	_lpRealAddress, esi
;----------------------------------------------------------------------------	
	lea	esi, SzCr0Msg
	add	esi, _lpRealAddress
	mov	ecx, sizeof SzCr0Msg
	call	ShowMsg

	mov	eax, cr0
	call	DwordToHex				;显示cr0
;----------------------------------------------------------------------------	
	lea	esi, SzCr3Msg
	add	esi, _lpRealAddress
	mov	ecx, sizeof SzCr3Msg
	call	ShowMsg
	
	mov	eax, cr3				;显示CR3
	call	DwordToHex
;----------------------------------------------------------------------------
	lea	esi, SzDr7Msg
	add	esi, _lpRealAddress
	mov	ecx, sizeof SzDr7Msg
	call	ShowMsg
	
	mov	eax, dr7				;调试寄存器dr7
	call	DwordToHex
	
;----------------------------------------------------------------------------
	Invoke	PrintLn, edi
	mov	edi,eax
	add	edi, (5+14) * 2				;换行
		
	lea	esi, SzTrMsg
	add	esi, _lpRealAddress
	mov	ecx, sizeof SzTrMsg
	call	ShowMsg
	
	str	word ptr ss:[wBuf]
	movzx	eax, word ptr ss:[wBuf]			;Tr寄存器
	call	DwordToHex
;----------------------------------------------------------------------------	
	lea	esi, SzLdtrMsg
	add	esi, _lpRealAddress
	mov	ecx, sizeof SzLdtrMsg
	call	ShowMsg
	
	sldt	word ptr ss:[wBuf]
	movzx	eax, word ptr ss:[wBuf]			;LDTR寄存器
	call	DwordToHex
;----------------------------------------------------------------------------	
	ret
ShowControlReg Endp
;----------------------------------------------------------------------------
_DemoEntry		equ	$ - g_DemoCodeSeg
DemoEntry	Proc	far

	mov	edi, 9 * 80 * 2 + 5 * 2			;9行5列
	Invoke	ShowControlReg
;----------------------------------------------------------------------------
	;测试调整申请特权级指令
	mov	ax, g_TestSelector
	mov	dx, g_TestRSelector			
	arpl	ax, dx			;if (ax)rpl < (cx)rpl (ax)rpl = (cx)rpl
;----------------------------------------------------------------------------
	;装载描述符的界限
	mov	bx, 0
	mov	ax, g_TestSelector
	xor	edx, edx
	xor	cx, cx
	
	lsl	edx, eax		;将opera2的选择子指向的界限装入
	lsl	cx, ax
;----------------------------------------------------------------------------
	;装载描述符的属性字段
	xor	edx, edx
	xor	cx, cx
	lar	edx, eax
	lar	cx, ax	
;----------------------------------------------------------------------------
	;测试选择子指向的段是否可读
	verr	ax
	jnz	@f
	;描述符是可读的
@@:
;----------------------------------------------------------------------------
	;测试选择子指向的段是否可写
	verw	ax
	jnz	@f
	;描述符是可写的
@@:	
;----------------------------------------------------------------------------
	;由于是从16位代码段跳转过来的, 压栈都是16位的, 这里是32位代码段, 所以
	;必须扩展返回地址, 才能够正常的返回
;----------------------------------------------------------------------------
	pop	ax					;ip
	pop	cx					;cs
	movzx	ecx, cx
	movzx	eax, ax
	push	ecx					;cs
	push	eax					;eip
	ret
DemoEntry 	Endp	
DemoCodeSegLen		equ	$ - g_DemoCodeSeg
g_DemoCodeSeg	Ends
;============================================================================
	;16位段, 由实模式跳入
;============================================================================
g_Code16Seg	Segment	use16
_GoToProtect	Proc					;返回实模式
	mov	ax, NormalSelector			
	mov	fs, ax					;规范选择子
	mov	es, ax
	mov	ds, ax
	mov	ss, ax
	mov	gs, ax	
	
	mov	eax, cr0				;关PE位, 进入实模式
	and	al, 0feh
	mov	cr0, eax
	
	;刷新段选择子缓冲区, 退回实模式
	Jmp16	<seg StartCodeSeg >, < offset _RealProtect >
_GoToProtect 	Endp
;----------------------------------------------------------------------------
_ProtectEntry	Proc					;实模式跳入入口
	
	mov	ax, g_StackSelector
	mov	ss, ax
	mov	esp, Stack_Len				;设置堆栈
	
	mov	ax, g_DataSelector			;设置源数据段
	mov	ds, ax
	
	mov	ax, g_VideoSelector
	mov	es, ax					;设置目标ES
;----------------------------------------------------------------------------	
	;这个比较给力, 从16位代码段跳转到32位代码段
	CALL16	<g_DemoCodeSelector>, _DemoEntry	;演示特权指令
;----------------------------------------------------------------------------	
	call	_GoToProtect				;返回保护模式
_ProtectEntry 	Endp	
g_Code16Seg	Ends
;============================================================================
StartCodeSeg	Segment use16		;实模式启动代码
_InitGdt	Proc	uses es				;初始化全局描述符表
	local	_fGdtPtr:fword
	
	xor	eax, eax
	mov	ax, GdtSeg
	mov	es, ax					;es-->全局描述符表
;----------------------------------------------------------------------------
	shl	eax, 4					
	mov	dword ptr es:[_fGdtPtr+2], eax		;初始化VGDT描述符
	mov	word ptr es:[_fGdtPtr], GDTLen-1	;GDT长度
;----------------------------------------------------------------------------	
	xor	eax, eax
	mov	ax, g_Code16Seg				;初始化十六位的代码段
	shl	eax, 4
	mov	word ptr es:[g_CodeTempDesc+2], ax	;段基址低位
	shr	eax, 16
	mov	byte ptr es:[g_CodeTempDesc+4], al	;段基址高地址低位
	mov	byte ptr es:[g_CodeTempDesc+7], ah	;段基址高地址高位
;----------------------------------------------------------------------------	
	xor	eax, eax
	mov	ax, g_DemoCodeSeg			;初始化32位的演示代码段
	shl	eax, 4
	mov	word ptr es:[g_DemoCodeDesc+2], ax	
	shr	eax, 16
	mov	byte ptr es:[g_DemoCodeDesc+4], al	
	mov	byte ptr es:[g_DemoCodeDesc+7], ah	
;----------------------------------------------------------------------------	
	xor	eax, eax
	mov	ax, Stack0Seg				;初始化32位的演示堆栈
	shl	eax, 4
	mov	word ptr es:[g_StackDesc+2], ax	
	shr	eax, 16
	mov	byte ptr es:[g_StackDesc+4], al	
	mov	byte ptr es:[g_StackDesc+7], ah	
;----------------------------------------------------------------------------
	lgdt	fword ptr ss:[_fGdtPtr]			;装载GDT
	ret
_InitGdt 	Endp
;============================================================================
;;将al中的数字转成ASCII加上显示属性并在EAX中返回
_HexToAscii     Proc     uses ebx
 
        mov     bl, al
 
        and     al, 0fh
        add     al, 90h
        daa
        adc     al, 40h
        daa
        mov     ah, 0ah
        shl     eax, 16                                 ;转换低位          
 
        mov     al, bl
        shr     al, 4
        and     al, 0fh
        add     al, 90h
        daa
        adc     al, 40h                                 ;将高位转成ASCII
        daa
        mov     ah, 0ah                                 ;属性
        ret
_HexToAscii     Endp
;----------------------------------------------------------------------------
	;es:si-->字符串返回缓冲区, eax要转换的dword
_DwordToHex	Proc	uses ebx 			;dword转成字符串, 
	
	mov	ebx, eax
	shr	eax, 16 + 8
	call	_HexToAscii
	stosd
	mov	eax, ebx
	shr	eax, 16
	call	_HexToAscii
	stosd
	mov	eax, ebx
	shr	eax, 8
	call	_HexToAscii
	stosd
	mov	eax, ebx
	call	_HexToAscii
	stosd
	ret
_DwordToHex 	Endp
;----------------------------------------------------------------------------
	;es:di-->字符串写入的缓冲区, ax要转换的word
_WordToHex	Proc	uses bx
	mov	bx, ax
	shr	ax, 8
	call	_HexToAscii
	stosd
	mov	ax, bx
	call	_HexToAscii
	stosd
	ret
_WordToHex 	Endp
;============================================================================
        ;换行, _dwValue传递当前的行列号, 计算好在Eax中返回下一行开始
_PrintLn        Proc    uses ebx  _dwValue:dword
        mov     eax, _dwValue
        mov     bl, 160
        div     bl
        and     eax, 0FFh
        inc     eax
        mov     bl, 160
        mul     bl
        ret
_PrintLn        Endp
;============================================================================
	;ds->si要显示的字符串, es->di要显示的位置 
_ShowMsg	Proc					;显示信息到屏幕上		
@@:	lodsb
	mov	ah, 0ch
	stosw
	loop	@b
	ret	
_ShowMsg 	Endp
;============================================================================
_ShowRegister	Proc	uses es	ds			;演示实模式下可以使用的特权指令
	local	_fRegBuf:fword
	local	_wBuf:word
	
	sgdt	fword ptr ss:[_fRegBuf]			;读取实模式下的GDT
	mov	edi, 80 * 2 * 5 + 5 * 2			;5行5列
	mov	ax, 0b800h
	mov	es, ax
	cld
;----------------------------------------------------------------------------
	;显示GDT
	mov	ax, GdtSeg
	mov	ds, ax	
	lea	si, SzGdtMsg
	mov	cx, sizeof SzGdtMsg
	call	_ShowMsg
	
	mov	eax, dword ptr ss:[_fRegBuf+2]
	call	_DwordToHex				;显示GDT基址
	
	lea	si, SzLimitMsg
	mov	cx, sizeof SzLimitMsg
	call	_ShowMsg
	
	mov	ax, word ptr ss:[_fRegBuf]
	call	_WordToHex				;显示GDT限长
;----------------------------------------------------------------------------
	Invoke	_PrintLn, edi				;换行
	mov	edi, eax
	add	edi, 5 * 2
	
	;显示IDT
	sidt	fword ptr ss:[_fRegBuf]			;读取实模式下的IDT
	lea	si, SzIdtMsg
	mov	cx, sizeof SzIdtMsg
	call	_ShowMsg
	
	mov	eax, dword ptr ss:[_fRegBuf+2]
	call	_DwordToHex				;显示GDT基址
	
	lea	si, SzLimitMsg
	mov	cx, sizeof SzLimitMsg
	call	_ShowMsg
	
	mov	ax, word ptr ss:[_fRegBuf]
	call	_WordToHex				;显示GDT限长
;----------------------------------------------------------------------------
	smsw	word ptr [_wBuf]			;显示CR0的低16位
	
	Invoke	_PrintLn, edi				;换行
	mov	edi, eax
	add	edi, 5 * 2
	
	lea	si, SzMswMsg
	mov	cx, sizeof SzMswMsg
	call	_ShowMsg
	
	mov	ax, word ptr ss:[_wBuf]
	call	_WordToHex				;显示MSW
;----------------------------------------------------------------------------						
	ret
_ShowRegister 	Endp
;============================================================================
Jmain		Proc			;启动入口
	call	_ShowRegister				;显示实模式的关键寄存器
	call	_InitGdt				;初始化GDT全局描述符
;----------------------------------------------------------------------------
	mov	ax, GdtSeg
	mov	ds, ax
	
	mov	ds:[_RegSs], ss
	mov	ds:[_RegSp], sp				;保存SS:SP
	
	_EnableA20					;关中断开A20地址线
	mov	eax, cr0
	or	eax, 1
	mov	cr0, eax				;开启分段, 进入保护模式
;----------------------------------------------------------------------------
	Jmp16	g_CodeTempSelector, <offset _ProtectEntry>;跳入保护模式
	
Jmain 		Endp
;----------------------------------------------------------------------------
_RealProtect	Proc                                    ;返回保护模式
 
        mov     ax, GdtSeg
        mov     ds, ax
        lss	sp, dword ptr ds:[_RegSp]		;恢复SS:SP
      
        _DisableA20                                     ;关A20地址线, 开中断
        sti
        mov     ax, 4c00h
        int     21h
_RealProtect	Endp
StartCodeSeg	Ends
End	Jmain