一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

Linux|Centos|Ubuntu|系統進程|Fedora|注冊表|Bios|Solaris|Windows7|Windows10|Windows11|windows server|

服務器之家 - 服務器系統 - Linux - Linux從頭學:x86 處理器如何進行-層層的內存保護?

Linux從頭學:x86 處理器如何進行-層層的內存保護?

2021-08-30 23:25IOT物聯網小鎮道哥 Linux

這篇文章,我們來看一下 bootloader 是如何來進行自我進化到保護模式的,然后深入看一下保護模式是如何對內存進行安全保護的。

Linux從頭學:x86 處理器如何進行-層層的內存保護?
  • 實模式:bootloader 為程序計算段的基地址
  • 保護模式:bootloader 為自己創建段描述符
    • 確定 GDT 的地址
    • 創建代碼段的描述符
    • 創建數據段的描述符
    • 創建棧段的描述符
  • 段描述符是如何確保段的安全的?
    • 段寄存器高速緩存
    • 對段寄存器本身的保護
    • 對段界限的檢查

在上一篇文章中,我們已經順利的從實模式,過渡到了保護模式。

保護模式與實模式最本質的區別就是:保護模式使用了全局描述符表,用來保存每一個程序(bootloader,操作系統,應用程序)使用到的每個段信息:開始地址,長度,以及其他一些保護參數。

這篇文章,我們來看一下 bootloader 是如何來進行自我進化到保護模式的,然后深入看一下保護模式是如何對內存進行安全保護的。

作為背景知識,我們先來看一下 x86 中的地址變換過程:

Linux從頭學:x86 處理器如何進行-層層的內存保護?

x86 處理器中的分頁機制是可以被關閉的,此時線性地址就等于物理地址,這也是我們一直討論的情況。

下一篇文章,我們就把 x86 中的分頁機制打開,并與 Linux 中的分段和分頁機制進行對比。

實模式:bootloader 為程序計算段的基地址

在之前的文章:Linux從頭學06:16張結構圖,徹底理解【代碼重定位】的底層原理中,我們討論了 bootloader 是如何把應用程序讀取到內存中,最后跳入到程序的入口地址的。

這里所說的程序,可以是操作系統,也可以是應用程序。

下面這張圖,是程序被加載到內存中之后,header 中的信息:

Linux從頭學:x86 處理器如何進行-層層的內存保護?

因為程序是被 bootloader 動態讀取到內存中的,它是不知道自己被放在內存中的什么位置,因此它也不知道自己代碼段、數據段、棧的開始地址。

但是,程序要想能夠正常執行,就必須要知道這些信息,那怎么辦?

只有 bootloader 才能解決問題,因為是它來把程序從硬盤加載到內存中的。

因此,bootloader 在跳入程序的入口地址之前,必須把其中的代碼段、數據段、棧段的基地址計算出來,然后寫入到程序的 header 中,如下圖所示:

Linux從頭學:x86 處理器如何進行-層層的內存保護?

這樣的話,程序開始執行時,就可以從自己的 header 中獲取到這 3 個段基地址,并且賦值給相應的寄存器,從而順利的執行程序。

也就是說:程序的 header 空間,充當了 bootloader 與它進行信息交互的媒介,用來傳遞 3 個段寄存器的基地址。

以上的這個過程,一直工作在實模式,因此就沒有段描述符什么事情。

在以后文章中,我們還會看到在保護模式下,bootloader 仍然會利用 OS 的 header 空間,來傳遞段的索引號。然后 OS 利用這個段索引號,去查找 GDT 表,從而找到每一個段的基地址以及其他一些保護信息。

保護模式:bootloader 為自己創建段描述符

bootloader 從 BIOS 接管系統之后,剛開始是運行在實模式下的。

當它完成一些準備工作之后,就可以進入保護模式了,也就是把 CR0 寄存器的 bit0 設置為 1。

這個準備工作中,最重要的就是:建立 GDT 這個表,并且把 GDT 的開始地址,存儲到寄存器 GDTR 中。

下面這張圖,是 bootloader 被加載到內存中的布局圖:

Linux從頭學:x86 處理器如何進行-層層的內存保護?

bootloader 被加載到 0x0000_7C00 地址處。

它最少需要創建 3 個段描述符:代碼段、數據段和棧段。

確定 GDT 的地址

在創建段描述符之前,需要先確定: 把 GDT 表放在內存中的什么位置?

暫且就把它放在 0x0001_0000 這個地址吧,距離零地址 64K 的位置。

按照處理器的要求,在第 1 個表項(稱之為 item 或者 entry,每本書上都不一樣)必須為空描述符(index = 0)。

Linux從頭學:x86 處理器如何進行-層層的內存保護?

創建代碼段描述符

bootloader 的代碼放在 0x0000_7C00 開始的地址,長度是 512B。

根據這些信息,就可以構造出代碼段的描述符了:

Linux從頭學:x86 處理器如何進行-層層的內存保護?

創建數據段描述符

bootloader 待會需要把操作系統或其他應用程序,從硬盤讀取到內存中,例如:讀取到 0x0002_0000 的位置。

那么 bootloader 就必須能夠訪問到這個位置,并且是以數據段的讀寫方式。

為了利用全部的 4G 內存空間,bootloader 可以把這 4G 空間,作為一個數據段來定義它的描述符,如下:

Linux從頭學:x86 處理器如何進行-層層的內存保護?

創建棧段描述符

理論上,bootloader 可以使用內存中的任意一塊空閑空間,來作為自己的棧。

因為棧在 push 操作的時候,是向低地址方向增長的。

因此很多書籍都會把棧頂基地址設置為 bootloader 的開始地址,也就是 0x0000_7C00 地址處,并且把棧的空間大小限制在 4K 的范圍。

Linux從頭學:x86 處理器如何進行-層層的內存保護?

根據以上這些信息,就可以創建出棧的段描述符,如下:

Linux從頭學:x86 處理器如何進行-層層的內存保護?

當以上這幾個段的描述符都創建好之后,就可以把 GDT 的地址(0x0001_0000),設置到 GDTR 寄存器中了。

最后,再把 CR0 寄存器的 bit0 設置為 1,就正式的進入保護模式來執行 bootloader 中后面的代碼了。

段描述符是如何確保段的安全訪問的?

段寄存器高速緩存

進入保護模式之后,雖然對段寄存器中內容的解釋改變了,但是執行每一條指令,還是需要使用到這些段寄存器的: cs, ds, ss等等。

想象一下:每執行一條指令,都會從邏輯地址中,獲取到段索引號,然后去查找 GDT 表,從而定位到段的基地址。

大家都知道程序有個“局部性”原理,也就是連續執行的代碼,都是集中在一段連續的程序空間中的。

這個連續的程序空間,它們都是在同一個代碼段中,因此段的基地址都是相同的,那么它們都屬于 GDT 中同一個代碼段描述符所代表的段空間。

如果每一條指令都去查表,就會影響到程序的執行效率。

所以,處理器內部就為每一個段寄存器,安排了一個高速緩存。

拿代碼段寄存器 cs 來說:當執行一條指令的時候,如果它與上一條指令中的段索引號不同,才會根據新的段索引號到 GDT 中查找相應的段描述符表項。

查找到之后,就把這個表項的內容復制到 cs 寄存器的高速緩存中。

當繼續執行后面的指令時,如果邏輯地址中的段索引號沒有變化,處理器就直接從高速緩存中讀取段描述,從而避免了查表操作,提升了系統效率。

對段寄存器本身的保護

當邏輯地址中段寄存器的索引號改變時,就會根據新的索引號,到 GDT 中去查表。

當然了,這個索引號不能超過 GDT 的界限。

當定位到某一個描述符表項之后,就開始進行一系列檢查。

再來看一下每一個段描述符中 8 個字節的內容:

Linux從頭學:x86 處理器如何進行-層層的內存保護?

bit8 ~ bit11 定義了當前這個段的類型。

假如: 我們在切換代碼段空間的時候,不小心犯錯,定位到了 GDT 中的一個數據段描述符表項,那么處理器就能夠及時發現:

“當前這個段描述符的類型是數據段,你卻把它當做代碼段來使用,禁止,殺無赦!”

因此,處理器就會拒絕把這個段描述符復制到代碼段的高速緩存中,從而對代碼段寄存器進行了保護。

對段界限的檢查

在通過了第一層的段類型保護之后,還會繼續對段的界限進行檢查,這就要使用到邏輯地址中的偏移地址( EIP )了。

如果偏移地址超過了描述符中規定的界限,那么就說明發生錯誤了。

例如:在 bootloader 的代碼段描述符中,最大的界限是 512B,如果把 EIP 設置為 0x0000_1000,那就肯定錯誤了。

因為這個地址壓根就不屬于代碼段的空間范圍。

對于數據段來說比較有意思,因為我們把數據段描述符的基地址設置為 0x0000_0000,段的界限是整個 4G 的空間,所以它可以對整個內存進行操作。

多想一步:

代碼段也是屬于這 4G 空間,因此可以通過數據段,來改寫代碼段空間中的指令內容。

也就是說:如果你想修改代碼段的指令,直接通過代碼段來操作是不可以的。

因為代碼段描述符中規定了:代碼段的內容只能被讀取、執行,但是不能被寫入。

此時,就可以另辟蹊徑:代碼段也放在 4G 的空間,那么就可以通過數據段的可寫特性,來改寫代碼段中的指令。

想一想 gdb 的調試過程,是不是就利用了這個道理?

原文鏈接:https://mp.weixin.qq.com/s/qzVcPfjVQt5yNfRPrrTwzQ

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产99热99| 日本h乱淫动漫在线观看 | 成人福利免费在线观看 | 日日操美女 | 香蕉视频在线观看网址 | 狠狠干2017 | 色一情一区二区三区四区 | 国产99视频精品免费视频7 | 99精品影院| 九九热视频免费观看 | 91尤物在线视频 | 久久三级网站 | 校草让我脱了内裤给全班看 | 亚洲国产精品综合一区在线 | 国产高清在线不卡 | 亚洲精品老司机福利在线播放 | 免费观看俄罗斯特黄特色 | 人成午夜免费大片在线观看 | 男插女的下面免费视频夜色 | 韩国美女被的免费视频 | 毛片免费毛片一级jjj毛片 | 色一情一区二区三区四区 | 91在线亚洲综合在线 | 国产自拍资源 | 黑人巨荃大战乌克兰美女 | 亚洲国产日韩成人综合天堂 | 精品国产午夜久久久久九九 | 肉色欧美久久久久久久蜜桃 | 亚洲欧美成人综合久久久 | 亚洲国产精品综合久久一线 | 色婷婷婷丁香亚洲综合不卡 | 国产成年人网站 | 羞羞在线观看 | 99久热只有精品视频免费观看17 | 女娃开嫩苞经历小说 | 男人桶女下面60分钟视频 | 门房秦大爷在线阅读 | 关晓彤被调教出奶水的视频 | 91制片厂制作传媒网站破解 | 亚洲国产精品日韩高清秒播 | 欧美日韩高清一区 |