2021-05-12 14:32:11
在 Linux x86-32 模式下分析記憶體對映流程
前言
虛擬記憶體機制已經成為了現代作業系統所不可缺少的一部分, 不僅可以為每個程式提供獨立的地址空間保證安全性,更可以通過和磁碟的記憶體交換來提高記憶體的使用效率。虛擬記憶體管理作為Linux 上的重要組成部分程式碼非常龐大。這次並不是探明 Linux 原始碼級的記憶體對映,而是通過範例來驗證 x86-32 下的虛擬記憶體轉換流程。
對映流程簡述
x86-32 模式下的記憶體對映分為2部分, 分段和分頁。之所以使用 2 步對映更多的是歷史相容原因。
編譯出的組合程式碼裡使用的是邏輯地址,表示形式為 [段識別符號:段內偏移量], 在預設情形下可以省略段識別符號,直接給出段內偏移即可。段識別符號共有 6 個,分別是(CS, DS, SS, ES, FS, GS), 每個都有自己的含義。
邏輯地址經過 "分段" 會轉換為 線性地址,從我之前的文章可以看出,分段機制現在已經不實際使用了,在linux 中使用的分段模式為 “扁平模式”, 即邏輯地址和線性地址是一樣的。
從線性地址轉換為實際實體地址的過程稱為 "分頁"。分頁才是實際的虛擬地址轉換。分頁過程中使用了可疊代的頁表機制,作業系統為每個程式維護獨立的一組頁表來保證程式之間的互不干涉。
因為本文是反向驗證的流程,所以並不會仔細介紹整個對映流程,需要對虛擬記憶體機制有一定的了解。
驗證方案
本文整個流程參考了網上的另一篇文章,我會在文章末尾列出連結。
因為每個程式的相關資源都是獨立的,必須要保證程式的執行不能終止,且要在程式中輸出自己的相關暫存器狀態。大部分暫存器的存取特權只支援在核心獲取,不能在使用者程式中獲取,我們要編寫相關模組執行於核心,通過linux 的 /proc 檔案系統將引數傳遞到使用者程式。
同時也要驗證我們通過手動對映流程獲得的實體地址是否就是程式內地址,要有工具能夠直接檢視指定實體地址的資料。我們編寫一個字元裝置來處理應用程式的請求,通過 kmap 函數將指定實體地址頁臨時對映獲取其資料。
最終需要程式為 4 個,如下所示。
程式 | 功能 |
---|---|
sys_reg.ko | 載入到核心,讀取相關暫存器, 建立 /proc/sys_reg 檔案 |
running-prog | 測試程式,需要一直執行,讀取 /proc/sys_reg 來列印本程式相關暫存器值 |
phy_mem.ko | 載入到核心,讀取指定實體地址資料,建立 /dev/phy_mem 檔案 |
read-phy-mem | 通過 /dev/phy_mem 來獲取指定實體地址資料並列印 |
驗證過程
編譯載入
編譯檔案,載入 sys_reg.ko, phy_mem.ko 模組
執行 running-prog
執行後可以得到以下輸出:
可以看到變數 a, 這就是我們要尋找實體地址的變數,其資料和地址都以及輸出了。
分段機制
通過 CR0.PG ,可以看出系統已經開啟了分頁機制。 變數 a 定在了資料段, 通過 ds 段暫存器的值可以看出使用的是 GDT ,entry 是 15. GDTR 的基址是 0xF7386000, 注意這裡是線性地址, linux 核心的地址對映偏移量是 0xC0000000, 然後獲得 使用的 GDT entry地址如下。
0xF7386000 - 0xC0000000 + 15 * 8 = 0x37386078
通過獲得的 GDT entry值和 gdt entry 格式,可以知道該分段的引數:
name | 值 |
---|---|
base | 0x00000000 |
limit | 0xfffff |
G | 1 |
可以看出段基址是 0x00000000, G和limit決定了該段是 4G 大小。所以從邏輯地址獲得變數 a 的線性地址如下:
0x0804A044 + 0x00000000 = 0x0804A044;
分頁機制
因為 CR4.PAE = 1,說明系統開啟了 PAE(實體地址擴充套件). PAE 模式下的分頁有如下 2 種。
暫存器及 entry 格式如下:
此時 CR3 中的基址就是 PDPTE 的基址 0x1EF49000, 變數 a 線性地址的bit 31-30 代表了 PDPTE 的序號。 我們可以算出 使用的 PDPTE 地址:
0x1EF49000 + 0 * 8 = 0x1EF49000
可以看到 page directory 的基址為 0x1ec9f000, 使用線性地址中的 bits 29~ 21 來確定偏移為 0x40, 所以使用的 PDE 地址為
0x1EC9F000 + 0x40 * 8 = 0x1EC9F200
PDE 為 0x0000000020A36067, bit7 = 0,說明指向的是 page table, page table 地址為0x20A36000, 使用線性地址的 bits 20~12 作為偏移為 0x4A, 使用的 PTE 地址為
0x20A36000 + 0x4A * 8 = 0x20A36250
PTE 為 0x000000000B628067, 得到了最終的 4K page frame 基址為 0x0B628000, 使用線性地址的 bits 11~0 作為偏移為 0x44, 我們計算出的 變數 a 的實體地址為
0x0B628000 + 0x44 = 0x0B628044
我們看到了資料 0x013579BB, 說明我們正確找到了 a 的實體地址,反向驗證了 linux 在 x86-32 模式下開啟了 PAE 後的線性地址對映。
結束
感謝 Linux記憶體地址對映 一文,我的整個流程參考了原作者的文件和程式碼, 謝謝原作者的分享。
下一篇討論 在 Linux x86-64 模式下分析記憶體對映流程 http://www.linuxidc.com/Linux/2015-02/113078.htm
下面是原始碼下載. study-linux-vm-32bit
------------------------------------------分割線------------------------------------------
免費下載地址在 http://linux.linuxidc.com/
使用者名稱與密碼都是www.linuxidc.com
具體下載目錄在 /2015年資料/2月/8日/在 Linux x86-32 模式下分析記憶體對映流程/
下載方法見 http://www.linuxidc.com/Linux/2013-07/87684.htm
------------------------------------------分割線------------------------------------------
使用方式
make
make install
載入模組
sudo insmod ./output/sys_reg.ko
sudo insmod ./output/phy_mem.ko
running-prog
./output/running-prog
read-phy-mem
因為讀寫 /dev/phy_mem 裝置的許可權問題請使用 sudo 執行.
sudo ./read-phy-mem addr len
status
本程式在 i386 linux mint 14, kernel 3.5.0-17 模式下測試通過.
相關文章