┌┐┌┐∞ 【 80386保護模式簡介 】 ┘└┘└┘ -------------------------------------------------------------------------- 在保護模式下有很多新的名詞 ,包含 GDT.LDT.IDT 以及 CR0-CR3 ,筆者對保護 模式並不清楚 ,所以底下資料可能有錯誤。這裡使用大量的線性記憶體觀念 ,請您 一定要從頭往後看 ,否則很可能會看不懂 ,且必須懂線性記憶體計算方式。 -------------------------------------------------------------------------- 【 GDT 介紹 】 在真實模式下每個區段都等於64K ,可是保護模式下每個區段的大小卻是可變動 的 ,每個區段有多大呢 ,就是由 GDT 來決定。 您可以用 SGDT CS:[BX] 的方式將 GDT 的值讀出 ,它的長度為 6 BYTE ,底下 是筆者寫的小程式讀出。 XXXX:0000 FF 0F 00 20 C0 00 ^^^^^^^^^^^GDT表所在的線性記憶體位址 ^^^^^GDT表長度+1 將此表資料讀出來. X:00C02000 00 00 00 00 00 00 00 00-FF FF 00 A0 C2 9B 40 00 ........... B.@. X:00C02010 FF FF B0 DD 01 93 40 00-FF FF E0 B3 00 9A 00 00 ..0]..@...`3.... X:00C02020 FF FF E0 B3 00 93 00 00-00 00 00 20 C1 82 80 00 ..`3....... A... X:00C02030 00 00 00 20 C1 93 C0 00-00 00 00 20 C0 93 C0 00 ... A.@.... @.@. X:00C02040 00 00 00 00 00 92 40 00-FF FF 00 80 0B 92 40 00 ......@.......@. . . . . 它所代表的意思是如下圖所示:(每組 8 byte) ┌──────────────────────┐ 1│ Limit bit 0-15 │ 0 byte ├──────────────────────┤ 3│ Base bit 0-15 │ 2 ├──────────┬───────────┤ 5│ 存取權 │ Base bit 16-23 │ 4 ├──────────┼───────────┤ 7│ Base bit 24-31 │G│..│limit bit 16-19│ 6 └──────────┴───────────┘ "G"代表 Limit 的單位是 Byte 或 PAGE(4K) 所以.... #0000 Segment not present. #0008 Base=00C2A000 Limit=0000FFFF Flags=9B USE32 Byte granularity #0010 Base=0001DDB0 Limit=0000FFFF Flags=93 USE32 Byte granularity #0018 Base=0000B3E0 Limit=0000FFFF Flags=9A USE16 Byte granularity #0020 Base=0000B3E0 Limit=0000FFFF Flags=93 USE16 Byte granularity #0028 Base=00C12000 Limit=00000000 Flags=82 Page granularity #0030 Base=00C12000 Limit=00000000 Flags=93 USE32 Page granularity #0038 Base=00C02000 Limit=00000000 Flags=93 USE32 Page granularity #0040 Base=00000000 Limit=00000000 Flags=92 USE32 Byte granularity #0048 Base=000B8000 Limit=0000FFFF Flags=92 USE32 Byte granularity #0050 Base=0001F56C Limit=000007FF Flags=92 USE32 Byte granularity #0058 Base=00000000 Limit=00000144 Flags=92 USE32 Page granularity #0060 Base=00000000 Limit=00000144 Flags=93 USE32 Page granularity #0068 Base=00127F48 Limit=0000C32F Flags=9B USE16 Byte granularity #0070 Base=00134278 Limit=000028F7 Flags=93 USE16 Byte granularity #0078 Base=00000000 Limit=00000000 Flags=92 USE16 Byte granularity ^^^^^Selector ^^存取權 Base 就是指這個Secector:00000000對應到線性記憶體的何處 ,也就是說將線性記 憶體從 Base 所指的地方開始長度為 Limit ,剪下來變成一個獨立的區段 ,如果您 在該區段想看超過 LIMIT 長度的記憶體 ,則會發生保護模式錯誤...應用程式可攔 截所發生的中斷適當的加以處理。 注意 ,Limit的單位可以是 byte ,也可以是page(4k) ,由 "G" 是否為 1 來決定 至於 Selector 的數值我猜想應該是被標上 8 的倍數吧 ,因為很多書都是如此介 紹它。 -------------------------------------------------------------------------- 【 LDT 介紹 】 上面介紹了 GDT 可以設定很多個Secector ,而 LDT 則是在這些被定義出來 的Selector中再切割出更小的單元。也就是說 LDT 的資料長度只有 2 BYTE ,這 個值直接就是指 Selector。 ※這個命令必需在最高權力下才能執行 ,所以筆者使用 386DEBUG 來執行 ,在傳 統 Real Mode/V86 都不能執行。 C:\>386debug 386debug.exp (改過的.exp檔) 000C:0002743C 660F0007 SLDT [EDI] -T -D EDI 0014:00000000 28 00 <-- LDT 所指的Selector為0028 根據 GDT 的資料查表得到下表 ,但是由於 0028 這段落禁止觀看 ,所以我改看0030 的段落 ,因為它的 Base 是一樣的。 #0028 Base=00C12000 Limit=00000000 Flags=82 Page granularity #0030 Base=00C12000 Limit=00000000 Flags=93 USE32 Page granularity -D 30:0 0030:00000000 FF 00 F0 CE 09 92 40 00-31 00 00 00 CA 9B C0 00 ..pN..@.1...J.@. 0030:00000010 31 00 00 00 CA 93 C0 00-FF FF 00 80 0B 92 40 00 1...J.@.......@. 0030:00000020 FF 00 F0 CE 09 92 40 00-4D 00 90 CE 09 92 40 00 ..pN..@.M..N..@. 0030:00000030 44 01 00 00 00 93 C0 00-00 00 00 00 00 92 40 00 D.....@.......@. 0030:00000040 FF FF 00 80 0B 92 40 00-00 00 00 00 00 92 40 00 ......@.......@. 0030:00000050 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 0030:00000060 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 0030:00000070 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ -DL 0 #0004 Base=0009CEF0 Limit=000000FF Flags=92 USE32 Byte granularity #000C Base=00CA0000 Limit=00000031 Flags=9B USE32 Page granularity #0014 Base=00CA0000 Limit=00000031 Flags=93 USE32 Page granularity #001C Base=000B8000 Limit=0000FFFF Flags=92 USE32 Byte granularity #0024 Base=0009CEF0 Limit=000000FF Flags=92 USE32 Byte granularity #002C Base=0009CE90 Limit=0000004D Flags=92 USE32 Byte granularity #0034 Base=00000000 Limit=00000144 Flags=93 USE32 Page granularity #003C Base=00000000 Limit=00000000 Flags=92 USE32 Byte granularity #0044 Base=000B8000 Limit=0000FFFF Flags=92 USE32 Byte granularity #004C Base=00000000 Limit=00000000 Flags=92 USE32 Byte granularity #0054 Segment not present. #005C Segment not present. #0064 Segment not present. #006C Segment not present. #0074 Segment not present. #007C Segment not present. -------------------------------------------------------------------------- 【 IDT 介紹 】 在以往中斷向量表都是用 4 byte 來表示 ,但是在保護模式下則由 8 byte 表 示 ,至於那幾個 byte 表示什麼 ,筆者還未搞懂 ,底下只弄懂幾個。 C:\>386debug 386debug.exp (改過的.exp檔) 000C:00027434 660F010F SIDT [EDI] -D EDI 0014:00000000 FF 07 6C F5 01 00 .. ..-.. .. .. .. .. .. .. .. ^^^^^^^^^^^線性記憶體位址 ^^^^^長+1 因為該線性記憶體已對映到 50:0 #0050 Base=0001F56C Limit=000007FF Flags=92 USE32 Byte granularity 所以: 0050:00000000 00 34 08 00 00 EE 00 00-0A 34 08 00 00 EE 00 00 .4...n...4...n.. 0050:00000010 14 34 08 00 00 EE 00 00-1E 34 08 00 00 EE 00 00 .4...n...4...n.. 0050:00000020 28 34 08 00 00 EE 00 00-32 34 08 00 00 EE 00 00 (4...n..24...n.. 0050:00000030 3C 34 08 00 00 EE 00 00-6C 16 C8 0F 00 8E 00 00 <4...n..F4...n.. 0050:00000040 50 34 08 00 00 EE 00 00-5A 34 08 00 00 EE 00 00 P4...n..Z4...n.. 0050:00000050 64 34 08 00 00 EE 00 00-6E 34 08 00 00 EE 00 00 d4...n..n4...n.. 0050:00000060 78 34 08 00 00 EE 00 00-82 34 08 00 00 EE 00 00 x4...n...4...n.. 0050:00000070 8C 34 08 00 00 EE 00 00-96 34 08 00 00 EE 00 00 .4...n...4...n.. -DI 0 #0000 Selector=0008 Offset=00003400 Flags=EE ;int_0 #0001 Selector=0008 Offset=0000340A Flags=EE ;int_1 #0002 Selector=0008 Offset=00003414 Flags=EE ;int_2 #0003 Selector=0008 Offset=0000341E Flags=EE ;int_3 #0004 Selector=0008 Offset=00003428 Flags=EE #0005 Selector=0008 Offset=00003432 Flags=EE #0006 Selector=0008 Offset=0000343C Flags=EE #0007 Selector=0FC8 Offset=0000166C Flags=8E ;此處為Q387使用 #0008 Selector=0008 Offset=00003450 Flags=EE #0009 Selector=0008 Offset=0000345A Flags=EE #000A Selector=0008 Offset=00003464 Flags=EE #000B Selector=0008 Offset=0000346E Flags=EE #000C Selector=0008 Offset=00003478 Flags=EE #000D Selector=0008 Offset=00003482 Flags=EE #000E Selector=0008 Offset=0000348C Flags=EE #000F Selector=0008 Offset=00003496 Flags=EE 請仔細看一看這個表的對應情形 ,筆者故意載入Q387 以便讓 INT_7 的 Selector 與 眾不同 ,讓您更易判斷中斷表對應關係。 -------------------------------------------------------------------------- 實例解說: 底下是讀取 SoftICE INT_0 的程式碼範例: Load IDT LDT = FF 07 12 C0 80 00 所以觀看 0080C012 的記憶體 0080C012 47 2C 18 00 00 EE 00 00-4C 2C 18 00 00 EE 00 00 G,.....L,..... 0080C022 51 2C 18 00 00 EE 00 00-56 2C 18 00 00 EE 00 00 Q,.....V,..... 0080C032 5B 2C 18 00 00 EE 00 00-60 2C 18 00 00 EE 00 00 [,.....`,..... 0080C042 65 2C 18 00 00 EE 00 00-6A 2C 18 00 00 EE 00 00 e,.....j,..... 0080C052 6F 2C 18 00 00 EE 00 00-74 2C 18 00 00 EE 00 00 o,.....t,..... 0080C062 79 2C 18 00 00 EE 00 00-7E 2C 18 00 00 EE 00 00 y,.....~,..... 由此得知 INT_0 是放在 0018:00002C47 的位址 ,於是查GDT表.. Load GDT GDT = C8 00 18 C8 80 00 所以觀看 0080C818 的記憶體 0080C818 00 00 00 00 00 00 00 00-FF FF 10 11 83 93 00 00 .............. 0080C828 FF FF 00 6E 81 93 00 00-FF FF 00 6E 81 9B 00 00 ...n.....n.. 0080C838 FF FF 00 00 00 93 CF 00-FF 7F 00 00 0B 92 00 00 ............ 0080C848 FF 7F 00 80 0B 92 00 00-FF FF 00 00 0C 92 00 00 ............ 0080C858 FF FF F0 32 82 9A 00 00-FF FF 00 C0 80 93 C0 00 ..2...... 0080C868 0F 00 00 C0 7F 92 C0 00-68 20 00 00 81 8B 00 00 ....h .... 得到 Selector=0018=線性記憶體位址 816E00 處 於是我們就可以得知該中斷程式放在 816E00:2C47 了 ,於是筆者把 816E00 的記憶體 搬到 8000:0000 ,然後用 DEBUG 來查看。 -u 8000:2c47 8000:2C47 6A00 PUSH 00 8000:2C49 E9F4D6 JMP 0340 8000:2C4C 6A01 PUSH 01 8000:2C4E E9C7D8 JMP 0518 8000:2C51 6A02 PUSH 02 8000:2C53 E98ADC JMP 08E0 8000:2C56 6A03 PUSH 03 8000:2C58 E9D6DC JMP 0931 8000:2C5B 6A04 PUSH 04 8000:2C5D E9E0D6 JMP 0340 8000:2C60 6A05 PUSH 05 8000:2C62 E9DBD6 JMP 0340 8000:2C65 6A06 PUSH 06 8000:2C67 E943DF JMP 0BAD 8000:2C6A 6A07 PUSH 07 8000:2C6C E975E0 JMP 0CE4 8000:2C6F 6A08 PUSH 08 8000:2C71 E97BE1 JMP 0DEF 8000:2C74 6A09 PUSH 09 8000:2C76 E91605 JMP 318F 8000:2C79 6A0A PUSH 0A 8000:2C7B E9C4D5 JMP 0242 8000:2C7E 6A0B PUSH 0B 8000:2C80 E9BFD5 JMP 0242 -------------------------------------------------------------------------- 看了上面幾個例子後 ,再來就是練習進入保護模式 ,底下的例子請勿載入 EMM 系 列的保護模式軟體 ,以免等級權限相衝當機。 code segment assume cs:code,ds:code start proc near jmp next buffer1 db 18h,00h,00h,00h,00h,00h ; ---+--- ------+-------- ; | | ; | | ; | GDT 表的記憶體位址 ; | ; +----------GDT 表的長度 ; ; buffer2 db 000h,000h,000h,000h,000h,000h,000h,000h ;保留段 db 0ffh,0ffh,000h,000h,000h,09bh,000h,000h ;程式段code:0 db 0ffh,0ffh,000h,080h,00bh,093h,000h,000h ;螢幕段B800:0 db 0100h dup (0) ; ------+------- ; | ; | ; 線性記憶體位址 ; msg_1 db 'Enter Protect Mode !' msg_2 db 0dh,0ah,'Return Real Mode !',0dh,0ah,'$' .386p next : mov ax,0600h ; mov bx,0700h ; mov cx,0000h ; mov dx,184fh ; int 10h ; CLS mov ah,02h ; mov bh,00h ; mov dx,0100h ; int 10h ; mov ax,cs mov ds,ax mov es,ax xor eax,eax xor ebx,ebx mov ax,cs mov cl,04h shl eax,cl mov bx,offset buffer2 add eax,ebx mov bx,offset buffer1+2 mov cs:[bx],eax ;GDT 位址設定 NOP xor eax,eax xor ebx,ebx mov ax,cs mov cl,04h shl eax,cl add eax,ebx mov bx,offset buffer2 mov cs:[bx+0ah],eax ;GDT Table 設定 mov byte ptr cs:[bx+0dh],9bh ;存取權 mov ax,cs mov ds,ax mov es,ax mov bx,offset buffer1 xor ecx,ecx cli cli lgdt cs:[bx] ;載入GDT mov eax,cr0 or eax,01h mov cr0,eax jmp protection ;進入保護模式 protection : db 66h mov ax,code mov ds,ax mov si,offset msg_1 mov bx,0010h mov es,bx mov di,0000h mov cx,0014h mov ah,70h show : cld ;將CS:MSG_1搬到 0010:00000000 lodsb ;(0010的區段=B8000 請參考GDT stosw ; 表) loop show ; mov eax,cr0 and al,not 1 mov cr0,eax db 0eah dw real_mode,code ;返回真實模式 real_mode : sti mov ax,cs mov ds,ax mov ah,09h mov dx,offset msg_2 int 21h mov ax,4cffh int 21h start endp code ends end start -------------------------------------------------------------------------- 上面這個例子並沒有設定 IDT (中斷表) ,如果您要設定中斷表的話 ,記得返回 真實模式時要還原 IDT 表. -------------------------------------------------------------------------- 如果您希望在載入 QEMM386 後還能正常進入保護模式的話 ,則必需透過 VCPI 的命令來切入保護模式 ,詳情可翻閱 VCPI 的書籍。 -- 軟蛀 -- example.ASM: code segment assume cs:code,ds:code start proc near jmp next buffer1 db 18h,00h,00h,00h,00h,00h ; ---+--- ------+-------- ; | | ; | | ; | GDT 表的記憶體位址 ; | ; +----------GDT 表的長度 ; ; buffer2 db 000h,000h,000h,000h,000h,000h,000h,000h ;保留段 db 0ffh,0ffh,000h,000h,000h,09bh,000h,000h ;程式段code:0 db 0ffh,0ffh,000h,080h,00bh,093h,000h,000h ;螢幕段B800:0 db 0100h dup (0) ; ------+------- ; | ; | ; 線性記憶體位址 ; msg_1 db 'Enter Protect Mode !' msg_2 db 0dh,0ah,'Return Real Mode !',0dh,0ah,'$' .386p next : mov ax,0600h ; mov bx,0700h ; mov cx,0000h ; mov dx,184fh ; int 10h ; CLS mov ah,02h ; mov bh,00h ; mov dx,0100h ; int 10h ; mov ax,cs mov ds,ax mov es,ax xor eax,eax xor ebx,ebx mov ax,cs mov cl,04h shl eax,cl mov bx,offset buffer2 add eax,ebx mov bx,offset buffer1+2 mov cs:[bx],eax ;GDT 位址設定 NOP xor eax,eax xor ebx,ebx mov ax,cs mov cl,04h shl eax,cl add eax,ebx mov bx,offset buffer2 mov cs:[bx+0ah],eax ;GDT Table 設定 mov byte ptr cs:[bx+0dh],9bh ;存取權 mov ax,cs mov ds,ax mov es,ax mov bx,offset buffer1 xor ecx,ecx cli cli lgdt cs:[bx] ;載入GDT mov eax,cr0 or eax,01h mov cr0,eax jmp protection ;進入保護模式 protection : db 66h mov ax,code mov ds,ax mov si,offset msg_1 mov bx,0010h mov es,bx mov di,0000h mov cx,0014h mov ah,70h show : cld ;將CS:MSG_1搬到 0010:00000000 lodsb ;(0010的區段=B8000 請參考GDT stosw ; 表) loop show ; mov eax,cr0 and al,not 1 mov cr0,eax db 0eah dw real_mode,code ;返回真實模式 real_mode : sti mov ax,cs mov ds,ax mov ah,09h mov dx,offset msg_2 int 21h mov ax,4cffh int 21h start endp code ends end start