忆杰的博客

忆杰的博客

保护模式3:任务内无特权级转移

在前面那篇文章中已经有从16位代码到32位代码的转换, 其实那就是任务内无特权级转换, 那么对于控制转移还没有特别提到. 这篇和下一篇就专门讲控制转移的…

代码实现逻辑: 首先在dos下初始化GDT和LDT表, 然后装载GDTR, 从实模式切换到保护模式,当然是关中断的.. 转移到保护模式后, 就装在LDTR, 装在堆栈. 装载各个选择子.. 都是指向LDT中的.. 描述符.. GDT只有进入和返回用到..然后在gdt中利用jmp指令转移到LDT中的一个16位段, 在16位段显示了一个字符串.. 然后就跳到LDT中的32位段. 大部分工作都在LDT的32位段中搞的..

原来写代码一直比较倾向于MASM9. 这里如果我们跳到32位段以后. 只要自己不切换段, 那么写代码就和Win32没有什么区别了, 只是没有那么多API给调用. 全部自己搞..

在这个32位段中做的事情说起来也很简单, 就是显示所有LDT局部描述符表中的描述符.. 我总共定义了6个段. 显示这6个段的基址偏远. 和属性. 只是代码有点多了. 逻辑倒是比较简单!

因为代码中我们要把LDT当成数据段来访问. 所以想要访问LDT, 或者GDT那么必须使用别名技术.

因为没有办法把系统段描述符的选择子装载到段寄存器的.

有图有真相, 下面这个截图就是在Bochs中跑起来的样子, 是不是很有成就感啊.. 呵呵搞定这个LDT和转移, 就可以开始向调用门什么的开始进军了.. 太暴力了!!  还是上代码吧.. 当然惯例. 贴一个下载地址.. 
        http://www.joenchen.com/JoenTools/Protect3.rar

 

  

;============================================================================
	;使用jmp, Call实现任务内无特权级变换实例(使用了LDT)
	;编译选项请参见 makefile  TAB = 8
;============================================================================
	.686p
	Include pm.inc
	option casemap:none

Stack_Len	equ	1024	;堆栈大小
;============================================================================
	;全局描述符
GdtDataSeg	Segment	use16
GDT	label	byte				;全局描述符表
Dummy:		Descriptor	0, 0, 0		;空的描述符
;----------------------------------------------------------------------------
;				;段基址	  	;段界限		;属性
Normal:		Descriptor	0, 		0ffffh,		DA_DRW 	;规范段描述符
g_Code16Desc:	Descriptor 	0,		0ffffh, 	DA_C	;非一致代码段16位
g_LdtTable:	Descriptor	0,	 	LdtLen-1, 	DA_LDT	;局部描述符表段的描述符

GDTLen	equ	$ - GDT				;GDT长度
GDT_Ptr		word	GDTLen-1		;VGDT
		dword	0
;----------------------------------------------------------------------------
NormalSelector	equ	Normal 		- GDT				;规范段选择子
Code16Selector	equ	g_Code16Desc 	- GDT				;代码段选择子
LdtSelector	equ	g_LdtTable	- GDT				;LDT段的选择子
;----------------------------------------------------------------------------
_RegSp		word	?			;用于保存SS:SP
_RegSs		word	?
GdtDataSeg	Ends
;============================================================================
LdtDataSeg	Segment	 use16			;局部描述符表
LDT	label	byte
				;段基址	  	;段界限		;属性
L_Code16Desc:	Descriptor 	0,	L_Code16SegLen, 	DA_C	;非一致代码段16位
L_Code32Desc:	Descriptor 	0,	L_Code32SegLen, DA_C + DA_32  	;非一致代码段32位
L_VideoDesc:	Descriptor	0b8000h,	0ffffh, 	DA_DRW	;显存段(可读写)
L_AliasDesc:	Descriptor	0,    	      	LdtLen-1,DA_DRW or DA_DPL3;LDT别名段(可读写, 别名技术)
L_DataDesc:	Descriptor	0, 		0ffffh, 	DA_DR	;数据段, 可读
L_StackDesc:	Descriptor	0, 		Stack_Len-1, 	DA_DRW	;堆栈段

LdtLen 		= $ - LDT
LdtCount	= ( $ - LDT ) / 8					;LDT描述符数量除以8是因为一个描述符8字节
;----------------------------------------------------------------------------
L_Code16Selector	equ	L_Code16Desc - LDT + SA_TIL		;LDT16位代码段选择子这里需要+4表示TI位为1
L_Code32Selector	equ	L_Code32Desc - LDT + SA_TIL		;LDT32位代码段
L_VideoSelector		equ	L_VideoDesc  - LDT + SA_TIL		;LDT视频段选择子
L_AliasSelector		equ	L_AliasDesc  - LDT + SA_TIL + SA_RPL3	;LDT别名数据段, 3环可访问
L_DataSelector		equ	L_DataDesc   - LDT + SA_TIL		;LDT数据段选择子
L_StackSelector		equ	L_StackDesc  - LDT + SA_TIL		;LDT堆栈段选择子
;----------------------------------------------------------------------------
LdtDataSeg	Ends

;----------------------------------------------------------------------------
L_StackSeg	Segment	use16				;保护模式下的堆栈
	byte	Stack_Len dup(0)
L_StackSeg	Ends
;----------------------------------------------------------------------------
L_DataSeg	Segment	use16				;16位数据段
	SzPrompt	byte	"Show LDT Segment Information. Protect By Joen!", 0
	SzBase		byte	"LDT Base:    ", "LDT Limit:    ", "LDT Attribute:    ", 0
L_DataSeg	Ends
;============================================================================
	;局部描述符中的16位段
;============================================================================
L_Code16Seg	Segment	use16

_ShowMessage	Proc	far	uses bx si		;显示一条信息

	lea	si, SzPrompt
	mov	di, 5*80*2+5*2				;5行5列
	mov	cx, sizeof SzPrompt
	cld
;----------------------------------------------------------------------------
	;显示一条提示信息
@@:	lodsb
	mov	ah, 07h					;属性
	stosw
	loop	@b

	ret
_ShowMessage 	Endp

L_Code16SegLen	= $ - L_Code16Seg			;段长度
L_Code16Seg	Ends

;============================================================================
	;局部描述符中的32位段
;============================================================================
L_Code32Seg	Segment	use32

;;将al中的数字转成ASCII加上显示属性并在EAX中返回
_HexToAscii	Proc 	 uses ebx 

	mov	bl, al

	and	al, 0fh
	add	al, 90h
	daa
	adc	al, 40h
	daa
	mov	ah, 7h
	shl	eax, 16					;转换低位		

	mov	al, bl
	shr	al, 4
	and	al, 0fh
	add	al, 90h
	daa
	adc	al, 40h					;将高位转成ASCII
	daa
	mov	ah, 07h					;属性

	ret
_HexToAscii 	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
;============================================================================
	;显示一条信息_lpStr:字符串首地址
	;_dwXY开始显示地址
;============================================================================
_PrintMessage	Proc	uses esi edi  _lpStr:dword, _dwXY:dword

	mov	esi, _lpStr
	xor	ecx, ecx
;----------------------------------------------------------------------------
@@:	mov	al, byte ptr ds:[esi]
	inc	esi
	inc	ecx
	or	al, al
	jnz	@b					;ecx == 字符串长度
	dec	ecx
;----------------------------------------------------------------------------
	mov	esi, _lpStr
	mov	edi, _dwXY
@@:	lodsb
	mov	ah, 07h					;属性
	stosw
	loop	@b
	ret
_PrintMessage 	Endp

_PrintLdt	Proc	far

	lea	eax, SzBase
	Invoke	_PrintMessage, eax, 7*80*2+5*2		;第7行5列打印一个字符串
;----------------------------------------------------------------------------
	;循环遍历所有的ldt表项
	xor	esi, esi
	xor	ebx, ebx

	Invoke	_PrintLn, 7*80*2+5*2			;换行
	mov	edi, eax
	add	edi, 5 * 2				;从第5行开始显示
;----------------------------------------------------------------------------
	;打印基址
@@:	mov	al, 'x'
	mov	ah, 07h					;打印Ox
	shl	eax, 16
	mov	al, '0'
	mov	ah, 07h
	stosd

	mov	al, byte ptr fs:[esi+7]			;基址bit4
	call	_HexToAscii
	stosd
	mov	al, byte ptr fs:[esi+4]			;基址bit2
	call	_HexToAscii
	stosd
	mov	al, byte ptr fs:[esi+3]			;基址bit1
	call	_HexToAscii
	stosd
	mov	al, byte ptr fs:[esi+2]			;基址bit0
	call	_HexToAscii
	stosd
;----------------------------------------------------------------------------
	;转换限长
	add	edi, 3 * 2				;再过来5个格子
	mov	al, 'x'
	mov	ah, 07h					;打印Ox
	shl	eax, 16
	mov	al, '0'
	mov	ah, 07h
	stosd

	mov	al, byte ptr fs:[esi+6]			;基址的高4位
	and	al, 0fh
	call	_HexToAscii
	shr	eax, 16
	stosw

	mov	al, byte ptr fs:[esi+1]			;限长高位
	call	_HexToAscii
	stosd		

	mov	al, byte ptr fs:[esi]			;限长低位
	call	_HexToAscii
	stosd
;----------------------------------------------------------------------------
	;转换属性
	add	edi, 7 * 2				;再过来7个格子
	mov	al, 'x'
	mov	ah, 07h					;打印Ox
	shl	eax, 16
	mov	al, '0'
	mov	ah, 07h
	stosd

	mov	al, byte ptr fs:[esi+6]
	shr	al, 4
	call	_HexToAscii				;转换属性高位
	shr	eax, 16
	stosw

	mov	al, byte ptr fs:[esi+5]			;转换属性低位
	call	_HexToAscii
	stosd
;----------------------------------------------------------------------------
	Invoke	_PrintLn, edi
	mov	edi, eax				;再换行
	add	edi, 5 * 2				;从第5行开始显示
	add	esi, 8					;描述符的大小
	inc	ebx
;----------------------------------------------------------------------------
	cmp	ebx, LdtCount				;遍历完所有的LDT描述符么?
	jb	@b
	Jmp32	g_Code16Desc, _LdtRet			;32位段->16位段

_PrintLdt 	Endp

L_Code32SegLen	 = $ - L_Code32Seg			;段长度
L_Code32Seg_Entry= _PrintLdt - L_Code32Seg		;定义段内入口
L_Code32Seg	Ends

;============================================================================
	;16位段, 由实模式跳入
;============================================================================
g_Code16Seg	Segment	use16

_Entry:
;----------------------------------------------------------------------------
	mov	ax, LdtSelector
	lldt	ax					;装载ldt	

	mov	ax, L_StackSelector
	mov	ss, ax
	mov	sp, Stack_Len				;初始化堆栈

	mov	ax, L_DataSelector
	mov	ds, ax					;初始化数据段

	mov	ax, L_VideoSelector
	mov	es, ax					;es-->视频段

	mov	ax, L_AliasSelector
	mov	fs, ax					;fs-->Ldt别名段

	;CALLLDT中的16位段
	CALL16	L_Code16Selector, < _ShowMessage >	;显示一个字符串

	;16位段->32位段
	Jmp16	L_Code32Selector, < L_Code32Seg_Entry >	;打印LDT段信息
;----------------------------------------------------------------------------
	;准备退回实模式
_LdtRet:mov	ax, NormalSelector
	mov	fs, ax					;规范选择子
	mov	es, ax
	mov	ds, ax
	mov	ss, ax	

	mov	eax, cr0				;关PE位, 进入实模式
	and	al, 0feh
	mov	cr0, eax

	;刷新段选择子缓冲区, 退回实模式
	Jmp16	<seg StartCode >, < offset _RetProtect >
g_Code16Seg	Ends
;============================================================================
	;这里是代码入口, 由此跳入保护模式
;============================================================================
StartCode	Segment	use16
_InitGdt	Proc	uses es				;初始化全局描述符表

	xor	eax, eax
	mov	ax, GdtDataSeg
	mov	es, ax					;es-->全局描述符表
;----------------------------------------------------------------------------
	shl	eax, 4
	add	eax, offset GDT
	mov	dword ptr es:[GDT_Ptr+2], eax		;初始化VGDT描述符
;----------------------------------------------------------------------------
	mov	ax, g_Code16Seg				;初始化十六位的代码段
	shl	eax, 4
	mov	word ptr es:[g_Code16Desc+2], ax	;段基址低位
	shr	eax, 16
	mov	byte ptr es:[g_Code16Desc+4], al	;段基址高地址低位
	mov	byte ptr es:[g_Code16Desc+7], ah	;段基址高地址高位
;----------------------------------------------------------------------------
	mov	ax, LdtDataSeg				;初始化LDT描述符
	shl	eax, 4
	mov	word ptr es:[g_LdtTable+2], ax		;段基址低位
	shr	eax, 16
	mov	byte ptr es:[g_LdtTable+4], al		;段基址高地址低位
	mov	byte ptr es:[g_LdtTable+7], ah		;段基址高地址高位
;----------------------------------------------------------------------------
	lgdt	fword ptr es:[GDT_Ptr]			;装载GDT
	ret
_InitGdt 	Endp
;----------------------------------------------------------------------------
_InitLdt	Proc					;初始化局部描述符

	xor	eax, eax
	mov	ax, LdtDataSeg				;es-->局部描述符表
	mov	es, ax
;----------------------------------------------------------------------------
	mov	ax, L_Code16Seg				;初始化LGT中的16位代码段
	shl	eax, 4
	mov	word ptr es:[L_Code16Desc+2], ax	;段基址的低地址
	shr	eax, 16
	mov	byte ptr es:[L_Code16Desc+4], al	;段基址高地址低位
	mov	byte ptr es:[L_Code16Desc+7], ah	;段基址的高地址高位
;----------------------------------------------------------------------------
	mov	ax, L_Code32Seg				;初始化LGT中的32位代码段
	shl	eax, 4
	mov	word ptr es:[L_Code32Desc+2], ax
	shr	eax, 16
	mov	byte ptr es:[L_Code32Desc+4], al
	mov	byte ptr es:[L_Code32Desc+7], ah
;----------------------------------------------------------------------------
	mov	ax, L_DataSeg				;初始化LGT中的16位只读数据段
	shl	eax, 4
	mov	word ptr es:[L_DataDesc+2], ax
	shr	eax, 16
	mov	byte ptr es:[L_DataDesc+4], al
	mov	byte ptr es:[L_DataDesc+7], ah
;----------------------------------------------------------------------------
	mov	ax, LdtDataSeg				;初始化LGD的别名段
	shl	eax, 4
	mov	word ptr es:[L_AliasDesc+2], ax
	shr	eax, 16
	mov	byte ptr es:[L_AliasDesc+4], al
	mov	byte ptr es:[L_AliasDesc+7], ah
;----------------------------------------------------------------------------
	mov	ax, L_StackSeg				;初始化LGT中的16位可读写堆栈段
	shl	eax, 4
	mov	word ptr es:[L_StackDesc+2], ax
	shr	eax, 16
	mov	byte ptr es:[L_StackDesc+4], al
	mov	byte ptr es:[L_StackDesc+7], ah
;----------------------------------------------------------------------------
	ret
_InitLdt 	Endp
Jmain		Proc					;程序入口

	call	_InitGdt				;初始化全局描述符表
	call	_InitLdt				;初始化局部描述符表

	mov	ax, GdtDataSeg
	mov	ds, ax

	push	ss
	pop	ds:[_RegSs]
	push	sp
	pop	ds:[_RegSp]				;保存SS:SP

	cli
	_EnableA20					;关中断开A20地址线

	mov	eax, cr0
	or	eax, 1
	mov	cr0, eax				;开启分段, 进入保护模式
;----------------------------------------------------------------------------
	Jmp16	Code16Selector, <offset _Entry>
_RetProtect::						;退回保护模式
;----------------------------------------------------------------------------
	mov	ax, GdtDataSeg
	mov	ds, ax
	mov	ax, ds:[ _RegSs ]			;恢复SS:SP
	mov	ss, ax
	mov	ax, ds:[ _RegSp ]
	mov	sp, ax

	_DisableA20					;关A20地址线, 开中断
	sti
	mov	ax, 4c00h
	int	21h
Jmain		Endp
StartCode	Ends
End		Jmain

 

发表评论


Warning: Undefined variable $user_ID in /www/wwwroot/joenchen.com/wp-content/themes/agan/comments.php on line 66

您必须登录 才能进行评论。