386-3.txt
上传用户:gb3576
上传日期:2007-01-03
资源大小:24k
文件大小:12k
- ┌┐┌┐∞
- 【 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 -- 软蛀 -- │
- └───────────────────────────────────┘