1. 程式人生 > >進入保護模式-彙編學習之路

進入保護模式-彙編學習之路

在真實模式下由於對記憶體的保護不是特別的完善,一個段可以任意訪問訪問不是該段範圍內的記憶體。例如以下一個小例子

mov cx,0x8000

push ds

mov ds,cx

mov [0x05],dx

pop ds

這個例子就說明了先儲存當前的資料段地址之後在修改其他段的記憶體,之後再返回當前的資料段。也就說明一個問題在真實模式下可以任意對記憶體進行訪問這樣做是很危險的例如寫一個程式從而達到修改核心程式碼等等。所以引入了保護模式。

在保護模式下對記憶體的訪問仍然是短地址加上段內偏移,但是在訪問前必須是已經註冊好的段切偏移量未超過該註冊段的段界限。但是如何註冊一個段呢,註冊在哪裡呢,怎麼註冊。所以本次筆記的重要角色出場了,即全域性描述符,有它來對一個要註冊的段進行描述,以下圖片是全域性描述符的結構(4位元組大小)

很明顯描述符指定了一個4位元組的段基址和20位的段界限,在真實模式下段基址是邏輯地址,左移4位加上偏移量才是真正的實體地址,而在32位保護模式下段基址是32位的線性地址,在未開啟分頁模式,可以認為就是實體地址。段基址可以是0-4g中的任意一個,但最好是以16位元組對其。段界限是對一個段的擴充套件,因為訪問記憶體是段基址加上段內偏移,所以對於向上擴充套件的段例如資料段,則段界限限定了一個段的上限,最小偏移為0,但對於向下擴充套件的段,例如棧段,則段界限限制了段的最小值,即最小偏移量。

G:代表粒度,如果為0,則段界限以位元組為單位,因為段界限是20位所以段界限的大小為0-1mb。如果為1,則段界限以4kb為單位,其大小為0-4gb

D/B:代表預設運算元大小或者預設棧指標大小或者上部邊界標誌。

該標誌位對於不同的段有著不同的含義,對於程式碼段,此標誌位為D位,D=0則表示指令中的偏移地址或者運算元是16位的,D=1則為32位的

對於棧段,此標誌位為B位,B=0,則預設棧指標位SP,或者決定棧的上部邊界為0xffff,B=1,則預設棧指標為ESP,或者決定棧的上部邊界為0xffffffff

AVL:

P:段存在位

DPL:段的特權級別

S:指定描述符的屬性,為0時則說明該段為系統段,為1時表示程式碼段或者資料段。

TYPE:表示該段的屬性

介紹完全域性描述符的結構後該說明如何讓計算機知道我們定義了各個段,這需要一個指令lgdt和一個暫存器GDTR。

首先說明暫存器GDTR 是一個6位元組的暫存器稱為全域性描述符暫存器 其高32位為全域性描述符的基址低16位為全域性描述符的界限,因為地址是從0開始算起所以全域性描述符的界限為實際大小減一。

lgdt 指令 定義為 lgdt m48  該指令的運算元是一個6位元組記憶體區域的 高4位元組為全域性描述符的基址低2位元組為全域性描述符的界限。

接下來是記錄利用全域性描述符來進行記憶體訪問的程式碼

      ;設定堆疊段和棧指標
         mov ax,cs     
         mov ss,ax
         mov sp,0x7c00
     
         ;計算GDT所在的邏輯段地址
         mov ax,[cs:gdt_base+0x7c00]        ;低16位
         mov dx,[cs:gdt_base+0x7c00+0x02]   ;高16位
         mov bx,16       
         div bx           
         mov ds,ax                          ;令DS指向該段以進行操作
         mov bx,dx                          ;段內起始偏移地址
     
         ;建立0#描述符,它是空描述符,這是處理器的要求
         mov dword [bx+0x00],0x00
         mov dword [bx+0x04],0x00 

         ;建立#1描述符,保護模式下的程式碼段描述符
         mov dword [bx+0x08],0x7c0001ff    
         mov dword [bx+0x0c],0x00409800    

         ;建立#2描述符,保護模式下的資料段描述符(文字模式下的顯示緩衝區)
         mov dword [bx+0x10],0x8000ffff    
         mov dword [bx+0x14],0x0040920b    

         ;建立#3描述符,保護模式下的堆疊段描述符
         mov dword [bx+0x18],0x00007a00
         mov dword [bx+0x1c],0x00409600

         ;初始化描述符表暫存器GDTR
         mov word [cs: gdt_size+0x7c00],31  ;描述符表的界限(總位元組數減一)  
                                            
         lgdt [cs: gdt_size+0x7c00]
     
         in al,0x92                         ;南橋晶片內的埠
         or al,0000_0010B
         out 0x92,al                        ;開啟A20

         cli                                ;保護模式下中斷機制尚未建立,應
                                            ;禁止中斷
         mov eax,cr0
         or eax,1
         mov cr0,eax                        ;設定PE位
     
         ;以下進入保護模式... ...
         jmp dword 0x0008:flush             ;16位的描述符選擇子:32位偏移
                                            ;清流水線並序列化處理器
         [bits 32]

    flush:
         mov cx,00000000000_10_000B         ;載入資料段選擇子(0x10)
         mov ds,cx

         ;以下在螢幕上顯示"Protect mode OK."
         mov byte [0x00],'P' 
         mov byte [0x02],'r'
         mov byte [0x04],'o'
         mov byte [0x06],'t'
         mov byte [0x08],'e'
         mov byte [0x0a],'c'
         mov byte [0x0c],'t'
         mov byte [0x0e],' '
         mov byte [0x10],'m'
         mov byte [0x12],'o'
         mov byte [0x14],'d'
         mov byte [0x16],'e'
         mov byte [0x18],' '
         mov byte [0x1a],'O'
         mov byte [0x1c],'K'

         ;以下用簡單的示例來幫助闡述32位保護模式下的堆疊操作
         mov cx,00000000000_11_000B         ;載入堆疊段選擇子
         mov ss,cx
         mov esp,0x7c00

         mov ebp,esp                        ;儲存堆疊指標
         push byte '.'                      ;壓入立即數(位元組)
        
         sub ebp,4
         cmp ebp,esp                        ;判斷壓入立即數時,ESP是否減4
         jnz ghalt                         
         pop eax
         mov [0x1e],al                      ;顯示句點
     
  ghalt:    
         hlt                                ;已經禁止中斷,將不會被喚醒

;-------------------------------------------------------------------------------
    
         gdt_size         dw 0
         gdt_base         dd 0x00007e00     ;GDT的實體地址
                            
         times 510-($-$$) db 0
                          db 0x55,0xaa

對於為什麼全域性描述符一開始要寫在0x00007e00處是因為主引導區程式碼是在0x00007c00處加上大小512位元組(0x200)所以就是0x00007e0000

第一次筆記就先按到這裡以後會繼續努力的加油了