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

Shell编程

开发平台:

DOS

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