发信人: Reinhard.bbs@bbs.sjtu.edu.cn (摘星), 信区: cnhacker
标 题: 80386 保护模式简介 -3 by 软蛀 -- Alex 整理(转寄)
发信站: 饮水思源站 (Fri Apr 25 20:38:59 1997)
转信站: Lilac!ustcnews!ustcnews!sjtunews!sjtubbs
出 处: bbs.sjtu.edu.cn
┌┐┌┐∞
【 80386 保护模式简介三 】 ┘└┘└┘
==========================================================================
前言:
前面两集主要是要告诉各位有关 IDT.GDT 的用法 ,虽然这样已经可以简单的进
入保护模式 ,但是它还不足以让你撰写程式 ,因此笔者还必需往下继续叙说 ,不过再
往下讲之前 ,又有一票烦且杂的观念要说 ,本篇还是继续在"观念"上打转 ,读者千万
不要以为本篇又是「干古」 ,如果本篇不懂的话 ,後面的精彩文章您大概也看不懂 ,
笔者会尽量把文章写到容易懂的范围。
--------------------------------------------------------------------------
┌————————┐
│80386 暂存器介绍│
└————————┘
80386 的暂存器除了扩充成 32 位元以外 ,亦增加了许多新的暂存器 ,除了一般
使用者暂存器(AX.BX....SI.DI)各位已经了解以外 ,也增加了系统暂存器、以及扩充
的旗标 暂存器....等等。
A.使用者暂存器 → EAX.EBX.ECX.EDX.ESI,EDI.EBP.ESP
B.指令指标暂存器 → CS.EIP 两个暂存器
C.区段暂存器 → CS.SS.DS.ES.FS.GS
虽然 80386 已经进入 32 位元时代 ,但是这几个暂存器仍是 16 位元的 ,且多
了 FS.GS 两个暂存器 ,这两个暂存器并无特殊意义 ,各位可以把它当做 DS.ES
来看待。
D.系统暂存器
A. 控制暂存器:包含 CR0.CR2.CR3 三个 ,各位可能看到漏了一个 CR1 ,原因是
386.486.586 都没有此暂存器
B. 除错暂存器:包含 DR0.DR1.DR2.DR3.DR6.DR7 共六个 ,也是漏了 DR4.DR5 两
个 ,原因同上
C. 保护模式分段控制:IDT.GDT.LDT.TR
--------------------------------------------------------------------------
┌————┐
│工作切换│
└————┘
当您设定某些系统暂存器以後 ,电脑并不会马上反应所设定的工作 ,必需透过工
作切换的动作才会起动 ,这个工作切换很难难用文字表达 ,笔者认为工作切换就是等
级切换的动作。可造成工作切换的指令包含 INT_X 、JMP TSS区段...等 ,其中INT_X
是指在 V86 下的程式若发生中断 ,电脑会自动切换至保护模式 ,并呼叫保护模式下的
中断处理程式 ,再由保护模式下的程式决定是否呼叫原来 V86 下的中断向量表 ,而
这切换到保护模式、再切回 V86 下 , 共发生两次工作切换......
┌——┐
│等级│
└——┘
保护模式下 ,等级共有 0.1.2.3 四个等级 ,其中第0级等级最高 ,第3级最低 ,
而0级因为是最高等级 ,因此也有人称为「特权等级」 ,而应用程式的等级为多少呢?
这表示在 EFLAG 的 IOPL (BIT12.13) 里 ,在 V86 下的等级多半是最低的第3级 ,所
以此值为 '11'。
或许各位会认为自己去修改这个旗标将自己的等级调高就好了 ,事实上改好後还
要经过工作切换的动作 ,等级才能被修改 ,而经过工作切换的动作後 ,你的程式控制
权将转交给别人 ;再简单的说 ,发生 INT_X 时 ,电脑会将等级切换成最高等级(事实
上是由中断表上决定的) ,并进入保护模式 ,之後保护模式的程式再来决定将使用者的
EFLAG 切成什麽等级 ,然後再 IRETD切回 V86 ,於是应用程式根本抢不过最早进入保
护模式的家伙。(这样你有办法在V86下抢到最高等级吗....不可能嘛)
等级的高低可以决定自己有多少控制权 ,例如等级最高的人才可以读写系统暂存
器 ,其馀的人想读写系统暂存器都会发生 General Protection Error 0D ,你可以把
它想像成等级不够 ,却要读取系统资源 ,会发生 INT_0D ,而原本这行指令将不会被
执行 ,而堆叠里所摆的 EIP 值也停在这行上面 ,如果 INT_0D 的处理程式不去跳过
这个指令 ,则会永远停在这个指令里(形同当机)。
在 V86 下发生中断时 ,会自动 PUSH EIP.CS.EFLAG.ESP.SS......数个暂存器 ,
并自动将 SS.ESP 的值替换 ,以免发生中断时 ,会动用到 V86 的堆叠 ,可是如果发
生的是 General Protection Error(俗称异常),则会在 PUSH EIP 之前再多摆入一
个DWORD 的错误代码 ,如果您的程式在 IRETD 前不减去这个可能存在的错误代码 ,
则会发生不可预知的後果。这也是保护模式下的程式不好写的原因之一。 而SS与ESP
所替换的值 ,则是最初进入保护模式後 ,由最高等级的人决定的(摆於TSS区段)。
第二集里笔者有介绍 GDT 表 ,其中有个 93 代表可写区段 ,如果设成 89 ,则表
示此区段是 TSS 表格 ,再由 TR 暂存器来指定发生中断时 ,取用那一个区段的表格.
举例来说 ,下面是 GDT 表格
gdttab db 000h,000h,000h,000h,000h,000h,000h,000h ;00
db 0ffh,0ffh,000h,000h,000h,09bh,000h,000h ;08
db 0ffh,0ffh,000h,000h,000h,093h,08fh,000h ;10
db 0ffh,0ffh,000h,000h,000h,089h,000h,000h ;18
db 0ffh,0ffh,000h,000h,000h,089h,000h,000h ;20
db 0ffh,0ffh,000h,000h,000h,093h,000h,000h ;28
db 0ffh,007h,000h,000h,000h,093h,000h,000h ;30
db 0ffh,0ffh,000h,080h,00bh,093h,000h,000h ;38
db 0ffh,0ffh,000h,000h,000h,093h,000h,000h ;40
我们可以看到 18.20 两个 Selector 正好就是 89h ,也就是说它们俩个都可以是
TSS 描述表格 ,如果 MOV AX,0018、LTR AX ,则表示发生工作切换时 ,取用 0018 的
描述表格。
--------------------------------------------------------------------------
┌——————┐
│TSS 表格简介│
└——————┘
TSS 也有人称为「工作切换」 ,其表格设定如下 ,详情可看书比较详细。
tssltr dd 00000000h
dd 0000ff00h ;ESP
dw 0028h,0000h ;SS.0
dd 0,0,0,0,0
dw offset enter_v86,0000h ;EIP
dd 00000200h ;EFlag
dd 0,0,0,0
dd 0000ff00h ;ESP
dd 0,0,0
dw 0010h,0000h ;ES.0
dw 0008h,0000h ;CS.0
dw 0028h,0000h ;SS.0
dw 0010h,0000h ;DS,0
dw 0010h,0000h ;FS.0
dw 0010h,0000h ;GS.0
dw 0000h,0000h ;LDT.0
dw 0000h,0068h ;0.IOMAP起点
db 1000h dup (0) ;4K IOMAP 表
dw 0ffffh
如果您的程式使用 JMP XXXX:YYYYYYYY 的方式跳到本区节的话 ,原本指定的
YYYYYYYY 将无用途 ,因为所有的暂存器将被替换成此表格的数值(含CS.EIP) ,并
完成等级切换的动作。
--------------------------------------------------------------------------
┌———————┐
│进入 V86 模式│
└———————┘
cli
lgdt fword ptr cs:gdtadds
lidt fword ptr cs:idtadds
mov eax,cr0
or al,01h
mov cr0,eax
mov bx,0018h
ltr bx ;发生工作切换时 ,SS:ESP 将参考 0018 的区段表格
jmp 0020h:0000h ;进入工作切换 ,会跳到此表格内指定的 CS:EIP
(LTR.JMP 不可指向同一表格)
enter_v86 : ;假设您已将 CS:EIP 指向此处继续执行
xor eax,eax
mov ax,code
push eax ;GS
push eax ;FS
push eax ;DS
push eax ;ES
push eax ;SS
mov ax,0f000h
push eax ;ESP
mov eax,00023000h ;设定VM=1 等级=3
push eax ;Eflag
xor eax,eax
mov ax,code
push eax ;CS
mov ax,offset return_dos
push eax ;EIP
clts ;将 387 切换成 32 位元模式
iretd ;回到 V86 (共弹出24h BYTE)
紧接著就程式回到 V86 下继续执行著...
--------------------------------------------------------------------------
┌————————┐
│中断向量表的处理│
└————————┘
在保护模式下 ,产生中断後 ,会切回保护模式 ,於是您必需去呼叫原先 V86 下
的中断表 ,以便让程式能够正确执行。
V86 下发生中断後 ,CPU 会取出 LTR 所设定 SS:ESP 值 ,然後将 V86 下的众
多暂存器暂存於此 ,不过因为 CPU 已变成 32 位元模式 ,所以堆叠内的 SP 值会被
减 12 byte (原本是6byte ,用以摆放 IP.CS.FLAG) ,且堆叠内的EIP值会指向 V86
下的 INT_X 的下一行 ,因此你必需先将 V86 下的 SP 值加 6 byte ,并修改 V86 下
的 SS:SP 里的内容为 INT_X 的下一行 ,然後将保护模式下的堆叠 CS:EIP 值指向原
V86 下的中断位址 ,这样才可以带动 V86 下的中断表。
底下仅列出部份中断的处理方式....您必需处理 256 个中断表。
new_20 :
push 0020h
jmp int_emu
new_21 :
push 0021h
jmp int_emu
new_22 :
push 0022h
jmp int_emu
new_23 :
push 0023h
jmp int_emu
int_emu :
push bp
mov bp,sp
add bp,04h
push eax
push ebx
mov ax,0010h ;
mov ds,ax ;(Selector 0010h 的 Base=0)
mov ax,ss:[bp+0ch] ;
sub ax,06h ;改V86的SP-6
mov ss:[bp+0ch],ax ;
xor eax,eax ;
xor ebx,ebx ;修改V86下的SS:SP ,帮它摆入
mov ax,ss:[bp+10h] ;INT_X 後的下一行位址 ,供V86
shl eax,04h ;下的程式IRET返回INT_X的下一行用
mov bx,ss:[bp+0ch] ;
add ebx,eax ;
mov ax,ss:[bp+00h] ;
mov ds:[ebx],ax ;
mov ax,ss:[bp+04h] ;
mov ds:[ebx+02h],ax ;
mov ax,ss:[bp+08h] ;
mov ds:[ebx+04h],ax ;
nop
xor ebx,ebx ;
mov bx,ss:[bp-02h] ;
shl ebx,02h ;
mov ax,ds:[ebx] ;IRETD 後到V86中断表所指的位址继续执行
mov ss:[bp+00h],ax ;(查 0000:0000 的中断表)
mov ax,ds:[ebx+02h] ;
mov ss:[bp+04h],ax ;
mov eax,ss:[bp+08h]
or eax,00032000h ;等级=3 VM=1
and eax,0fffffeffh ;关闭'T'旗标
mov ss:[bp+08h],eax
pop ebx
pop eax
pop bp
add sp,02h
iretd
--------------------------------------------------------------------------
┌——————┐
│相容性的处理│
└——————┘
或许您曾经在挂入 QEMM386、EMM386 之後 ,在 V86 下执行 MOV EAX,CR0 的指
令 ,但是前面笔者提到读写系统暂存器必需在最高等级才可执行 ,为什麽 User 仍
可在最低等级下执行本命令呢 ? 底下是欺骗方式。
(User) V86 下执行 MOV EAX,CR0
↓
发生 General Protection 0D
CPU 自动切入保护模式 ,并执行 INT_0D 的处理程式
(堆叠里多储存了错误代码 DWORD)
↓
(EMM) 检查发生错误的原因
读取 EAX,CR0 (因此时已是最高等级 ,本行可以正确执行)
↓
(EMM) 修改堆叠内的 EIP 值 ,指向下一行指令
↓
(EMM) 修改使用者等级 3 / 设定 VM 旗标等於 1
↓
(EMM) ESP 值扣掉错误代码 4byte
↓
(EMM) IRETD 切回 V86
↓
(User) 使用者取得 EAX 的数值
由於程式有一大半在保护模式下执行 ,所以使用者根本感觉不到 ,只知道自己真
的读到系统暂存器。这便是 EMM 系的欺骗手段。
再举例来说 ,笔者所写的 DEBUGOS ,在这个系统下您可以执行 MOV EAX,CR0 ,就
是因为笔者有加以处理 ,可是笔者检查保护模式错误原因里并没有处理 MOV EBX,CR0
,於是在这系统下 ,您就没办法执行本命令了 ,您可以试试看。
本来标准的程式是不会在 V86 下读写系统暂存器 ,可是确实也有不正常的程式
是这样搞的 ,例如倚天中文会 MOV EAX,CR3 ,或是一些保护程式会写入除错暂存器
(DRx)。所以为了相容性 ,这些最好做进去。
--------------------------------------------------------------------------
┌——————┐
│拦 I/O 能力│
└——————┘
在进入保护模式後 ,您可以在 IOMAP 里设定某些位元 ,用以管理 I/O 埠 ,每个
Bit 表示一个埠 ,4K=32768埠 ,当您设定此位元後 ,等级低的人读写此埠就会发生
General Protection Error 0D ,然後你就可以加以处理啦 ,不过 I/O MAP 只能设定
为读写时发生异常 ,无法单独设定为仅读取才发生或仅写入才发生 ,因此拦 I/O 的
人要自己去辨认原因。这点也是很麻烦的。
--------------------------------------------------------------------------
切入 V86 後 ,还有很多问题要处理 ,包含上面提到的部份 ,和 HIMEM.SYS 相容
啦 ,这些问题有待您自己去寻找解决办法。
有关保护模式的部份笔者只能介绍到此 ,再下去更深的理论我不会解释 ,也掰不
出来 ,不过您如果会切入 V86 ,自然也能够写在保护模式下执行的程式才对。如有问
题再来信。
DEBUGOS 这个小软体已经摆於 KPEMU300.ZIP 内了 ,这是一套模拟 KeyPro 的小
软体。
┌———————————————————————————————————┐
│ Soft Bugger 软体蛀虫 90:90/2 软体新技术的实行者 │
│ BBS:02-5955461 24HR ID:Werong Ho -- 软蛀 -- │
└———————————————————————————————————┘
--
沉默的人
Reinhard Young
※ 来源:·饮水思源站 bbs.sjtu.edu.cn·[FROM: 202.96.212.29]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:208.181毫秒