歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux資訊 >> 更多Linux >> 80386保護模式簡介

80386保護模式簡介

日期:2017/2/27 9:38:12   编辑:更多Linux

在保護模式下有很多新的名詞 ,包含 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 [email protected].@.     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 [email protected]..@.     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 ..倸.....摗@     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         >   >   > ┌┐┌┐∞   > 【 80386保護模式簡介二 】 ┘└┘└┘   > --------------------------------------------------------------------------   > 進入保護模式可以得到很多好處 ,讓你的程式不再有 640K 限制 ,可以ㄔ    > 擬記憶體、攔 I/O ,所有的應用程式讀寫系統暫存器 ,產生中斷....都可以完全攔   > 截 ,而且 TSS 工作切換能力可以讓你不占用 DOS 下的堆疊區 ,還有很多好處無法   > 一一敘述 ,因此由筆者來教你如何切入保護模式吧....從簡單的開始。   >   > 在保護模式下有很多新的名詞 ,包含 GDT.LDT.IDT 以及 CR0-CR3 ,筆者對保護   > 模式並不清楚 ,所以底下資料可能有錯誤。這裡使用大量的線性記憶體觀念 ,請您   > 一定要從頭往後看 ,否則很可能會看不懂 ,且必須懂線性記憶體計算方式。   >   > --------------------------------------------------------------------------   > 在進入保護模式時 ,首先你要先設定 GDT 表格 ,這個表格描述主要是來定義每   > 個段落的記憶體起始位址與長度、存取權。 這個情形就好像傳統 REAL MODE 那   > 樣 ,REAL MODE 每個區段的記憶體開始位址與長度都已經由 CPU 定死了 ,比如說當   > 我們看到 1000:0000 ,其實它就是指記憶體的第 64K 位址 ,同理看到 2000:0000   > 就代表是第 128K 位址 ,定址方式就是 Segment:Offset。   >   > 而保護模式的段落起始位址與長度卻是可程式變動的 ,這個可變動的段落起始   > 位址與長度就是由 GDT 來設定的 ,根據這個值 ,你可以將每個段落改成64K ,或是   > 1MB...甚至更多 ,可任意設定 1BYTE~4GB ,所以定址方式變成 Selector:Offset   > 或許您曾用過 386DEBUG ,看過定址方式為 XXXX:XXXXXXXX ,根據後面這八位數 ,   > 理論上可定址到 4GB ,其實這是不行的 ,如果你在 GDT 表格設定的記憶體為 1K   > 則你嘗試 DUMP 1K 以後的記憶體都會看到 FF ,就好像沒有記憶體一般。   >   > ---------------------------------------------------------------------------   > Gdtadds dw 0018h,GdtTable 32 位元線性位址   > GdtTable db 00h,00h,00h,00h,00h,00h,00h,00h ;   > db 7fh,ffh,00h,08h,0bh,93h,00h,00h ;B800:0 32K   > db ffh,ffh,56h,34h,12h,93h,0fh,78h ;   > ^^^^^^^ ^^^^^^^^^^^ ^^^ ^^^ ^^^   > ↑ ↑ ↑ ↑ ↑   > │ │ └──────93=可讀寫區段   > │ │ │ │   > └───────────┴────0fffff+1=1MB (Limits)   > │ │   > └─────────┴──12345678 (Base)   >   >   > 它所代表的意思是如下圖所示∶(每組 8 byte)   >   > ┌──────────────────────┐   > 1│ Limit bit 0-15 │ 0 byte   > ├──────────────────────┤   > 3│ Base bit 0-15 │ 2   > ├──────────┬───────────┤   > 5│ 存取權 │ Base bit 16-23 │ 4   > ├──────────┼────────w──┤   > 7│ Base bit 24-31 │G│..│limit bit 16-19│ 6   > └──────────┴───────────┘   > "G"代表 Limit 的單位是 Byte 或 PAGE(4K)   >   > 所以....   >   > #0000 Segment not present.   > #0008 Base=000B8000 Limit=0000FFFF Flags=93 USE32 Byte granularity   > #0010 Base=12345678 Limit=000FFFFF Flags=93 USE32 Byte granularity   > ^^^^^Selector ^^存取權   >   >   >   > 設定完後 ,就是切入保護模式 ,只要將 CR0 暫存器的 Bit0 設為 '1' ,再用一個   > 跳越指令 ,就進入保護模式了。   >   >   > ---------------------------------------------------------------------------   > 講不懂沒關系 ,現在來看看實例 ,這樣比較容易懂..   >   > C:\>386MICE SAMPLE.EXE   > -G 1AE   > EAX=00044A1C EBX=00000003 ECX=00000000 EDX=00000100   > ESI=00000000 EDI=00000000 EBP=00000000 ESP=0000FFFE   > DS=4A1C SS=4A1C ES=4A1C FS=4A0C GS=4A0C   > -U 1AE   > 4A1C:000001AE CLI   > 4A1C:000001AF LGDT CS:[BX] ──→ DUMP CS:[BX] ──→   > 4A1C:00000003 18 00 C9 A1 04 00 <--- GDT 表放在 0004A1C9 長度 18h   > 4A1C:000001B3 MOV   > 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 granularity   > #0010 Base=000B8000 Limit=0000FFFF Flags=93 USE32 Byte granularity   >   >   >   > -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 的動作 ,就是在螢幕秀 'A' 反白字元.   >   > 最後要進入真實模式時 ,只要將 CR0 的 Bit0 設為 '0' ,再用一個跳越指   > 令就回到真實模式了..   >   > --------------------------------------------------------------------------   > 後記:   > 若有問題 ,煩在本站『站內信箱』留信給我....盡量避免使用網路信 ,   > 且盡快提出 ,否則竣U來的課程將會更難懂 ,如果你是完全不懂 ,麻煩也留   > 信給我 ,我會再把這一章節再細細重新說明。至於對組合語言不懂 ,或是對   > 保護模式沒興趣的人 ,本人就幫不上忙了。   >   > A∶下一次筆者將繼續解說 V86 模式下的工作切換   > B∶等級權限 / 攔 I/O   >   > ┌───────────────────────────────────┐   > │ Soft Bugger 軟體蛀蟲 90:90/2 軟體新技術的實行者 │   > │ BBS:02-5955461 24HR ID:Werong Ho -- 軟蛀 -- │   > └──────────w────────────────────────┘   >         >   > ┌┐┌┐∞   > 【 80386 保護模式簡介三 】 ┘└┘└┘   > ==========================================================================   > 前言∶   >   > 前面兩集主要是要告訴各位有關 IDT.GDT 的用法 ,雖M這樣已經可以簡單的進   > 入保護模式 ,但是它還不足以讓你撰寫程式 ,因此筆者還必需往下繼續敘說 ,不過再   > 往下講之前 ,又有一票煩且雜的觀念要說 ,本篇還是繼續在"觀念"上打轉 ,讀者千萬   > 不要以為本篇又是「干古」 ,如果本篇不懂的話 ,後面的精彩文章您大概也看不懂 ,   > 筆者會盡量把文章寫到容易懂的范圍。   >   > --------------------------------------------------------------------------   > ┌────────┐   > │80386 暫存器介紹│   > └────────┘   >   > 80386 的暫存器除了擴充成 32 位元以外 ,亦增加了許多新獐 s器 ,除了一般   > 使用者暫存器(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   >   > --------------------------------------------------------------------------   > ┌──w───┐   > │相容性的處理│   > └──────┘   >   > 或許您曾經在掛入 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 -- 軟蛀 -- │   > └───────────────────────────────────┘         > ┌┐┌┐∞   > 【 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.DR5 兩   > 個 ,原因同上   > C. 保護模式分段控制:IDT.GDT.LDT.TR   >   > 注:自 586 起新增 CR4.DR4.DR5 系統暫存器   > --------------------------------------------------------------------------   > ┌────┐   > │工作切換│   > └────┘   >   > 當您設定某些系統暫存器以後 ,電腦並不會馬上反應所設定的工作 ,必需透過工   > 作切換的動作才會起動 ,這個工作切換很難用文字表達 ,筆者認為工作切換就是等級   > 切換的動作。可造成工作切換的指令包含 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 的   > 描述表格。   >   > 注一:General Protection Error 發生後會去呼叫該中斷 ,但是一般產生中斷只會   > 存入 EIP.CS.EFLAG.ESP.SS.... ,但是發生 General Protection Error 的話   > 堆疊會存入 錯誤代碼.EIP.CS.EFLAG.ESP.SS.....   > 堆疊多存放了一個"錯誤代碼" ,記得在切回 V86 前要將此值減去唷 !!   >   > 注二:前面說發生 GP Error #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: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 下產生中斷後 ,電腦會自動切回保護模式 ,並從 LTR 所指定的位址取得   > TSS 表格 ,然後以表格內的資料重新設定 SS.ESP ,然後把 V86 下的各暫存器值擺入   > 此堆疊內 ,在此需注意的是它擺放在堆疊的資料是32位元方式 ,所以對於 DS.ES....   > 這類16位元暫存器擺於堆疊 ,不足部份補 '0000' ,用以湊足 32Bit。   >   > 簡單來說 ,在真實模式下或 V86下使用一組 SS:SP ,一但透過中斷i入保護模式   > 後 ,原先的 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...... 忘了 ,比 Real Mode 還要   > 多好多喔。   >   > 底下僅列出部份中斷的處理方式....您必需處理 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 能力│   > └───w──┘   >   > TSS 表格內除了可定義產生工作切換後 ,SS.ESP.DS.ES....各暫存器替換值 ,也   > 可以開一塊記憶體做 IOMAP ,這塊記憶體每個 Bit 代表一個 PORT ,一般習慣是開4K   > 大小 (65536埠),當某位元設定為 '1' 後 ,只要不是最高等級的人去讀寫此埠 ,都會   > 發生 GP Err #0D ,當然在最低等級的 V86 程式也不例外 ,發生此錯誤後 ,就形同攔   > 到 I/O 動作了 ,緊接著透過最高等級的處理程式去判斷發生錯誤的原因 ,例如判斷   > 程式碼是否為 『EC IN AL,DX』、『EE OUT DX,AL』 ,或是其它程式碼 ,就可以分   > 辨發生的原因是讀或寫產生的 ,d到 I/O 後 ,你是否會寫騙 I/O 的程式 ?   >   > 以 S-ICE 的攔 I/O 能力為例 ,它先使用 IO-MAP 的方式去攔 I/O ,然後再判別   > "EE.E4.EC.E6...." 等等程式碼。   >   > 注:IOMAP 表是也是 TSS 表格的一部份。   > --------------------------------------------------------------------------   > ┌───────┐   > │相容性的處理一│ 系統暫存器的相容處理法   > └───────┘   >   > 或許您曾經在掛入 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 系的欺騙手段。   >   > 本來標准的程式是不會在 V86 下讀寫系統暫存器 ,可是確實也有不正常的程式   > O這樣搞的 ,例如倚天中文會執行 MOV EAX,CR3 ,或是一些保護程式會寫入除錯暫存   > 器 (DRx)。所以為了相容性 ,這些最好做進去。   >   > 注:判別發生的原因也可以利用判斷 I/O 的那種方法 ,但寫起來很麻煩。   > --------------------------------------------------------------------------   > ┌───────┐   > │相容性的處理二│ HIMEM.SYS   > └───────┘   > HIMEM.SYS 是一個可以控制 1MB 以外記憶體的程式 ,不過之前筆者有提過 ,要   > 讀寫超過 1MB 以外的記憶體必需進入保護模式才行(據說有後門可用) ,那麽載入自   > 己的保護模式程式後 ,再遇到呼叫 HIMEM.SYS 去搬移 1MB 以外的記憶體 ,電腦竟然   > 會發生 GP Err #0D ,原來這是因為 HIMEM.SYS 在執行搬移記憶體的命令後會去呼叫   > BIOS 的 AH=87h INT_15h 去搬記憶體 ,換句話說就是因為這個 BIOS 中斷會進入保   > 護模式去搬記憶體 ,所以才會造成當機 ,因此你的保護模式介面程式必需去模擬這個   > BIOS 函式 ,就可以與 HIMEM.SYS 相容了。   >   > 注:BIOS AH=87h INT_15h 會重設 GDT.IDT 表 ,然後進入保護模式去搬記憶體 ,然後   > 就當在 LIDT 或 LGDT 的命令上。   >   > 另外如果你的程式擺在 1MB 以上的記 擐 }去執行 ,還會有另一個問題產生 ,   > 不過如果你已經學會上面的這些功能 ,再嘗試去寫個程式去試試 ,你自然會知道   > 它會發生什麽問題 ,解決的辦法也很簡單 ,你一定會解決。   > --------------------------------------------------------------------------   > 切入 V86 後 ,還有很多問題要處理 ,不過上面提到的兩個問題如果你都能處理   > 的話 ,基本上就不會有其它大問題 ,等你會進入保護模式後 ,再來學習 VCPI、DPMI   > 就很簡單了。   >   > 如果各位會切入保護模式的話 ,接下來應該是學習 VCPI 的切入方式 ,雖然有很   > 多 y有介紹 ,但是要真正了解並不容易。建議各位去買套大宇出品的激斗戰士、戰   > 國策 ,它的外加保護就是切入保護模式的最佳范例 ,包含透過 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 來編譯。   > --------------------------------------------------------------------------   > ┌──────┐   > │進入保護模式│   > └──────┘   >   > 進入保護模式的程式范例 ,其目地是進入保護模式 ,並在保護模式下用絕對記憶   > 體讀寫的方式 ,直接將 'Protection Mode !' 字串寫入 Video Ram (B800:0000) ,   > 本程式以最精簡的方式撰寫 ,沒有任何錯誤處理 ,因此請確定電腦現在處在真實模式   > 下才可執行本程式。(禁掛 EMM 系保護模式軟體)   >   > 程式流程如下∶(底下所指記憶體位址皆為 32bit 絕對位址)   >   > 1. 設定 GDTtab 表所在的記憶體位址填入 GDTadds   >   > 2. 設定 Selector 0008 的記憶體起始位址就是現在 CS 的記憶體位址   > 設定 Selector 0010 的記憶體起始位址就是現在 CS 的記憶體位址   > Selector 0018 的記憶體起始位址就是 000B8000 = (B800:0000)   >   > 3. 執行 LGDT FWORD PTR CS:GDTadds 告訴 CPU 一但進入保護模式 ,各   > 區段的記憶體起始位址、長度   >   > 4. 設定 CR0 的 Bit0 = '1' ,並透過 JMP 指令進入保護模式   > ※ 進入保護模式後 ,DS.ES.SS.CS.GS.FS 等等暫存器定址方式不再   > 是 Segment ,而變成 Selector   >   > 5. 秀字 將 0010:MSG_1 搬到 0018:0000   > 意即將 'Protection Mode !' 字串搬到 Video Ram 去   >   > 6. 設定 CR0 的 Bit0 = '0' ,並透過 JMP 指令回到真實模式   > ※ 回到真實模式後 ,DS.ES.SS.CS.GS.FS 等等暫存器定址方式不再   > 是 Selector ,而變成 Segment   >   > 5. 秀字 將 CS:MSG_2 搬到 B800:00A0   > 意即將 'Return Real Mode !' 字串搬到 Video Ram 去   >   > 6. 結束程式   >   > ----------------------------- P.ASM ------------------------------------   > code segment   > assume cs:code,ds:code   > .386p   > start proc near   > jmp next   > gdtadds dw 001fh,0000h,0000h   > gdttab db 000h,000h,00h,00h,00h,00h,00h,00h ;00 Null   > db 0ffh,0ffh,00h,00h,00h,9bh,00h,00h ;08 PRG Seg   > db 0ffh,0ffh,00h,00h,00h,93h,00h,00h ;10 PRG Seg   > db 0ffh,0ffh,00h,80h,0bh,93h,00h,00h ;18 B8000   > msg_1 db 'Protection Mode !'   > msg_2 db 'Return Real Mode !'   >   > next :   > xor eax,eax ;   > xor ebx,ebx ;   > mov ax,cs ;設定 GDTadds   > shl eax,04h ;   > mov bx,offset gdttab ;   > add eax,ebx ;   > mov di,offset gdtadds+02h ;   > mov cs:[di],eax ;   > NOP   > xor eax,eax ;   > xor ebx,ebx ;   > mov ax,cs ;   > shl eax,04h ;   > mov di,offset gdttab+08h ;設定 GDTtab 內的   > mov si,offset gdttab+10h ;Selector 0008 及 0010   > mov cs:[di+02h],ax ;兩個段落的記憶體起始位址   > mov cs:[si+02h],ax ;   > shr eax,10h ;   > mov cs:[di+04h],al ;   > mov cs:[si+04h],al ;   > mov cs:[di+07h],ah ;   > mov cs:[si+07h],ah ;   > NOP   > cli   > lgdt fword ptr cs:gdtadds ;載入 GDT 表格   > mov eax,cr0 ;   > or al,01h ;   > mov cr0,eax ;   > jmp protection_mode ;進入保護模式   > protection_mode : ;   > mov ax,0010h ;   > mov ds,ax ;   > mov si,offset msg_1 ;   > mov ax,0018h ;將 0010:MSG_1 搬到 0018:0000   > mov es,ax ;   > mov di,0000h ;   > mov ah,70h ;   > mov cx,0011h ;   > cld ;   > L1 : ;   > lodsb ;   > stosw ;   > loop L1 ;   > NOP   > mov eax,cr0 ;   > and al,0feh ;   > mov cr0,eax ;回到真實模式   > jmp return_real_mode ;   > return_real_mode : ;   > sti   > mov ax,cs ;   > mov ds,ax ;   > mov si,offset msg_2 ;   > mov ax,0b800h ;   > mov es,ax ;將 CS:MSG_2 搬到 B800:00A0   > mov di,00a0h ;   > mov ah,70h ;   > mov cx,0012h ;   > cld ;   > L2 : ;   > lodsb ;   > stosw ;   > loop L2 ;   > mov ax,4cffh   > int 21h   > start endp   > code ends   > end start   > --------------------------------------------------------------------------   > 因為保護模式下不能呼叫真實模式下的中斷 ,所以筆者以直接填寫顯示卡記憶體   > 的方式秀字。這是一個簡單、尚未使用中斷向量表的范例。   >   > 注: 所謂一山不容二虎 ,如果已載入其它保護模式的程式 ,那本程式將會與它打架 ,   > 造成電腦當機。   >   > ┌────────┐   > │進入虛擬 86 模式│ 為求精簡 ,本程式毫無錯誤處理能力   > └────────┘   > ------------------------ V86.ASM ---------------------------------------   > code segment   > assume cs:code,ds:code   > .386p   > start proc near   > jmp next   > gdtadds dw 002fh,0000h,0000h   > gdttab db 000h,000h,000h,000h,000h,000h,000h,000h ;00 Null   > db 0ffh,0ffh,000h,000h,000h,09bh,000h,000h ;08 PRG Seg   > db 0ffh,0ffh,000h,000h,000h,093h,08fh,000h ;10 Dos=Page   > db 0ffh,0ffh,000h,000h,000h,089h,000h,000h ;18 TSSltr   > db 0ffh,0ffh,000h,000h,000h,089h,000h,000h ;20 TSSjmp   > db 0ffh,003h,000h,000h,000h,093h,000h,000h ;28 Stack (1K)   >   > tssltr dd 00000000h   > dd 000003ffh ;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 000003ffh ;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   > dw 0ffffh   >   > tssjmp dd 00000000h   > dd 000003ffh ;ESP   > dw 0028h,0000h ;SS.0   > dd 0,0,0,0,0   > dw offset enter_v86,0000h ;EIP   > dd 00000000h ;EFlag   > dd 0,0,0,0   > dd 000003ffh ;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   > iomap db 1000h dup (0)   > dw 0ffffh   >   > buffer1 db 0400h dup (0) ;Stack   >   > idtadds dw 07ffh,0000h,0000h   > idttab dw offset new_00,0008h,0ee00h,0000h,offset new_01,0008h,0ee00h,0000h   > dw offset new_02,0008h,0ee00h,0000h,offset new_03,0008h,0ee00h,0000h   > dw offset new_04,0008h,0ee00h,0000h,offset new_05,0008h,0ee00h,0000h   > dw offset new_06,0008h,0ee00h,0000h,offset new_07,0008h,0ee00h,0000h   > dw offset new_08,0008h,0ee00h,0000h,offset new_09,0008h,0ee00h,0000h   > dw offset new_0a,0008h,0ee00h,0000h,offset new_0b,0008h,0ee00h,0000h   > dw offset new_0c,0008h,0ee00h,0000h,offset new_0d,0008h,0ee00h,0000h   > dw offset new_0e,0008h,0ee00h,0000h,offset new_0f,0008h,0ee00h,0000h   > dw offset new_10,0008h,0ee00h,0000h,offset new_11,0008h,0ee00h,0000h   > dw offset new_12,0008h,0ee00h,0000h,offset new_13,0008h,0ee00h,0000h   > dw offset new_14,0008h,0ee00h,0000h,offset new_15,0008h,0ee00h,0000h   > dw offset new_16,0008h,0ee00h,0000h,offset new_17,0008h,0ee00h,0000h   > dw offset new_18,0008h,0ee00h,0000h,offset new_19,0008h,0ee00h,0000h   > dw offset new_1a,0008h,0ee00h,0000h,offset new_1b,0008h,0ee00h,0000h   > dw offset new_1c,0008h,0ee00h,0000h,offset new_1d,0008h,0ee00h,0000h   > dw offset new_1e,0008h,0ee00h,0000h,offset new_1f,0008h,0ee00h,0000h   > dw offset new_20,0008h,0ee00h,0000h,offset new_21,0008h,0ee00h,0000h   > dw offset new_22,0008h,0ee00h,0000h,offset new_23,0008h,0ee00h,0000h   > dw offset new_24,0008h,0ee00h,0000h,offset new_25,0008h,0ee00h,0000h   > dw offset new_26,0008h,0ee00h,0000h,offset new_27,0008h,0ee00h,0000h   > dw offset new_28,0008h,0ee00h,0000h,offset new_29,0008h,0ee00h,0000h   > dw offset new_2a,0008h,0ee00h,0000h,offset new_2b,0008h,0ee00h,0000h   > dw offset new_2c,0



Copyright © Linux教程網 All Rights Reserved