目錄 一、前景回顧 二、什麼是特權級檢查 三、門 四、如何進行特權級檢查 五、調用門的跳轉執行流程 六、調用門的跳轉許可權檢查 一、前景回顧 我們在前面講過保護模式較之於實模式的三大特點:分頁機制、特權級和分時機制。現在分頁機制的坑已經填好了,接下來我們開始填特權級的坑。 二、什麼是特權級檢查 首先我 ...
目錄
一、前景回顧
二、什麼是特權級檢查
三、門
四、如何進行特權級檢查
五、調用門的跳轉執行流程
六、調用門的跳轉許可權檢查
我們在前面講過保護模式較之於實模式的三大特點:分頁機制、特權級和分時機制。現在分頁機制的坑已經填好了,接下來我們開始填特權級的坑。
首先我們來看看什麼是特權級,CPU為了電腦的安全,將程式擁有的權力劃分為了4個等級,也就是特權級,特權級按照權力大小劃分為了0、1、2、3級,數字越小,權力越大。我們操作系統內核所占的特權級就是0級,處於至高無上的地位,而用戶的應用程式處於3級,最低特權級。不過在Linux中,實際只用上了0級和3級,即我們常說的用戶態便處於3級,內核態處於0級。
特權級檢查,說到底就是起到一個保護作用,操作系統為了避免用戶程式隨意訪問內核,修改內核數據,所以在“訪問者”訪問“受訪者”那一剎那,體現在實際中就是:段寄存器中的值被重新載入段選擇子時(因為我們已經進入保護模式),檢查訪問者的特權級和受訪者的特權級是否匹配。
在特權級中我們要認識一些特定術語:
1、CPL:處理器當前的特權級。
2、DPL:段或者門的特權等級,位於段描述符或者門描述符的DPL欄位。
3、RPL:請求特權級,位於選擇子的RPL欄位。
書上其實關於特權級的介紹還是比較詳細,只不過對於初學者來說,不是那麼好容易理解。我前前後後看了好幾遍才算是大體理解了。所以我嘗試用我自己的語言來描述一下特權級檢查,重點就是CPL、DPL和RPL之間的關係。
首先CPL是用來描述處理器當前的特權級的,我們的處理器只用上了0和3特權級,也就是說如果處理器在執行用戶態程式時,處理器此時的特權級就是3級,如果處理器此時處於內核態,那麼處理器的特權級就是0級。
DPL描述的是段描述符或者門描述符的訪問門檻,也就是說如果處理器的特權級低於目標段或者目標門的訪問門檻,那麼處理器就無法訪問段描述符或者門描述符描述的那段記憶體區域。我相信這個也是比較好理解的。
重點就是RPL,RPL表示的是請求資源的能力。這個其實是最不好理解的,咋一看RPL其實跟CPL應該是沒什麼區別的,因為書上都說,當處理器從一個特權級的代碼段A轉移到另一個特權級的代碼段B上運行時(這裡假設特權檢測通過),處理器的CPL就會變更為代碼段B的DPL,也就是目標代碼段描述符的DPL將保存在代碼段寄存器CS的RPL位,其實還是有區別的。
這裡我自己的理解是:RPL其實並不是指的處理器的請求資源能力,它應該是描述的段的請求資源的能力。我還是舉例子來說明,如果用戶通過中斷門從用戶態進入內核態(這個是很常見的低特權級進入高特權級的方式),此時CS段寄存器的CPL和RPL都應該是0,因為此時我們已經進入了內核態,無所不能了。如果用戶程式有壞心思,想將代碼段C(DPL為0)中存放的內核代碼拷貝到指定的數據緩衝區中,這個緩衝區本質上是一個數據段D。重點來了,不管這個數據段是來自用戶空間還是內核空間,當段選擇子載入到DS寄存器時,該段數據段的RPL都會被操作系統修改為用戶進程的CPL,也就是3。此時我們來看看特權檢查的規則:
請求某特權級為DPL級的資源時,參與特權檢查的不只是CPL,還要加上RPL,CPL和RPL的特權必須同時大於等於受訪者的特權DPL:
數值上CPL≤DPL並且RPL≤DPL
顯然該數據段就無法接收內核代碼,因為它的RPL為3,沒有這個能力。關於特權級更詳細的介紹我不在贅述,感興趣的朋友可以參考原書《操作系統真象還原》p229~p251。
“門結構”的存在是為了實現從低特權級到高特權級的轉變,CPU中實現特權級變換有四種門。門結構就是記錄一段程式起始地址的描述符。
他們的使用方式有些許不同:
1、任務門:任務門以任務段TSS位單位,用來實現任務切換。但是現代操作系統很少使用,我們後面也不用。
2、中斷門:以int指令主動發中斷的形式實現從低特權級到高特權級轉移,Linux系統調用便是使用此門實現的。
3、陷阱門:以int3指令主動發中斷的形式實現從低特權級到高特權級轉移,一般是在編譯器調試用。我們也不用。
4、調用門:call或jmp指令後接調用門選擇子作參數,以調用函數常式的方式實現從低特權級到高特權級轉移。
還是照搬書上的總結,當受訪者為數據時:
CPL <= 目標數據段DPL && RPL <=目標數據段DPL,即數據段不允許被比本數據段特權級更低的代碼段訪問。
當受訪者為代碼時,分為如下三種情況:
1、無門結構且目標為非一致性代碼段:CPL = RPL = 目標代碼段DPL
2、無門結構且目標為一致性代碼段:CPL >= 目標代碼段DPL && RPL >= 目標代碼段DPL
3、有門結構:DPL_GATE >= CPL >= DPL_CODE && RPL <= DPL_GATE
這裡以調用門的跳轉執行流程為例講解門的使用,先看圖:
調用門的門描述符依舊是占據4個位元組大小,被放置在GDT表中。用戶程式中通過“call 調用門選擇子”來訪問調用門,處理器通過選擇子中的高13位索引號加上GDT基址得到門描述符,在門描述符中又得到內核中被調用常式所在代碼段的選擇子以及偏移,又根據得到的選擇子在GDT表中索引得到內核中被調用常式所在代碼段的基址,將偏移量和基址結合就得到內核中被調用常式的地址。
援引書中的一個例子,能夠更好地說明調用門的特權級檢查:
假設當前處理器正在 DPL為3的代碼段上運行,即正在運行用戶程式,故處理器當前特權級CPL為 3。此時用戶進程想獲取安裝的物理記憶體大小,該數據存儲在操作系統的數據段中,該段DPL為0。由於當前運行的是用戶程式,CPL為3,所以無法訪問DPL為0的數據段。於是它使用調用門向系統救助。調用門是操作系統安裝在全局描述符表GDT中的,為了讓用戶進程可以使用此調用門,操作系統將該調用門描述符的DPL設為3。該調用門只需要一個參數,就是用戶程式用於存儲系統記憶體容量的緩衝區所在數據段的選擇子和偏移地址。調用門描述符中記錄的就是內核服務程式所在代碼段的選擇子及在代碼段內的偏移量。用戶進程用“call 調用門選擇子”的方式使用調用門,此調用門選擇子是由操作系統提供的,該選擇子的 RPL為3,此時如果用戶偽造一個調用門選擇子也沒用,因為此選擇子是用來索引門描述符的,並不用來指向緩衝區的選擇子,調用門選擇子中的高13位索引值必須要指向門描述符在GDT中的位置,選擇子中低2位的RPL偽造也沒意義,因為此時CPL為3,是短板,以它為主。此時處理器便進行特權級檢查,CPL為 3,RPL為3,門描述符DPL為3,即數值上(CPL≤DPL && RPL≤DPL)成立,初步檢查通過。接下來還要再將CPL與門描述符中選擇子所對應的代碼段描述符DPL比較,這是調用門對應的內核服務程式的DPL,為敘述方便將其記作DPL_CODE。由於DPL_CODE是內核程式的特權級,所以DPL_CODE為0,CPL為3,即數值上滿足CPL≥DPL_CODE,CPL比目標特權級低,檢查通過,該用戶程式可以用調用門,於是處理器的當前特權級CPL的值用DPL_CODE代替,記錄在CS.RPL中,此時CPL變為0。接下來,處理器便以0特權級的身份開始執行該內核服務程式,由於該服務程式的參數是用戶提交的緩衝區所在的數據段的選擇子及偏移量,為避免用戶將緩衝區指向了內核的數據區,安全起見,在該內核服務程式中,操作系統將這個用戶所提交的選擇子的RPL變更為用戶進程的CPL,也就是指向緩衝區所在段的選擇子的 RPL變成了3。前面說過,參數都是內核在0級棧中獲得的,雖然用戶進程將緩衝區的選擇子及偏移量壓在了3特權級棧中,但由於調用門的特權級變換,參數已經由處理器在固件一級上自動複製到0特權級棧中了。用戶的代碼段寄存器 CS 也在特權級發生變化時,由處理器自動壓入到0特權級棧中,所以操作系統需要的參數都可以在自己的0特權級棧中找到。用戶緩衝區的選擇子修改過後,接下來內核服務程式將用戶所需要的記憶體容量大小寫到這個選擇子和用戶提交的偏移量對應的緩衝區。如果用戶程式想搞破壞,所提交的這個緩衝區選擇子指向的目標段不是用戶進程自己的數據段,而是內核數據段或內核代碼段,由於目標段的DPL為0,雖然此時已在內核中執行,CPL為0,但選擇子RPL已經被改為3,數值上不滿足CPL≤DPL && RPL≤DPL,往緩衝區中的寫入被拒絕,處理器引發異常。如果用戶程式提交的緩衝區選擇子確實指向用戶程式自己的數據段,DPL則為3,數值上滿足CPL≤DPL && RPL≤DPL,往緩衝區中的寫入則會成功。如果中斷服務程式內部再有訪問內核自己記憶體段的操作,還會按照數值上(CPL≤DPL && RPL≤DPL)的策略進行新一輪的特權檢測。通常,如果不是用戶程式向內核提交緩衝區地址來接收數據的話,內核不會主動訪問用戶的記憶體段,多是訪問自己的數據段或代碼段,內核服務程式中若訪問內核自己的記憶體段,由於記憶體段的DPL為0,所以段選擇子的RPL也必須為0。
好了,本回到此結束了,一兩句話是講不清楚特權級的,要想搞懂還是得認真看書。預知後事如何,請看下回分解。