386-3.txt
上传用户:gb3576
上传日期:2007-01-03
资源大小:24k
文件大小:12k
源码类别:

Shell编程

开发平台:

DOS

  1.                                                     ┌┐┌┐∞
  2. 【 80386 保护模式简介三 】                     ┘└┘└┘
  3. ==========================================================================
  4. 前言:
  5.  
  6.     前面两集主要是要告诉各位有关 IDT.GDT 的用法 ,虽然这样已经可以简单的进
  7. 入保护模式 ,但是它还不足以让你撰写程式 ,因此笔者还必需往下继续叙说 ,不过再
  8. 往下讲之前 ,又有一票烦且杂的观念要说 ,本篇还是继续在"观念"上打转 ,读者千万
  9. 不要以为本篇又是「干古」 ,如果本篇不懂的话 ,後面的精彩文章您大概也看不懂 ,
  10. 笔者會尽量把文章写到容易懂的范围。
  11.  
  12. --------------------------------------------------------------------------
  13. ┌────────┐
  14. │80386 暂存器介绍│
  15. └────────┘
  16.  
  17.     80386 的暂存器除了扩充成 32 位元以外 ,亦增加了许多新的暂存器 ,除了一般
  18. 使用者暂存器(AX.BX....SI.DI)各位已经了解以外 ,也增加了系统暂存器、以及扩充
  19. 的旗标 暂存器....等等。
  20.  
  21.  
  22. A.使用者暂存器   → EAX.EBX.ECX.EDX.ESI,EDI.EBP.ESP
  23.  
  24. B.指令指标暂存器 → CS.EIP 两个暂存器
  25.  
  26. C.区段暂存器     → CS.SS.DS.ES.FS.GS
  27.     虽然 80386 已经进入 32 位元时代 ,但是这几个暂存器仍是 16 位元的 ,且多
  28.     了 FS.GS 两个暂存器 ,这两个暂存器并无特殊意义 ,各位可以把它当做 DS.ES
  29.     来看待。
  30.  
  31. D.系统暂存器
  32.     A. 控制暂存器:包含 CR0.CR2.CR3 三个 ,各位可能看到漏了一个 CR1 ,原因是
  33.        386.486.586 都没有此暂存器
  34.     B. 除错暂存器:包含 DR0.DR1.DR2.DR3.DR6.DR7 共六个 ,也是漏了 DR4.DR5 两
  35.        个 ,原因同上
  36.     C. 保护模式分段控制:IDT.GDT.LDT.TR
  37.  
  38. --------------------------------------------------------------------------
  39. ┌────┐
  40. │工作切换│
  41. └────┘
  42.  
  43.     当您设定某些系统暂存器以後 ,电脑并不會马上反应所设定的工作 ,必需透过工
  44. 作切换的动作才會起动 ,这个工作切换很难难用文字表达 ,笔者认为工作切换就是等
  45. 级切换的动作。可造成工作切换的指令包含 INT_X 、JMP TSS区段...等 ,其中INT_X
  46. 是指在 V86 下的程式若发生中断 ,电脑會自动切换至保护模式 ,并呼叫保护模式下的
  47. 中断处理程式 ,再由保护模式下的程式决定是否呼叫原来 V86  下的中断向量表 ,而
  48. 这切换到保护模式、再切回 V86 下 , 共发生两次工作切换......
  49.  
  50. ┌──┐
  51. │等级│
  52. └──┘
  53.  
  54.     保护模式下 ,等级共有 0.1.2.3 四个等级 ,其中第0级等级最高 ,第3级最低 ,
  55. 而0级因为是最高等级 ,因此也有人称为「特权等级」 ,而应用程式的等级为多少呢?
  56. 这表示在 EFLAG 的 IOPL (BIT12.13) 里 ,在 V86 下的等级多半是最低的第3级 ,所
  57. 以此值为 '11'。
  58.  
  59.     或许各位會认为自己去修改这个旗标将自己的等级调高就好了 ,事实上改好後还
  60. 要经过工作切换的动作 ,等级才能被修改 ,而经过工作切换的动作後 ,你的程式控制
  61. 权将转交给别人 ;再简单的说 ,发生 INT_X 时 ,电脑會将等级切换成最高等级(事实
  62. 上是由中断表上决定的) ,并进入保护模式 ,之後保护模式的程式再来决定将使用者的
  63. EFLAG 切成什麼等级 ,然後再 IRETD切回 V86 ,於是应用程式根本抢不过最早进入保
  64. 护模式的家伙。(这样你有办法在V86下抢到最高等级吗....不可能嘛)
  65.  
  66.     等级的高低可以决定自己有多少控制权 ,例如等级最高的人才可以读写系统暂存
  67. 器 ,其馀的人想读写系统暂存器都會发生 General Protection Error 0D ,你可以把
  68. 它想像成等级不够 ,却要读取系统资源 ,會发生 INT_0D ,而原本这行指令将不會被
  69. 执行 ,而堆叠里所摆的 EIP 值也停在这行上面 ,如果 INT_0D 的处理程式不去跳过
  70. 这个指令 ,则會永远停在这个指令里(形同当机)。
  71.  
  72.     在 V86 下发生中断时 ,會自动 PUSH EIP.CS.EFLAG.ESP.SS......数个暂存器 ,
  73. 并自动将 SS.ESP 的值替换 ,以免发生中断时 ,會动用到 V86 的堆叠 ,可是如果发
  74. 生的是 General Protection Error(俗称异常),则會在 PUSH EIP 之前再多摆入一
  75. 个DWORD 的错误代码 ,如果您的程式在 IRETD 前不减去这个可能存在的错误代码 ,
  76. 则會发生不可预知的後果。这也是保护模式下的程式不好写的原因之一。 而SS与ESP
  77. 所替换的值 ,则是最初进入保护模式後 ,由最高等级的人决定的(摆於TSS区段)。
  78.  
  79.     第二集里笔者有介绍 GDT 表 ,其中有个 93 代表可写区段 ,如果设成 89 ,则表
  80. 示此区段是 TSS 表格 ,再由 TR 暂存器来指定发生中断时 ,取用那一个区段的表格.
  81.  
  82. 举例来说 ,下面是 GDT 表格
  83.  
  84. gdttab  db      000h,000h,000h,000h,000h,000h,000h,000h ;00
  85.         db      0ffh,0ffh,000h,000h,000h,09bh,000h,000h ;08
  86.         db      0ffh,0ffh,000h,000h,000h,093h,08fh,000h ;10
  87.         db      0ffh,0ffh,000h,000h,000h,089h,000h,000h ;18
  88.         db      0ffh,0ffh,000h,000h,000h,089h,000h,000h ;20
  89.         db      0ffh,0ffh,000h,000h,000h,093h,000h,000h ;28
  90.         db      0ffh,007h,000h,000h,000h,093h,000h,000h ;30
  91.         db      0ffh,0ffh,000h,080h,00bh,093h,000h,000h ;38
  92.         db      0ffh,0ffh,000h,000h,000h,093h,000h,000h ;40
  93.  
  94.   我们可以看到 18.20 两个 Selector 正好就是 89h ,也就是说它们俩个都可以是
  95. TSS 描述表格 ,如果 MOV AX,0018、LTR AX ,则表示发生工作切换时 ,取用 0018 的
  96. 描述表格。
  97.  
  98. --------------------------------------------------------------------------
  99. ┌──────┐
  100. │TSS 表格简介│
  101. └──────┘
  102.     TSS 也有人称为「工作切换」 ,其表格设定如下 ,详情可看书比较详细。
  103.  
  104. tssltr  dd      00000000h
  105.         dd      0000ff00h       ;ESP
  106.         dw      0028h,0000h     ;SS.0
  107.         dd      0,0,0,0,0
  108.         dw      offset enter_v86,0000h      ;EIP
  109.         dd      00000200h       ;EFlag
  110.         dd      0,0,0,0
  111.         dd      0000ff00h       ;ESP
  112.         dd      0,0,0
  113.         dw      0010h,0000h     ;ES.0
  114.         dw      0008h,0000h     ;CS.0
  115.         dw      0028h,0000h     ;SS.0
  116.         dw      0010h,0000h     ;DS,0
  117.         dw      0010h,0000h     ;FS.0
  118.         dw      0010h,0000h     ;GS.0
  119.         dw      0000h,0000h     ;LDT.0
  120.         dw      0000h,0068h     ;0.IOMAP起点
  121.         db      1000h dup (0)   ;4K IOMAP 表
  122.         dw      0ffffh
  123.  
  124.  
  125.     如果您的程式使用 JMP XXXX:YYYYYYYY 的方式跳到本区节的话 ,原本指定的
  126. YYYYYYYY 将无用途 ,因为所有的暂存器将被替换成此表格的数值(含CS.EIP) ,并
  127. 完成等级切换的动作。
  128.  
  129.  
  130. --------------------------------------------------------------------------
  131. ┌───────┐
  132. │进入 V86  模式│
  133. └───────┘
  134.  
  135.         cli
  136.         lgdt    fword ptr cs:gdtadds
  137.         lidt    fword ptr cs:idtadds
  138.         mov     eax,cr0
  139.         or      al,01h
  140.         mov     cr0,eax
  141.         mov     bx,0018h
  142.         ltr     bx            ;发生工作切换时 ,SS:ESP 将参考 0018 的区段表格
  143.         jmp     0020h:0000h   ;进入工作切换 ,會跳到此表格内指定的 CS:EIP
  144.                                (LTR.JMP 不可指向同一表格)
  145.  
  146. enter_v86 :                   ;假设您已将 CS:EIP 指向此处继续执行
  147.         xor     eax,eax
  148.         mov     ax,code
  149.         push    eax             ;GS
  150.         push    eax             ;FS
  151.         push    eax             ;DS
  152.         push    eax             ;ES
  153.         push    eax             ;SS
  154.         mov     ax,0f000h
  155.         push    eax             ;ESP
  156.         mov     eax,00023000h   ;设定VM=1    等级=3
  157.         push    eax             ;Eflag
  158.         xor     eax,eax
  159.         mov     ax,code
  160.         push    eax             ;CS
  161.         mov     ax,offset return_dos
  162.         push    eax             ;EIP
  163.         clts                    ;将 387 切换成 32 位元模式
  164.         iretd                   ;回到 V86 (共弹出24h BYTE)
  165.  
  166. 紧接著就程式回到 V86 下继续执行著...
  167. --------------------------------------------------------------------------
  168. ┌────────┐
  169. │中断向量表的处理│
  170. └────────┘
  171.  
  172.     在保护模式下 ,产生中断後 ,會切回保护模式 ,於是您必需去呼叫原先 V86 下
  173. 的中断表 ,以便让程式能够正确执行。
  174.  
  175.     V86 下发生中断後 ,CPU 會取出 LTR 所设定 SS:ESP 值 ,然後将 V86 下的众
  176. 多暂存器暂存於此 ,不过因为 CPU 已变成 32 位元模式 ,所以堆叠内的 SP 值會被
  177. 减 12 byte (原本是6byte ,用以摆放 IP.CS.FLAG) ,且堆叠内的EIP值會指向 V86
  178. 下的 INT_X 的下一行 ,因此你必需先将 V86 下的 SP 值加 6 byte ,并修改 V86 下
  179. 的 SS:SP 里的内容为 INT_X 的下一行 ,然後将保护模式下的堆叠 CS:EIP 值指向原
  180. V86 下的中断位址 ,这样才可以带动 V86 下的中断表。
  181.  
  182.     底下仅列出部份中断的处理方式....您必需处理 256 个中断表。
  183.  
  184. new_20 :
  185.         push    0020h
  186.         jmp     int_emu
  187. new_21 :
  188.         push    0021h
  189.         jmp     int_emu
  190. new_22 :
  191.         push    0022h
  192.         jmp     int_emu
  193. new_23 :
  194.         push    0023h
  195.         jmp     int_emu
  196.  
  197. int_emu :
  198.         push    bp
  199.         mov     bp,sp
  200.         add     bp,04h
  201.         push    eax
  202.         push    ebx
  203.         mov     ax,0010h                ;
  204.         mov     ds,ax                   ;(Selector 0010h 的 Base=0)
  205.         mov     ax,ss:[bp+0ch]          ;
  206.         sub     ax,06h                  ;改V86的SP-6
  207.         mov     ss:[bp+0ch],ax          ;
  208.         xor     eax,eax                 ;
  209.         xor     ebx,ebx                 ;修改V86下的SS:SP ,帮它摆入
  210.         mov     ax,ss:[bp+10h]          ;INT_X 後的下一行位址 ,供V86
  211.         shl     eax,04h                 ;下的程式IRET返回INT_X的下一行用
  212.         mov     bx,ss:[bp+0ch]          ;
  213.         add     ebx,eax                 ;
  214.         mov     ax,ss:[bp+00h]          ;
  215.         mov     ds:[ebx],ax             ;
  216.         mov     ax,ss:[bp+04h]          ;
  217.         mov     ds:[ebx+02h],ax         ;
  218.         mov     ax,ss:[bp+08h]          ;
  219.         mov     ds:[ebx+04h],ax         ;
  220.         nop
  221.         xor     ebx,ebx                 ;
  222.         mov     bx,ss:[bp-02h]          ;
  223.         shl     ebx,02h                 ;
  224.         mov     ax,ds:[ebx]             ;IRETD 後到V86中断表所指的位址继续执行
  225.         mov     ss:[bp+00h],ax          ;(查 0000:0000 的中断表)
  226.         mov     ax,ds:[ebx+02h]         ;
  227.         mov     ss:[bp+04h],ax          ;
  228.         mov     eax,ss:[bp+08h]
  229.         or      eax,00032000h           ;等级=3  VM=1
  230.         and     eax,0fffffeffh          ;关闭'T'旗标
  231.         mov     ss:[bp+08h],eax
  232.         pop     ebx
  233.         pop     eax
  234.         pop     bp
  235.         add     sp,02h
  236.         iretd
  237.  
  238. --------------------------------------------------------------------------
  239. ┌──────┐
  240. │相容性的处理│
  241. └──────┘
  242.  
  243.     或许您曾经在挂入 QEMM386、EMM386 之後 ,在 V86 下执行 MOV EAX,CR0 的指
  244. 令 ,但是前面笔者提到读写系统暂存器必需在最高等级才可执行 ,为什麼 User  仍
  245. 可在最低等级下执行本命令呢 ?  底下是欺骗方式。
  246.  
  247.  
  248.         (User)  V86 下执行 MOV EAX,CR0
  249.                      ↓
  250.                 发生 General Protection 0D
  251.                 CPU 自动切入保护模式 ,并执行 INT_0D 的处理程式
  252.                 (堆叠里多储存了错误代码 DWORD)
  253.                      ↓
  254.         (EMM)   检查发生错误的原因
  255.                 读取 EAX,CR0 (因此时已是最高等级 ,本行可以正确执行)
  256.                      ↓
  257.         (EMM)   修改堆叠内的 EIP 值 ,指向下一行指令
  258.                      ↓
  259.         (EMM)   修改使用者等级 3 / 设定 VM 旗标等於 1
  260.                      ↓
  261.         (EMM)   ESP 值扣掉错误代码 4byte
  262.                      ↓
  263.         (EMM)      IRETD 切回 V86
  264.                      ↓
  265.         (User) 使用者取得 EAX 的数值
  266.  
  267.     由於程式有一大半在保护模式下执行 ,所以使用者根本感觉不到 ,只知道自己真
  268. 的读到系统暂存器。这便是 EMM 系的欺骗手段。
  269.  
  270.  
  271.     再举例来说 ,笔者所写的 DEBUGOS ,在这个系统下您可以执行 MOV EAX,CR0 ,就
  272. 是因为笔者有加以处理 ,可是笔者检查保护模式错误原因里并没有处理 MOV EBX,CR0
  273.  ,於是在这系统下 ,您就没办法执行本命令了 ,您可以试试看。
  274.  
  275.     本来标准的程式是不會在 V86 下读写系统暂存器 ,可是确实也有不正常的程式
  276. 是这样搞的 ,例如倚天中文會 MOV EAX,CR3 ,或是一些保护程式會写入除错暂存器
  277. (DRx)。所以为了相容性 ,这些最好做进去。
  278.  
  279. --------------------------------------------------------------------------
  280. ┌──────┐
  281. │拦 I/O  能力│
  282. └──────┘
  283.  
  284.     在进入保护模式後 ,您可以在 IOMAP 里设定某些位元 ,用以管理 I/O 埠 ,每个
  285. Bit 表示一个埠 ,4K=32768埠 ,当您设定此位元後 ,等级低的人读写此埠就會发生
  286. General Protection Error 0D ,然後你就可以加以处理啦 ,不过 I/O MAP 只能设定
  287. 为读写时发生异常 ,无法单独设定为仅读取才发生或仅写入才发生 ,因此拦 I/O 的
  288. 人要自己去辨认原因。这点也是很麻烦的。
  289.  
  290. --------------------------------------------------------------------------
  291.     切入 V86 後 ,还有很多问题要处理 ,包含上面提到的部份 ,和 HIMEM.SYS 相容
  292. 啦 ,这些问题有待您自己去寻找解决办法。
  293.  
  294.     有关保护模式的部份笔者只能介绍到此 ,再下去更深的理论我不會解释 ,也掰不
  295. 出来 ,不过您如果會切入 V86 ,自然也能够写在保护模式下执行的程式才对。如有问
  296. 题再来信。
  297.  
  298.     DEBUGOS 这个小软体已经摆於 KPEMU300.ZIP 内了 ,这是一套模拟 KeyPro 的小
  299. 软体。
  300.  
  301. ┌───────────────────────────────────┐
  302. │  Soft Bugger 软体蛀虫 90:90/2                    软体新技术的实行者  │
  303. │  BBS:02-5955461 24HR          ID:Werong Ho               -- 软蛀 --  │
  304. └───────────────────────────────────┘