发信人: Reinhard.bbs@bbs.sjtu.edu.cn (摘星), 信区: cnhacker
标 题: 80386 保护模式简介 -4 by 软蛀 -- Alex 整理(转寄)
发信站: 饮水思源站 (Fri Apr 25 20:41:49 1997)
转信站: Lilac!ustcnews!ustcnews!sjtunews!sjtubbs
出 处: bbs.sjtu.edu.cn
┌┐┌┐∞
【 80386 保护模式简介四 】 ┘└┘└┘
==========================================================================
前言:
本集的内容主要是由第三集改进解释的方式 ,重新再介绍一次 V86 拦 I/O 的
动作 ,因为好像有不少人对於第三集的解释方式一知半解....可能是我写的还不是
很好吧 ,所以重写一次。
--------------------------------------------------------------------------
┌————————┐
│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
注:自 586 起新增 CR4.DR4.DR5 系统暂存器
--------------------------------------------------------------------------
┌————┐
│工作切换│
└————┘
当您设定某些系统暂存器以後 ,电脑并不会马上反应所设定的工作 ,必需透过工
作切换的动作才会起动 ,这个工作切换很难用文字表达 ,笔者认为工作切换就是等级
切换的动作。可造成工作切换的指令包含 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 的
描述表格。
注一:General Protection Error 发生後会去呼叫该中断 ,但是一般产生中断只会
存入 EIP.CS.EFLAG.ESP.SS.... ,但是发生 General Protection Error 的话
堆叠会存入 错误代码.EIP.CS.EFLAG.ESP.SS.....
堆叠多存放了一个"错误代码" ,记得在切回 V86 前要将此值减去唷 !!
注二:前面说发生 GP Error #0D 等於呼叫 INT_0D ,这只能说是半对 ,原因『注一』
已说明 ,不再重复。
--------------------------------------------------------------------------
┌——————┐
│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 下产生中断後 ,电脑会自动切回保护模式 ,并从 LTR 所指定的位址取得
TSS 表格 ,然後以表格内的资料重新设定 SS.ESP ,然後把 V86 下的各暂存器值摆入
此堆叠内 ,在此需注意的是它摆放在堆叠的资料是32位元方式 ,所以对於 DS.ES....
这类16位元暂存器摆於堆叠 ,不足部份补 '0000' ,用以凑足 32Bit。
简单来说 ,在真实模式下或 V86下使用一组 SS:SP ,一但透过中断进入保护模式
後 ,原先的 SS:SP 暂存器将被置换另一组数值(定义於TSS表) ,然後再将大部份的暂
存器值摆放在这个新堆叠区内(包含SS.ESP) ,直到执行 IRETD 回到 V86 後 ,SS:ESP
暂存器值才会从原先堆叠中弹出。换句话说 ,在 V86下发生中断会使用自己的堆叠 ,
而不会破坏 V86 的堆叠区 ,这也就是为什麽像 S-ICE 除错程式执行 'T' 的命令却
不会更动 User 的堆叠资料。
存於保护模式堆叠内的 CS:EIP 会指向 V86下 "INT_X" 的下一行 ,而 SS:SP 值
却仍维持原来数值(不像以往产生中断会自动减6 ,然後堆叠内摆入 FLAG.CS.IP),因
此保护模式下处理中断的程式必需修改 V86 的 SP 值减6 ,并将 V86 的 CS.IP.FLAG
摆入 V86 的堆叠 ,最後再去查 0000:0000 的表格 ,将保护模式堆叠内的 CS:EIP 值
修改、指向此中断向量表 ,最後保护模式的程式执行 IRETD 返回 V86 後 ,跳到 V86
下的中断所指位址 ,这样便完成整个模拟 DOS 中断的效果。
PS:保护模式下堆叠会存放 EFLAG.EIP.ECS.ESP.SS...... 忘了 ,比 Real Mode 还要
多好多喔。
底下仅列出部份中断的处理方式....您必需处理 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
--------------------------------------------------------------------------
┌——————┐
│拦 I/O 能力│
└——————┘
TSS 表格内除了可定义产生工作切换後 ,SS.ESP.DS.ES....各暂存器替换值 ,也
可以开一块记忆体做 IOMAP ,这块记忆体每个 Bit 代表一个 PORT ,一般习惯是开4K
大小 (65536埠),当某位元设定为 '1' 後 ,只要不是最高等级的人去读写此埠 ,都会
发生 GP Err #0D ,当然在最低等级的 V86 程式也不例外 ,发生此错误後 ,就形同拦
到 I/O 动作了 ,紧接著透过最高等级的处理程式去判断发生错误的原因 ,例如判断
程式码是否为 『EC IN AL,DX』、『EE OUT DX,AL』 ,或是其它程式码 ,就可以分
辨发生的原因是读或写产生的 ,拦到 I/O 後 ,你是否会写骗 I/O 的程式 ?
以 S-ICE 的拦 I/O 能力为例 ,它先使用 IO-MAP 的方式去拦 I/O ,然後再判别
"EE.E4.EC.E6...." 等等程式码。
注:IOMAP 表是也是 TSS 表格的一部份。
--------------------------------------------------------------------------
┌———————┐
│相容性的处理一│ 系统暂存器的相容处理法
└———————┘
或许您曾经在挂入 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 系的欺骗手段。
本来标准的程式是不会在 V86 下读写系统暂存器 ,可是确实也有不正常的程式
是这样搞的 ,例如倚天中文会执行 MOV EAX,CR3 ,或是一些保护程式会写入除错暂存
器 (DRx)。所以为了相容性 ,这些最好做进去。
注:判别发生的原因也可以利用判断 I/O 的那种方法 ,但写起来很麻烦。
--------------------------------------------------------------------------
┌———————┐
│相容性的处理二│ HIMEM.SYS
└———————┘
HIMEM.SYS 是一个可以控制 1MB 以外记忆体的程式 ,不过之前笔者有提过 ,要
读写超过 1MB 以外的记忆体必需进入保护模式才行(据说有後门可用) ,那麽载入自
己的保护模式程式後 ,再遇到呼叫 HIMEM.SYS 去搬移 1MB 以外的记忆体 ,电脑竟然
会发生 GP Err #0D ,原来这是因为 HIMEM.SYS 在执行搬移记忆体的命令後会去呼叫
BIOS 的 AH=87h INT_15h 去搬记忆体 ,换句话说就是因为这个 BIOS 中断会进入保
护模式去搬记忆体 ,所以才会造成当机 ,因此你的保护模式介面程式必需去模拟这个
BIOS 函式 ,就可以与 HIMEM.SYS 相容了。
注:BIOS AH=87h INT_15h 会重设 GDT.IDT 表 ,然後进入保护模式去搬记忆体 ,然後
就当在 LIDT 或 LGDT 的命令上。
另外如果你的程式摆在 1MB 以上的记忆体位址去执行 ,还会有另一个问题产生 ,
不过如果你已经学会上面的这些功能 ,再尝试去写个程式去试试 ,你自然会知道
它会发生什麽问题 ,解决的办法也很简单 ,你一定会解决。
--------------------------------------------------------------------------
切入 V86 後 ,还有很多问题要处理 ,不过上面提到的两个问题如果你都能处理
的话 ,基本上就不会有其它大问题 ,等你会进入保护模式後 ,再来学习 VCPI、DPMI
就很简单了。
如果各位会切入保护模式的话 ,接下来应该是学习 VCPI 的切入方式 ,虽然有很
多书籍有介绍 ,但是要真正了解并不容易。建议各位去买套大宇出品的激斗战士、战
国策 ,它的外加保护就是切入保护模式的最佳范例 ,包含透过 VCPI、自己切286.386
保护模式 ,虽然这是不道德的行为 ,但是却是一个最佳范本。花个五百块学新知绝对
划算。
有关保护模式的部份笔者暂时介绍到此 ,下一集笔者将为您介绍虚拟记忆体 ,如
果情况允许 ,还会顺便介绍更难懂的分页机能。教各位如何写出类似 S-ICE 的 BPR
功能 ,锁定某一块记忆体的读写状态。
┌———————————————————————————————————┐
│ 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.112毫秒