荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: zzt (我命由我不由天), 信区: Program
标  题: 80386保护模式简介(1)
发信站: BBS 荔园晨风站 (Sun May 23 16:48:46 1999), 转信


发信人: mayanyi (▓▓※中坚分子※▓), 信区: Program
标  题: 80386保护模式简介(1)
发信站: 网易虚拟社区 (Wed May 19 01:39:20 1999), 站内信件

80386保护模式简介(1)

发信人: subtle (淡然处之), 信区: Dos
标  题: 80386 保护模式简介
发信站: 鼓浪听涛 (Sun May 18 16:22:09 1997)
上海交大BBS站供稿

----------------------------------------------------------------------
----
在保护模式下有很多新的名词,包含GDT.LDT.IDT以及CR0-CR3,笔者对保护模式并
不清楚,所以底下资料可能有错误。这里使用大量的线性记忆体观念,请您一定要
从头往後看,否则很可能会看不懂,且必须懂线性记忆体计算方式。

----------------------------------------------------------------------
----
【GDT介绍】
在真实模式下每个区段都等於64K,可是保护模式下每个区段的大小却是可变动的
,每个区段有多大呢,就是由GDT来决定。


您可以用SGDTCS:[BX]的方式将GDT的值读出,它的长度为6BYTE,底下是笔者写的小
程式读出。

XXXX:0000FF0F0020C000
^^^^^^^^^^^GDT表所在的线性记忆体位址
^^^^^GDT表长度+1

将此表资料读出来.
X:00C020000000000000000000-FFFF00A0C29B4000...........B.@.
X:00C02010FFFFB0DD01934000-FFFFE0B3009A0000..0]..@...`3....
X:00C02020FFFFE0B300930000-00000020C1828000..`3.......A...
X:00C0203000000020C193C000-00000020C093C000...A.@....@.@.
X:00C020400000000000924000-FFFF00800B924000......@.......@.




它所代表的意思是如下图所示:(每组8byte)

┌棗棗棗棗棗棗棗棗棗棗棗┐
1│Limitbit0-15│0byte
├棗棗棗棗棗棗棗棗棗棗棗┤
3│Basebit0-15│2
├棗棗棗棗棗┬棗棗棗棗棗棭?
5│存取权│Basebit16-23│4
├棗棗棗棗棗┼棗棗棗棗棗棭?
7│Basebit24-31│G│..│limitbit16-19│6
└棗棗棗棗棗┴棗棗棗棗棗棭?
"G"代表Limit的单位是Byte或PAGE(4K)

所以....

#0000Segmentnotpresent.
#0008Base=00C2A000Limit=0000FFFFFlags=9BUSE32Bytegranularity
#0010Base=0001DDB0Limit=0000FFFFFlags=93USE32Bytegranularity
#0018Base=0000B3E0Limit=0000FFFFFlags=9AUSE16Bytegranularity
#0020Base=0000B3E0Limit=0000FFFFFlags=93USE16Bytegranularity
#0028Base=00C12000Limit=00000000Flags=82Pagegranularity
#0030Base=00C12000Limit=00000000Flags=93USE32Pagegranularity
#0038Base=00C02000Limit=00000000Flags=93USE32Pagegranularity
#0040Base=00000000Limit=00000000Flags=92USE32Bytegranularity
#0048Base=000B8000Limit=0000FFFFFlags=92USE32Bytegranularity
#0050Base=0001F56CLimit=000007FFFlags=92USE32Bytegranularity
#0058Base=00000000Limit=00000144Flags=92USE32Pagegranularity
#0060Base=00000000Limit=00000144Flags=93USE32Pagegranularity
#0068Base=00127F48Limit=0000C32FFlags=9BUSE16Bytegranularity
#0070Base=00134278Limit=000028F7Flags=93USE16Bytegranularity
#0078Base=00000000Limit=00000000Flags=92USE16Bytegranularity
^^^^^Selector^^存取权

Base就是指这个Secector:00000000对应到线性记忆体的何处,也就是说将线性记
忆体从Base所指的地方开始长度为Limit,剪下来变成一个独立的区段,如果您在该
区段想看超过LIMIT长度的记忆体,则会发生保护模式错误...应用程式可拦截所发
生的中断适当的加以处理。注意,Limit的单位可以是byte,也可以是page(4k),由
"G"是否为1来决定

至於Selector的数值我猜想应该是被标上8的倍数吧,因为很多书都是如此介绍它


----------------------------------------------------------------------
----
【LDT介绍】
上面介绍了GDT可以设定很多个Secector,而LDT则是在这些被定义出来的Selecto
r中再切割出更小的单元。也就是说LDT的资料长度只有2BYTE,这个值直接就是指
Selector。

※这个命令必需在最高权力下才能执行,所以笔者使用386DEBUG来执行,在传统Re
alMode/V86都不能执行。

C:\>386debug386debug.exp(改过的.exp档)
000C:0002743C660F0007SLDT[EDI]
-T
-DEDI
0014:000000002800<--LDT所指的Selector为0028
根据GDT的资料查表得到下表,但是由於0028这段落禁止观看,所以我改看0030的段
落,因为它的Base是一样的。

#0028Base=00C12000Limit=00000000Flags=82Pagegranularity
#0030Base=00C12000Limit=00000000Flags=93USE32Pagegranularity

-D30:0
0030:00000000FF00F0CE09924000-31000000CA9BC000..pN..@.1...J.@.
0030:0000001031000000CA93C000-FFFF00800B9240001...J.@.......@.
0030:00000020FF00F0CE09924000-4D0090CE09924000..pN..@.M..N..@.
0030:00000030440100000093C000-0000000000924000D.....@.......@.
0030:00000040FFFF00800B924000-0000000000924000......@.......@.
0030:000000500000000000000000-0000000000000000................
0030:000000600000000000000000-0000000000000000................
0030:000000700000000000000000-0000000000000000................

-DL0
#0004Base=0009CEF0Limit=000000FFFlags=92USE32Bytegranularity
#000CBase=00CA0000Limit=00000031Flags=9BUSE32Pagegranularity
#0014Base=00CA0000Limit=00000031Flags=93USE32Pagegranularity
#001CBase=000B8000Limit=0000FFFFFlags=92USE32Bytegranularity
#0024Base=0009CEF0Limit=000000FFFlags=92USE32Bytegranularity
#002CBase=0009CE90Limit=0000004DFlags=92USE32Bytegranularity
#0034Base=00000000Limit=00000144Flags=93USE32Pagegranularity
#003CBase=00000000Limit=00000000Flags=92USE32Bytegranularity
#0044Base=000B8000Limit=0000FFFFFlags=92USE32Bytegranularity
#004CBase=00000000Limit=00000000Flags=92USE32Bytegranularity
#0054Segmentnotpresent.
#005CSegmentnotpresent.
#0064Segmentnotpresent.
#006CSegmentnotpresent.
#0074Segmentnotpresent.
#007CSegmentnotpresent.

----------------------------------------------------------------------
----
【IDT介绍】
在以往中断向量表都是用4byte来表示,但是在保护模式下则由8byte表示,至於那
几个byte表示什麽,笔者还未搞懂,底下只弄懂几个。


C:\>386debug386debug.exp(改过的.exp档)
000C:00027434660F010FSIDT[EDI]
-DEDI
0014:00000000FF076CF50100....-................
^^^^^^^^^^^线性记忆体位址
^^^^^长+1
因为该线性记忆体已对映到50:0
#0050Base=0001F56CLimit=000007FFFlags=92USE32Bytegranularity
所以:
0050:000000000034080000EE0000-0A34080000EE0000.4...n...4...n..
0050:000000101434080000EE0000-1E34080000EE0000.4...n...4...n..
0050:000000202834080000EE0000-3234080000EE0000(4...n..24...n..
0050:000000303C34080000EE0000-6C16C80F008E0000<4...n..F4...n..
0050:000000405034080000EE0000-5A34080000EE0000P4...n..Z4...n..
0050:000000506434080000EE0000-6E34080000EE0000d4...n..n4...n..
0050:000000607834080000EE0000-8234080000EE0000x4...n...4...n..
0050:000000708C34080000EE0000-9634080000EE0000.4...n...4...n..

-DI0
#0000Selector=0008Offset=00003400Flags=EE;int_0
#0001Selector=0008Offset=0000340AFlags=EE;int_1
#0002Selector=0008Offset=00003414Flags=EE;int_2
#0003Selector=0008Offset=0000341EFlags=EE;int_3
#0004Selector=0008Offset=00003428Flags=EE
#0005Selector=0008Offset=00003432Flags=EE
#0006Selector=0008Offset=0000343CFlags=EE
#0007Selector=0FC8Offset=0000166CFlags=8E;此处为Q387使用
#0008Selector=0008Offset=00003450Flags=EE
#0009Selector=0008Offset=0000345AFlags=EE
#000ASelector=0008Offset=00003464Flags=EE
#000BSelector=0008Offset=0000346EFlags=EE
#000CSelector=0008Offset=00003478Flags=EE
#000DSelector=0008Offset=00003482Flags=EE
#000ESelector=0008Offset=0000348CFlags=EE
#000FSelector=0008Offset=00003496Flags=EE

请仔细看一看这个表的对应情形,笔者故意载入Q387以便让INT_7的Selector与
众不同,让您更易判断中断表对应关系。
----------------------------------------------------------------------
----
实例解说:
底下是读取SoftICEINT_0的程式码□例:

LoadIDT
LDT=FF0712C08000所以观看0080C012的记忆体
0080C012472C180000EE0000-4C2C180000EE0000G,...□.L,...□.
0080C022512C180000EE0000-562C180000EE0000Q,...□.V,...□.
0080C0325B2C180000EE0000-602C180000EE0000[,...□.`,...□.
0080C042652C180000EE0000-6A2C180000EE0000e,...□.j,...□.
0080C0526F2C180000EE0000-742C180000EE0000o,...□.t,...□.
0080C062792C180000EE0000-7E2C180000EE0000y,...□.~,...□.
由此得知INT_0是放在0018:00002C47的位址,於是查GDT表..


LoadGDT
GDT=C80018C88000所以观看0080C818的记忆体
0080C8180000000000000000-FFFF101183930000..............
0080C828FFFF006E81930000-FFFF006E819B0000...n.....n..
0080C838FFFF00000093CF00-FF7F00000B920000.....□.....
0080C848FF7F00800B920000-FFFF00000C920000...........
0080C858FFFFF032829A0000-FFFF00C08093C000..□.....□□
0080C8680F0000C07F92C000-68200000818B0000...□□h....
得到Selector=0018=线性记忆体位址816E00处

於是我们就可以得知该中断程式放在816E00:2C47了,於是笔者把816E00的记忆体
搬到8000:0000,然後用DEBUG来查看。

-u8000:2c47
8000:2C476A00PUSH00
8000:2C49E9F4D6JMP0340
8000:2C4C6A01PUSH01
8000:2C4EE9C7D8JMP0518
8000:2C516A02PUSH02
8000:2C53E98ADCJMP08E0
8000:2C566A03PUSH03
8000:2C58E9D6DCJMP0931
8000:2C5B6A04PUSH04
8000:2C5DE9E0D6JMP0340
8000:2C606A05PUSH05
8000:2C62E9DBD6JMP0340
8000:2C656A06PUSH06
8000:2C67E943DFJMP0BAD
8000:2C6A6A07PUSH07
8000:2C6CE975E0JMP0CE4
8000:2C6F6A08PUSH08
8000:2C71E97BE1JMP0DEF
8000:2C746A09PUSH09
8000:2C76E91605JMP318F
8000:2C796A0APUSH0A
8000:2C7BE9C4D5JMP0242
8000:2C7E6A0BPUSH0B
8000:2C80E9BFD5JMP0242

----------------------------------------------------------------------
----
看了上面几个例子後,再来就是练习进入保护模式,底下的例子请勿载入EMM系列的
保护模式软体,以免等级权限相冲当机。

codesegment
assumecs:code,ds:code
startprocnear
jmpnext

buffer1db18h,00h,00h,00h,00h,00h
;---+---------+--------
;||
;||
;|GDT表的记忆体位址
;|
;+----------GDT表的长度
;
;
buffer2db000h,000h,000h,000h,000h,000h,000h,000h;保留段
db0ffh,0ffh,000h,000h,000h,09bh,000h,000h;程式段code:0
db0ffh,0ffh,000h,080h,00bh,093h,000h,000h;萤幕段B800:0
db0100hdup(0)
;------+-------
;|
;|
;线性记忆体位址
;
msg_1db'EnterProtectMode!'
msg_2db0dh,0ah,'ReturnRealMode!',0dh,0ah,'$'
386p
next:
movax,0600h;
movbx,0700h;
movcx,0000h;
movdx,184fh;
int10h;CLS
movah,02h;
movbh,00h;
movdx,0100h;
int10h;
movax,cs
movds,ax
moves,ax
xoreax,eax
xorebx,ebx
movax,cs
movcl,04h
shleax,cl
movbx,offsetbuffer2
addeax,ebx
movbx,offsetbuffer1+2
movcs:[bx],eax;GDT位址设定
NOP
xoreax,eax
xorebx,ebx
movax,cs
movcl,04h
shleax,cl
addeax,ebx
movbx,offsetbuffer2
movcs:[bx+0ah],eax;GDTTable设定
movbyteptrcs:[bx+0dh],9bh;存取权
movax,cs
movds,ax
moves,ax
movbx,offsetbuffer1
xorecx,ecx
cli
cli
lgdtcs:[bx];载入GDT
moveax,cr0
oreax,01h
movcr0,eax
jmpprotection;进入保护模式
protection:
db66h
movax,code
movds,ax
movsi,offsetmsg_1
movbx,0010h
moves,bx
movdi,0000h
movcx,0014h
movah,70h
show:
cld;将CS:MSG_1搬到0010:00000000
lodsb;(0010的区段=B8000请参考GDT
stosw;表)
loopshow;
moveax,cr0
andal,not1
movcr0,eax
db0eah
dwreal_mode,code;返回真实模式
real_mode:
sti
movax,cs
movds,ax
movah,09h
movdx,offsetmsg_2
int21h
movax,4cffh
int21h
movax,4cffh
int21h
startendp
codeends
endstart

----------------------------------------------------------------------
----
上面这个例子并没有设定IDT(中断表),如果您要设定中断表的话,记得返回真实模
式时要还原IDT表.
----------------------------------------------------------------------
----
如果您希望在载入QEMM386後还能正常进入保护模式的话,则必需透过VCPI的命令
来切入保护模式,详情可翻阅VCPI的书籍。


┌┐┌┐∞
【80386保护模式简介二】┘└┘└┘
----------------------------------------------------------------------
----
进入保护模式可以得到很多好处,让你的程式不再有640K限制,可以产生虚拟记忆
体、拦I/O,所有的应用程式读写系统暂存器,产生中断....都可以完全拦截,而且
TSS工作切换能力可以让你不占用DOS下的堆叠区,还有很多好处无法一一叙述,因
此由笔者来教你如何切入保护模式吧....从简单的开始。

在保护模式下有很多新的名词,包含GDT.LDT.IDT以及CR0-CR3,笔者对保护模式并
不清楚,所以底下资料可能有错误。这里使用大量的线性记忆体观念,请您一定要
从头往後看,否则很可能会看不懂,且必须懂线性记忆体计算方式。

----------------------------------------------------------------------
----
在进入保护模式时,首先你要先设定GDT表格,这个表格描述主要是来定义每
个段落的记忆体起始位址与长度、存取权。这个情形就好像传统REALMODE那
样,REALMODE每个区段的记忆体开始位址与长度都已经由CPU定死了,比如说当
我们看到1000:0000,其实它就是指记忆体的第64K位址,同理看到2000:0000
就代表是第128K位址,定址方式就是Segment:Offset。

而保护模式的段落起始位址与长度却是可程式变动的,这个可变动的段落起始位址
与长度就是由GDT来设定的,根据这个值,你可以将每个段落改成64K,或是1MB...甚
至更多,可任意设定1BYTE~4GB,所以定址方式变成Selector:Offset或许您曾用过
386DEBUG,看过定址方式为XXXX:XXXXXXXX,根据後面这八位数,理论上可定址到4G
B,其实这是不行的,如果你在GDT表格设定的记忆体为1K则你尝试DUMP1K以後的记
忆体都会看到FF,就好像没有记忆体一般。

----------------------------------------------------------------------
-----
Gdtaddsdw0018h,GdtTable32位元线性位址
GdtTabledb00h,00h,00h,00h,00h,00h,00h,00h;
db7fh,ffh,00h,08h,0bh,93h,00h,00h;B800:032K
dbffh,ffh,56h,34h,12h,93h,0fh,78h;
^^^^^^^^^^^^^^^^^^^^^^^^^^^
↑↑↑↑↑
││└棗棗棗93=可读写区段
││││
└棗棗棗棗棗棭貤棗70fffff+1=1MB(Limits)
││
└棗棗棗棗棭貤712345678(Base)


它所代表的意思是如下图所示:(每组8byte)

┌棗棗棗棗棗棗棗棗棗棗棗┐
1│Limitbit0-15│0byte
├棗棗棗棗棗棗棗棗棗棗棗┤
3│Basebit0-15│2
├棗棗棗棗棗┬棗棗棗棗棗棭?
5│存取权│Basebit16-23│4
├棗棗棗棗棗┼棗棗棗棗棗棭?
7│Basebit24-31│G│..│limitbit16-19│6
└棗棗棗棗棗┴棗棗棗棗棗棭?
"G"代表Limit的单位是Byte或PAGE(4K)

所以....

#0000Segmentnotpresent.
#0008Base=000B8000Limit=0000FFFFFlags=93USE32Bytegranularity
#0010Base=12345678Limit=000FFFFFFlags=93USE32Bytegranularity
^^^^^Selector^^存取权



设定完後,就是切入保护模式,只要将CR0暂存器的Bit0设为'1',再用一个跳越指令
,就进入保护模式了。


----------------------------------------------------------------------
-----
讲不懂没关系,现在来看看实例,这样比较容易懂..

C:\>386MICESAMPLE.EXE
-G1AE
EAX=00044A1CEBX=00000003ECX=00000000EDX=00000100
ESI=00000000EDI=00000000EBP=00000000ESP=0000FFFE
DS=4A1CSS=4A1CES=4A1CFS=4A0CGS=4A0C
-U1AE
4A1C:000001AECLI
4A1C:000001AFLGDTCS:[BX]棗→DUMPCS:[BX]棗→
4A1C:000000031800C9A10400<---GDT表放在0004A1C9长度18h
4A1C:000001B3MOV
EAX,CR0                                                        │
4A1C:000001B6           OR
EAX,1                                                          ↓
4A1C:000001BA           MOV     CR0,EAX

4A1C:00000009  00 00 00 00 00 00 00 00-FF FF C0 A1 04 9B 00-00
4A1C:000001BD           JMP     01C0

4A1C:00000010  FF FF 00 80 0B 93 00 00 (GDT表)
4A1C:000001BF           NOP
4A1C:000001C0           MOV     AX,0008H
4A1C:000001C3           MOV     DS,AX
4A1C:000001C5           MOV     WORD PTR DS:[0000H],7041h


由上面的 GDT 表知道 此程式共规划了三个区段 ,其中 0000 区段是不使用
故区段的表示方式如下:

#0000  Segment not present.
#0008  Base=0004A1C0  Limit=0000FFFF  Flags=9B  USE32  Byte granularit
y
#0010  Base=000B8000  Limit=0000FFFF  Flags=93  USE32  Byte granularit
y



-G 1BD
EAX=00000001  EBX=00000003  ECX=00000000  EDX=00000100
ESI=00000000  EDI=00000000  EBP=00000000  ESP=0000FFFE
DS=4A1C  SS=4A1C  ES=4A1C  FS=4A0C  GS=4A0C
4A1C:000001BD           JMP     01C0

-T (这儿就算是进入保护模式了)
EAX=00000001  EBX=00000003  ECX=00000000  EDX=00000100
ESI=00000000  EDI=00000000  EBP=00000000  ESP=0000FFFE
DS=0000  SS=0000  ES=0000  FS=0000  GS=0000
0000:000001C0           MOV     AX,0008H
0000:000001C3           MOV     DS,AX
0000:000001C5           MOV     WORD PTR DS:[0000H],7041h



因为进入保护模式 ,所以 Selector 的区段应该要去查 GDT 表格 ,这个例子的
Selector 0010 的 Base = B8000 ,所以...保护模式下的 0010:00000000 = 真实
模式下的 B800:0000 ,这样您懂了吗? 在行号 1C5 的位址有一行写入 7041 的动
作 ,就是在反白字元. 最後要进入真实模式时 ,只要将 CR0 的 Bit0 设为 '0'
,再用一个跳越指令就回到真实模式了.. ----------------------------------
----------------------------------------後记:    若有问题 ,烦在本站『站
内信箱』留信给我....尽量避免使用网路信 ,且尽快提出 ,否则接下来的课程将
会更难懂 ,如果你是完全不懂 ,麻烦也留信给我 ,我会再把这一章节再细细重新
说明。至於对组合语言不懂 ,或是对保护模式没兴趣的人 ,本人就帮不上忙了。

     A:下一次笔者将继续解说 V86 模式下的工作切换
    B:等级权限 / 拦 I/O

┌棗棗棗棗棗棗棗棗棗棗棗棗棗棗棗棗棗棭?
│  Soft Bugger 软体蛀虫 90:90/2                    软体新技术的实行者
  │
│  BBS:02-5955461 24HR          ID:Werong Ho               -- 软蛀 --
  │
└棗棗棗棗棗棗棗棗棗棗棗棗棗棗棗棗棗棭?




【 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.DR
5 两
个 ,原因同上
    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 ,则表示发生工作切换时 ,取用 0
018 的描述表格。

----------------------------------------------------------------------
----
┌棗棗棗┐
│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:E
IP
                               (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 的
指令 ,但是前面笔者提到读写系统暂存器必需在最高等级才可执行 ,为什麽 Use
r  仍可在最低等级下执行本命令呢 ?  底下是欺骗方式。


        (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 MA
P 只能设定为读写时发生异常 ,无法单独设定为仅读取才发生或仅写入才发生 ,
因此拦 I/O 的人要自己去辨认原因。这点也是很麻烦的。

----------------------------------------------------------------------
----
    切入 V86 後 ,还有很多问题要处理 ,包含上面提到的部份 ,和 HIMEM.SYS
相容啦 ,这些问题有待您自己去寻找解决办法。

    有关保护模式的部份笔者只能介绍到此 ,再下去更深的理论我不会解释 ,也
掰不出来 ,不过您如果会切入 V86 ,自然也能够写在保护模式下执行的程式才对
。如有问题再来信。

    DEBUGOS 这个小软体已经摆於 KPEMU300.ZIP 内了 ,这是一套模拟 KeyPro
的小软体。

┌棗棗棗棗棗棗棗棗棗棗棗棗棗棗棗棗棗棭?
│  Soft Bugger 软体蛀虫 90:90/2                    软体新技术的实行者
  │
----------------------------------------------------------------------
----
    切入 V86 後 ,还有很多问题要处理 ,包含上面提到的部份 ,和 HIMEM.SYS
相容啦 ,这些问题有待您自己去寻找解决办法。

    有关保护模式的部份笔者只能介绍到此 ,再下去更深的理论我不会解释 ,也
掰不出来 ,不过您如果会切入 V86 ,自然也能够写在保护模式下执行的程式才对
。如有问题再来信。

    DEBUGOS 这个小软体已经摆於 KPEMU300.ZIP 内了 ,这是一套模拟 KeyPro
的小软体。

┌棗棗棗棗棗棗棗棗棗棗棗棗棗棗棗棗棗棭?
│  Soft Bugger 软体蛀虫 90:90/2                    软体新技术的实行者
  │
│  BBS:02-5955461 24HR          ID:Werong Ho               -- 软蛀 --
  │
└棗棗棗棗棗棗棗棗棗棗棗棗棗棗棗棗棗棭?



【 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.DR
5 两个 ,原因同上
    C. 保护模式分段控制:IDT.GDT.LDT.TR

注:自 586 起新增 CR4.DR4.DR5 系统暂存器
----------------------------------------------------------------------
----
┌棗棗┐
│工作切换│
└棗棗┘

当您设定某些系统暂存器以後,电脑并不会马上反应所设定的工作,必需透过工作
切换的动作才会起动,这个工作切换很难用文字表达,笔者认为工作切换就是等级
切换的动作。可造成工作切换的指令包含INT_X、JMPTSS区段...等,其中INT_X是
指在V86下的程式若发生中断,电脑会自动切换至保护模式,并呼叫保护模式下的中
断处理程式,再由保护模式下的程式决定是否呼叫原来V86下的中断向量表,而这切
换到保护模式、再切回V86下,共发生两次工作切换......
┌棗┐
│等级│
└棗┘

保护模式下,等级共有0.1.2.3四个等级,其中第0级等级最高,第3级最低,而0级因
为是最高等级,因此也有人称为「特权等级」,而应用程式的等级为多少呢?这表示
在EFLAG的IOPL(BIT12.13)里,在V86下的等级多半是最低的第3级,所以此值为'11
'。

或许各位会认为自己去修改这个旗标将自己的等级调高就好了,事实上改好後还要
经过工作切换的动作,等级才能被修改,而经过工作切换的动作後,你的程式控制权
将转交给别人;再简单的说,发生INT_X时,电脑会将等级切换成最高等级(事实上是
由中断表上决定的),并进入保护模式,之後保护模式的程式再来决定将使用者的E
FLAG切成什麽等级,然後再IRETD切回V86,於是应用程式根本抢不过最早进入保护
模式的家伙。(这样你有办法在V86下抢到最高等级吗....不可能嘛)

等级的高低可以决定自己有多少控制权,例如等级最高的人才可以读写系统暂存器
,其馀的人想读写系统暂存器都会发生GeneralProtectionError0D,你可以把它想
像成等级不够,却要读取系统资源,会发生INT_0D,而原本这行指令将不会被执行,
而堆叠里所摆的EIP值也停在这行上面,如果INT_0D的处理程式不去跳过这个指令
,则会永远停在这个指令里(形同当机)。※注二

在V86下发生中断时,会自动PUSHEIP.CS.EFLAG.ESP.SS......数个暂存器,并自动
将SS.ESP的值替换,以免发生中断时,会动用到V86的堆叠,可是如果发生的是Gene
ralProtectionError(俗称异常),则会在PUSHEIP之前再多摆入一个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描
述表格,如果MOVAX,0018、LTRAX,则表示发生工作切换时,取用0018的描述表格。


注一:GeneralProtectionError发生後会去呼叫该中断,但是一般产生中断只会存
入EIP.CS.EFLAG.ESP.SS....,但是发生GeneralProtectionError的话堆叠会存入
错误代码.EIP.CS.EFLAG.ESP.SS.....堆叠多存放了一个"错误代码",记得在切回
V86前要将此值减去唷!!

注二:前面说发生GPError#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:E
IP
                               (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......忘了,比RealMode还要多
好多喔。

底下仅列出部份中断的处理方式....您必需处理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大小(6
5536埠),当某位元设定为'1'後,只要不是最高等级的人去读写此埠,都会发生GPE
rr#0D,当然在最低等级的V86程式也不例外,发生此错误後,就形同拦到I/O动作了
,紧接著透过最高等级的处理程式去判断发生错误的原因,例如判断程式码是否为
『ECINAL,DX』、『EEOUTDX,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
的指令 ,但是前面笔者提到读写系统暂存器必需在最高等级才可执行 ,为什麽 U
ser 仍可在最低等级下执行本命令呢 ?  底下是欺骗方式。


        (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下读写系统暂存器,可是确实也有不正常的程式是这
样搞的,例如倚天中文会执行MOVEAX,CR3,或是一些保护程式会写入除错暂存器(D
Rx)。所以为了相容性,这些最好做进去。

注:判别发生的原因也可以利用判断 I/O 的那种方法 ,但写起来很麻烦。
----------------------------------------------------------------------
----
┌棗棗棗棭?
│相容性的处理二│      HIMEM.SYS
└棗棗棗棭?
HIMEM.SYS是一个可以控制1MB以外记忆体的程式,不过之前笔者有提过,要读写超
过1MB以外的记忆体必需进入保护模式才行(据说有後门可用),那麽载入自己的保
护模式程式後,再遇到呼叫HIMEM.SYS去搬移1MB以外的记忆体,电脑竟然会发生GP
Err#0D,原来这是因为HIMEM.SYS在执行搬移记忆体的命令後会去呼叫BIOS的AH=8
7hINT_15h去搬记忆体,换句话说就是因为这个BIOS中断会进入保护模式去搬记忆
体,所以才会造成当机,因此你的保护模式介面程式必需去模拟这个BIOS函式,就可
以与HIMEM.SYS相容了。

注:BIOSAH=87hINT_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               -- 软蛀 --
  │
└棗棗棗棗棗棗棗棗棗棗棗棗棗棗棗棗棗棭?


                                                    ┌┐┌┐∞
【 80386 保护模式简介五 】                     ┘└┘└┘
======================================================================
====
前言:
    底下是进入保护模式、进入 V86 的精简范例 ,执行前请确定 CPU 是处在真
实模式 ,程式码因为用到 386 指令 ,请用 TASM 3.1 来编译。
----------------------------------------------------------------------
----
┌棗棗棗┐
│进入保护模式│
└棗棗棗┘

进入保护模式的程式范例,其目地是进入保护模式,并在保护模式下用绝对记忆体
读写的方式,直接将'ProtectionMode!'字串写入VideoRam(B800:0000),本程式以
最精简的方式撰写,没有任何错误处理,因此请确定电脑现在处在真实模式下才可
执行本程式。(禁挂EMM系保护模式软体)


--
m;31m※ 来源:.网易虚拟社区 http://club.netease.com.[FROM: 202.102.65.183]m
--
m;34m※ 转寄:.网易 BBS bbs.netease.com.[FROM: bbs.huizhou.gd.cn]m
..

--



日出东方,唯我不败;
    天上地下,唯我独尊。

※ 来源:.BBS 荔园晨风站 bbs.szu.edu.cn.[FROM: 192.168.1.143]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店