欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

《Orange’s 一個操作系統(tǒng)的實現(xiàn)》3.保護模式5-

系統(tǒng) 1953 0

學(xué)習(xí)過程中遇到一個對保護模式總結(jié)很好的Blog,轉(zhuǎn)來分享一下。

先說下特權(quán)級的概念,在保護模式下,系統(tǒng)依靠特權(quán)級來實施代碼和數(shù)據(jù)的保護,相當(dāng)于權(quán)限啦。特權(quán)級共有4個級別,0,1,2,3,數(shù)字越小表示權(quán)限越高。如圖:?

保護模式 對CPL,RPL,DPL的總結(jié) - xuejianxinokok - xuejianxinokok的博客
???
較為核心的代碼和數(shù)據(jù)放在較高(靠內(nèi))的層級中,處理器用此來防止較低特權(quán)的任務(wù)在不被允許的情況下訪問處于高特權(quán)級的段。為了防止概念混淆,我們不用特權(quán)級大小來說明,改為內(nèi)層(高),外層(低)來講。

特權(quán)級有3種 CPL , DPL RPL ,每個都是有4個等級。?
我對他們的 關(guān)系 理解是這樣:一般來說, CPL 代表當(dāng)前代碼段的權(quán)限,如果它想要去訪問一個段或門,首先要看看對方的權(quán)限如何,也就是檢查對方的 DPL ,如果滿足當(dāng)前的權(quán)限比要訪問的權(quán)限高,則有可能允許去訪問,有些情況我們還要檢查?
選擇子的權(quán)限,即 RPL ,因為我們通過選擇子:偏移量的方式去訪問一個段,這算是一個訪問請求動作,因此?
稱為請求訪問權(quán)限 RPL (Requst Privilege Level)。當(dāng)請求權(quán)限也滿足條件,那么訪問就被允許了。
?

CPL (Current Privilege Level)?
CPL 是當(dāng)前執(zhí)行的任務(wù)的特權(quán)等級,它存儲在CS和SS的第0位和第1位上。(兩位表示0~3四個等級)?
通常情況下, CPL 等于代碼所在段的特權(quán)等級,當(dāng)程序轉(zhuǎn)移到不同的代碼段時,處理器將改變 CPL 。?
注意:在遇到 一致代碼 段時,情況特殊, 一致代碼 段的特點是:可以被 等級相同或者更低特權(quán)級 的代碼訪問,當(dāng)處理器訪問一個與當(dāng)前代碼段 CPL 特權(quán)級不同的 一致代碼 段時, CPL 不會改變。?

DPL (Descriptor Privilege Level) ?
表示門或者段的特權(quán)級,存儲在門或者段的描述符的 DPL 字段中。正如上面說的那樣,當(dāng)當(dāng)前代碼段試圖訪問一個段或者門時,其 DPL 將會和當(dāng)前特權(quán)級 CPL 以及段或門的選擇子比較,根據(jù)段或者門的類型不同, DPL 的含義不同:?
??? 1.數(shù)據(jù)段的 DPL :規(guī)定了訪問此段的最低權(quán)限。比如一個數(shù)據(jù)段的 DPL 是1,那么只有運行在 CPL 為0或1的程序才可能訪問它。為什么說可能呢?因為還有一個比較的因素是 RPL 。訪問數(shù)據(jù)段要滿足有 效特權(quán)級別 (上述) 高于數(shù)據(jù)段的 DPL .?
??? 2.非 一致代碼 段的 DPL (不使用調(diào)用門的情況): DPL 規(guī)定訪問此段的特權(quán),只有 CPL 與之相等才有可能訪問。?
??? 3.調(diào)用門的 DPL ,規(guī)定了程序或任務(wù)訪問該門的最低權(quán)限。與數(shù)據(jù)段同。?
??? 4. 一致代碼 段和通過調(diào)用門訪問的非 一致代碼 段, DPL 規(guī)定訪問此段的最高權(quán)限。?
???? 比如一個段的 DPL 為2,那么 CPL 為0或者1的程序都無法訪問。?
?? 5. TSS的 DPL ,同數(shù)據(jù)段。?

RPL (Rquest Privilege Level)
?
RPL 是通過選擇子的低兩位來表現(xiàn)出來的( 這么說來,CS和SS也是存放選擇子的,同時 CPL 存放在CS和SS的低兩位上,那么對CS和SS來說,選擇子的 RPL =當(dāng)前段的 CPL )。處理器通過檢查 RPL CPL 來確認(rèn)一個訪問是否合法。即提出訪問的段除了有足夠的特權(quán)級 CPL ,如果 RPL 不夠也是不行的(有些情況會忽略 RPL 檢查)。?
為什么要有 RPL ?
操作系統(tǒng)往往通過設(shè)置 RPL 的方法來避免低特權(quán)級的應(yīng)用程序訪問高特權(quán)級的內(nèi)層數(shù)據(jù)。?
例子情景:調(diào)用者調(diào)用操作系統(tǒng)的某過程去訪問一個段。?
當(dāng)操作系統(tǒng)(被調(diào)用過程)從應(yīng)用程序(調(diào)用者)接受一個選擇子時,會把選擇子的 RPL 設(shè)置稱調(diào)用者的權(quán)限等級,于是操作系統(tǒng)用這個選擇子去訪問相應(yīng)的段時(這時 CPL 為操作系統(tǒng)的等級,因為正在運行操作系統(tǒng)的代碼),處理器會使用調(diào)用者的特權(quán)級去進行特權(quán)級檢查,而不是正在實施訪問動作的操作系統(tǒng)的特權(quán)級( CPL ),這樣操作系統(tǒng)就不用以自己的身份去訪問(就防止了應(yīng)用去訪問需要高權(quán)限的內(nèi)層數(shù)據(jù),除非應(yīng)用程序本身的權(quán)限就足夠高)。?
那么 RPL 的作用就比較明顯了:因為同一時刻只能有一個 CPL ,而當(dāng)?shù)蜋?quán)限的應(yīng)用去調(diào)用擁有至高權(quán)限的操作系統(tǒng)的功能來訪問一個目標(biāo)段時,進入操作系統(tǒng)代碼段時 CPL 變成了操作系統(tǒng)的 CPL ,如果沒有 RPL ,那么權(quán)限檢查的時候就會用 CPL ,而這個 CPL 權(quán)限比應(yīng)用程序高,也就可能去訪問需要高權(quán)限才能訪問的數(shù)據(jù),這就不安全了。所以引入 RPL ,讓它去代表訪問權(quán)限,因此在檢查 CPL 的同時,也會檢查 RPL .一般來說如果 RPL 的數(shù)字比 CPL 大(權(quán)限比 CPL 的低),那么 RPL 會起決定性作用。?
說這么多不明白都不行啦~真累?
下面是引用的一個超棒的關(guān)于權(quán)限控制的總結(jié):?
引用地址 ?
還有一篇文章 在此 。?

■ 數(shù)據(jù)訪問時的權(quán)限check

一、 訪問data segment時(ds、es、fs 及gs)?
1、 程序指令要訪問數(shù)據(jù)時,data segment selector 被加載進 data segment register(ds、es、fs 和 gs)前,處理器會進行一系列的權(quán)限檢查,通過了才能被加載進入segment register。處理器分為兩步進行檢查:?
★? CPL (當(dāng)前程序運行的權(quán)限級別)與 RPL (位于selector中的? RPL )作比較,并設(shè)置有效權(quán)限級別為低權(quán)限的一個。?
★ 得出的有效權(quán)限級別與 DPL (segment descriptor 中的 DPL )作比較,有效權(quán)限級別高于 DPL ,那么就通過。低于就不允許訪問。
?
2、舉個例子:?
如果: CPL ?= 3、 RPL ?= 2、 DPL ?= 2。那么
EPL =? CPL ?>? RPL ??? CPL ?:? RPL ;?
if (EPL <=? DPL ) {?
/* 允許訪問 */?
} else {?
/* 失敗,#GP 異常生產(chǎn),執(zhí)行異常 */
}
?
或者:?
if (( CPL ?<=? DPL ) && ( RPL ?<=? DPL )) {?
/* 允許訪問 */?
} else {?
/* 失敗,#GP 異常生產(chǎn),執(zhí)行異常 */?
}

?? 也就是要訪問目標(biāo)data segment,那么必須要有足夠的權(quán)限,這個足夠的權(quán)限就是:當(dāng)前運行的權(quán)限級別及選擇子的請求權(quán)限級別要高于等于目標(biāo)data segment的權(quán)限級別。

?

二、 訪問stack segment時?
1、 該問stack 時的權(quán)限檢查更嚴(yán)格, CPL RPL DPL 三者必須相等才能通過該問請求。

2、 舉個例子:

?

?

if ( CPL ?==? RPL ?&&? RPL ?==? DPL ?&&? CPL ?==? DPL ) {?
????? /* 允許訪問 */?
} else {?
???? /* 失敗,#GP 異常生產(chǎn),執(zhí)行異常 */?
}

?

也就是說每個權(quán)限級別有相對應(yīng)的statck segment。不能越權(quán)訪問,即使高權(quán)限訪問低權(quán)限也是被拒絕的

?

?

?

■ 控制權(quán)的轉(zhuǎn)移及權(quán)限檢查。

?? 權(quán)限檢查的4個要素:

★? CPL :處理器當(dāng)前運行的級別,也就是:當(dāng)前 CS 的級別,在 CS 的 BIT0 ~ Bit1

★? DPL :訪問目標(biāo)代碼段所需的級別。定義在 segment descriptor 的? DPL ?域中

★? RPL : 通過 selector 進行訪問時,selector 內(nèi)定義的級別。

★ conforming/nonconforming:目標(biāo)代碼屬于 nonconforming 還是 conforming 定義在segment descritptor 的 C 標(biāo)志位中

x86 的各方面檢查依賴于目標(biāo)代碼段是 nonconforming(不一致) 還是 conforming (一致) ?類型


一、 直接轉(zhuǎn)移(far call 及 far jmp)?
??? 1、 直接轉(zhuǎn)移定義為不帶gate selector或 taskselector的遠(yuǎn)調(diào)用。當(dāng)執(zhí)行一條 call cs:eip 或 jmp cs:eip 指令時,cs 是目標(biāo)代碼段的selector,處理器在加載指令操作數(shù)中的cs進cs register前,要進行一系列的權(quán)限檢查,控制權(quán)的轉(zhuǎn)移權(quán)限分兩部分,根據(jù)目標(biāo)代碼段descriptor定義的兩種情況:?
1)、nonconforming target code segment?
★ 直接轉(zhuǎn)移后的權(quán)限級別是不能必改變的。因此, CPL ?必須要等于目標(biāo)代碼段的? DPL 。?
★ 要有足夠的請求權(quán)限進行訪問。因此,目標(biāo)代碼段選擇子的 RPL ?<=? CPL

2)、conforming target code segment?
★ conforming code segment 允許訪問高權(quán)限級別的代碼。這里只需檢查? CPL ?>=? DPL ?即可, RPL ?忽略不檢查。?
★ 轉(zhuǎn)移后 CPL 不會改變。

?? 2、 以上兩步通過后,處理器加載目標(biāo)代碼段的CS 進入CS register,但權(quán)限級別不改變,繼而 RPL 被忽略。

★ 處理器根據(jù)CS selector在相應(yīng)的descriptor table 找到 code segment descriptor。CS 的Bit2(TI域) 指示在哪個descriptor table 表查找,CS.TI = 0 時在GDT查找,CS.TI = 1時在LDT 查找。?
★ CS的Bit15~Bit3 是selector index 值,處理器基于GDT或LDT來查找segment descriptor。具體是:GDTR.base 或 LDTR.base + CS.SI × 8 得出code segment descritpro。?
★ 處理器自動加載code segment descriptor 的 base address、segment limit及attribute 域進入 CS register的相應(yīng)的隱藏域。?
★ 轉(zhuǎn)到CS.base + eip 處執(zhí)行指令

總結(jié):用代碼形式來說明直接轉(zhuǎn)移 call cs:eip 這條指令?
例: call 1a:804304c (即cs = 1a, eip = 804304c)

?

?

target_cs = 1a;?
target_eip = 0x0804304c;?
CPL ?= CS. RPL ;??????????? /* 當(dāng)前執(zhí)行的代碼段的權(quán)限級別就是 CPL ?*/?
RPL ?= target_cs. RPL ;???? /* 目標(biāo)段 selector 的低3位是 RPL ?*/?
target_si = target_cs.SI;??? /* 目標(biāo)段 selector 的索引是Bit15~Bit3 */?
target_ti = target_cs.TI;??? /* 目標(biāo)段selector的描述符表索引是Bit2 */
CODESEG_DESCRIPTOR target_descriptor;

if (target_ti == 0) { /* target_cs.TI為0 就是參考到 GDT(全局描述符表) */?
/* 以GDTR寄存器的base 為基地址加上selector的索引乘以8即得出目標(biāo)?
?????? 段描述符,目標(biāo)描述符的 DPL 就是目標(biāo)段所需的訪問權(quán)限 */?
??? target_descriptor = GDTR.base + target_si * 8

} else {?????????????? /* 否則就是參考 LDT (局部描述符表)*/?
??? /* 以 LDTR寄存器的base 為基地址得出目標(biāo)段描述符 */

target_descriptor = LDTR.base + target_si * 8;?
}
?
DPL ?= target_descriptor. DPL ;???? /* 獲取 DPL ?*/
?
if (target_descriptor.type & 0x06) { /* conforming */?
if ( CPL ?>=? DPL ) {?? /* 允許執(zhí)行高權(quán)限代碼 */?
???? /* go ahead */?
??? } else {?
???? /* 引發(fā) #GP 異常 */?
??? goto DO_GP_EXCEPTION;?
}?

} else {????????????????? /* nonconforming */?
??? if ( CPL ?==? DPL ?&&? RPL ?<=? CPL ) {???
??????? /* go ahead */?
??? } else {?
??????? /* 引發(fā) #GP 異常 */?
?????? goto DO_GP_EXCEPTION;?
??? }?
???
}
?
/****** go ahead … …******/?
CS = target_cs;?????????? /* 加載目標(biāo)段CS 進入 CS 寄存器 */?
EIP = target_eip;??????? /* 加載目標(biāo)指令EIP 進入 EIP 寄存器 */?
???????????????? /* 當(dāng)前執(zhí)行權(quán)限? CPL ?不改變 */

goto target_descriptor.base + target_eip; /* 跳轉(zhuǎn)到目標(biāo)地址執(zhí)行 */
?

DO_GP_EXCEPTION:???? /* 執(zhí)行 #GP異常點 */?
?????????? … …

?

?

?

二、 使用call gate 進行控制權(quán)的轉(zhuǎn)移?
使用call gate進行轉(zhuǎn)移控制,目的是建立一個利用gate進行向高權(quán)限代碼轉(zhuǎn)移的一種保護機制。gate符相當(dāng)一個進入高權(quán)限代碼的一個通道。


對于指令 call cs:eip 來說:?
★ 目標(biāo)代碼的selector是一個門符的選擇子。用來獲取門描述符。?
★ 門描述符含目標(biāo)代碼段的selector及目標(biāo)代碼的偏移量。?
★ 目標(biāo)代碼的ip值被忽略。因為門符已經(jīng)提供了目標(biāo)代碼的偏移量。?

1、 權(quán)限的檢查?
★ 首先,必須要有足夠的權(quán)限來訪問gate符,所以: CPL ?<= DPLg(門符的 DPL )且: RPL ?<= DPLg?
★ 進前代碼向高權(quán)限代碼轉(zhuǎn)移,所以,對于conforming類型的代碼段來說,必須 CPL ?>= DPLs(目標(biāo)代碼段的 DPL )?
★ 對于nonconforming類型代碼段來說,必須 CPL ?= DPLs?
★ call 指令改變當(dāng)前權(quán)限,而jmp指令不改變當(dāng)前權(quán)限。

總結(jié):

?

?

if (( CPL ?<= DPLg) && ( RPL ?<= DPLg)) { /* 足夠的權(quán)限訪問門符 */?
if (target.C == CONFORMING) {?? /* 目標(biāo)代碼屬于 conforming類型 */?
??? /* 向高權(quán)限級別代碼轉(zhuǎn)移控制 */?
??? if ( CPL ?>= DPLs) {?
?????????? /* 通過訪問 */?
????? } else {?
??????????? /* 失敗,#Gp異常發(fā)生 */?
??? }?

??? } else {?????????????? /* 目標(biāo)代碼屬于 nonconforming 類型 */?
?????? /* 平級轉(zhuǎn)移 */?
??? if ( CPL ?== DPLs) {?
??????????? /* 通過訪問 */?
??????? } else {?
???????????? /* 失敗,#GP 異常發(fā)生 */?
??????? }?
}

} else {?? /* 沒有足夠權(quán)限訪問門符 */?
/* #GP 異常發(fā)生 */?
}

?

?

2、 控制權(quán)的轉(zhuǎn)移?
指令:call 1a:804304c (其中1a是call gate selector)

?

?

gate_selector = 0x1a;?????????????? /* call gate selector */?
RPL ?= gate_selector. RPL ;??????????? /* 門符選擇子 RPL ?*/?
CPL ?= CS. RPL ;???????????????????? /* 當(dāng)前代碼段低3位是 CPL */
CALLGATE_DESCRIPTOR call_gate_descriptor;????? /* 門符的描述符 */?
CODESEG_DESCRIPTOR target_cs_descritpor;????? /* 目標(biāo)代碼段的描述符 */
call_gate_si = gate_selector.SI;????? /* 門符 selector 的索引 */?
call_gate_ti = gate_selector.TI;????? /* 門符selector的描述符表索引 */?
??????????????????????????????????????????????????????????????????????????
/* 獲取call gate descriptor */?
if (call_gate_ti == 0) {????? /* TI為0 就是參考到 GDT(全局描述符表) */?
/* 以GDTR寄存器的base 為基地址加上selector的索引乘以8即得出門符的描述符,門符的 DPL 就是門符的訪問權(quán)限 */?
??? call_gate_descriptor = GDTR.base + call_gate_si * 8;?
} else {?????????????? /* 否則就是參考 LDT (局部描述符表)*/?
???? /* 以 LDTR寄存器的base 為基地址得出目標(biāo)段描述符 */?
call_gate_descriptor = LDTR.base + call_gate_si * 8;?
}

/* 獲取 target code segment descriptor */
target_cs = call_gate_descriptor.selector;??? /* 獲取門符的目標(biāo)代碼段選擇子 */?
target_cs_si = target_cs.SI;???????? /* 目標(biāo)代碼段的索引 */?
target_cs_ti = target_cs.TI;????????????????? /* 目標(biāo)代碼描述符表索引 */
if (target_cs_ti == 0)?
??? target_cs_descriptor = GDTR.base + target_cs_si * 8;?
else?
??? target_cs_descriptor = LDTR.base + target_cs_si * 8;

DPLg = call_gate_descriptor. DPL ;???? /* 獲取門符的 DPL ?*/?
DPLs = target_cs_descriptor. DPL ;???? /* 獲取目標(biāo)代碼段的 DPL ?*/

if ( CPL ?<= DPLg &&? RPL ?<= DPLg) {?? /* 有足夠權(quán)限訪問門符 */?
???? if (target_cs_descriptor.type & 0x06) { /* conforming */?
????????? if ( CPL ?>= DPLs) {?
???????????????? /* 允許訪問目標(biāo)代碼段 */?
????????? } else {?
???????????????? /* #GP 異常產(chǎn)生 */?
????????? }?
???? } else if ( CPL ?== DPLs) { /* nonconforming */?
???????? /* 允許訪問目標(biāo)代碼段 */?
???? } else {?
????????? /* 拒絕訪問,#GP 異常發(fā)生 */?
????????? goto DO_GP_EXCEPTION;?
???? }?
} else { /* 無權(quán)限訪問門符 */?
???? /* 拒絕訪問, #GP異常發(fā)生 */?
???? goto DO_GP_EXCEPTION;?
}
?
/* 允許訪問 */?
current_CS = target_cs;?????? /* 加載目標(biāo)代碼段進入CS 寄存器 */?
current_CS. RPL ?= DPLs;??????? /* 改變當(dāng)前執(zhí)行段權(quán)限 */?
current_EIP = call_gate_descriptor.offset;?? /* 加載EIP */
?
/* 跳轉(zhuǎn)到目標(biāo)代碼執(zhí)行 */?
/* goto current_CS:current_EIP */?
goto target_cs_descriptor.base + call_gate_descriptor.offset;
?
return;
?
DO_GP_EXCEPTION:???????? /* 執(zhí)行異常 */

?

?

?

三、 使用中斷門或陷井門進行轉(zhuǎn)移?
?? 中斷門符及陷井門必須存放在IDT中,IDT表也可以存放call gate。

1、 中斷調(diào)用時的權(quán)限檢查?
?? 用中斷門符進行轉(zhuǎn)移時,所作的權(quán)限檢查同call gate相同,區(qū)別在于intterrupt gate 轉(zhuǎn)移不需要檢查 RPL ,因為,沒有 RPL 需要檢查。?
★ 必須有足夠的權(quán)限訪問門符, CPL ?<= DPLg?
★ 向同級權(quán)限代碼轉(zhuǎn)移時, CPL ?== DPLs,向高權(quán)限代碼轉(zhuǎn)移時, CPL ?> DPLs

總結(jié)

?

?

if ( CPL ?<= DPLg) { /* 有足夠權(quán)限訪問門符 */?
??? if ( CPL ?>= DPLs) {?
??????? /* 允許訪問目標(biāo)代碼頭 */?
??? } else {?
???????? /* 失敗,#GP異常發(fā)生 */?
??? }

} else {?
/* 失敗,#GP異常發(fā)生 */?
}

?

2、 控制權(quán)的轉(zhuǎn)移?
?? 發(fā)生異常或中斷調(diào)用時?
★ 用中斷向量在中斷描述符表查找描述符:中斷向量×8,然后加上IDT表基址得出描述符表。?
★ 從查找到的描述符中得到目標(biāo)代碼段選擇子,并在相應(yīng)的GDT或LDT中獲取目標(biāo)代碼段描述符。?
★ 目標(biāo)代碼段描述符的基址加上門符中的offset,確定最終執(zhí)行入口點。


中斷或陷井門符轉(zhuǎn)移的總結(jié):?
例: int 0x80 指令發(fā)生的情況

?

?

vector = 0x80;?
INTGATE_DESCRIPTOR gate_descriptor = IDTR.base + vector * 8;?
CODESEG_DESCRIPTOR target_descriptor;?
TSS tss = TR.base;?????????????? /* 得到TSS 內(nèi)存塊 */?
DPLg = gate_descriptor. DPL ;?
target_cs = gate_descriptor.selector;
?
if ( CPL ?<= DPLg) {??????????? /* 允許訪問門符 */

if (target_cs.TI == 0) {?? /* index on GDT */?
??? target_descriptor = GDTR.base + target_cs.SI * 8;?
} else {????????????? /* index on LDT */?
target_descriptor = LDTR.base + target_cs.SI * 8;?
??? }

DPLs = target_descriptor. DPL ;?


if ( CPL ?> DPLs) {???? /* 向高權(quán)限代碼轉(zhuǎn)移 */?

??? /* 根據(jù)目標(biāo)代碼段的 DPL 值來選取相應(yīng)權(quán)限的stack結(jié)構(gòu) */?
??? switch (DPLs) {?
??? case 0 :???? /* 假如目標(biāo)代碼處理0級,則選0級的stack結(jié)構(gòu) */?
???????????? SS = tss.ss0;?
???????????? ESP = tss.esp0;?
???????????? break;?
??????? case 1:?
???????????? SS = tss.ss1;?
??????????? ESP = tss.esp1;?
???????????? break;?
??????? case 2:?
???????????? SS = tss.ss2;?
???????????? ESP = tss.esp2;?
???????????? break;?
??? }

?????? /* 以下必須保護舊的stack結(jié)構(gòu),以便返回 */?
??? *--esp = SS;????????? /* 將當(dāng)前SS入棧保護 */?
??? *--esp = ESP;???????? /* 將當(dāng)前ESP入棧保護 */?

} else if ( CPL ?== DPLs) {?
???? /* 同級轉(zhuǎn)移,繼續(xù)向下執(zhí)行 */?
} else {?
??? /* 失敗,#GP異常產(chǎn)生,轉(zhuǎn)去處理異常 */?
}?


*--esp = EFLAGS;????????? /* eflags 寄存器入棧 */?

???? /* 分別將 NT、NT、RF及VM標(biāo)志位清0 */?
EFLAGS.TF = 0;????????????
EFLAGS.NT = 0;?
EFLAGS.RF = 0;?
EFLAGS.VM = 0;?

if (gate_descriptor.type == I_GATE32) { /* 假如是中斷門符 */?
EFLAGS.IF = 0;????????? /* 也將IF標(biāo)志位清0,屏蔽響應(yīng)中斷 */?
???? }?
?????????????
???? *--esp = CS;????????????? /* 當(dāng)前段選擇子入棧 */?
???? *--esp = EIP;???????????? /* 當(dāng)前EIP 入棧 */
CS = target_selector;????? /* 加載目標(biāo)代碼段 */?
CS. RPL ?= DPLs;??????????? /* 改變當(dāng)前執(zhí)行權(quán)限級別 */?
EIP = gate_descriptor.offset; /* 加載進入EIP */?

/* 執(zhí)行中斷例程 */?
goto target_descritptor.base + gate_descriptor.offset;?

} else {?
/* 失敗,#GP 異常產(chǎn)生,轉(zhuǎn)去處理異常 */?
}

?

?

?

■ 堆棧的切換


控制權(quán)發(fā)生轉(zhuǎn)移后,處理器自動進行相應(yīng)的堆棧切換。?
1、 當(dāng)轉(zhuǎn)向到同權(quán)限級別的代碼時,不會進行堆棧級別的調(diào)整,也就是不進行堆棧切換。?
2、 當(dāng)轉(zhuǎn)向高權(quán)限級別時,將發(fā)生相應(yīng)級別的堆棧切換。從TSS塊獲取相應(yīng)級別的stack結(jié)構(gòu)。

例:假如當(dāng)前運行級別 CPL 為2時,發(fā)生了向0級代碼轉(zhuǎn)移時:

?

?

TSS tss = TR.base;???????? /* 從TR寄存器中獲取TSS 塊 */?
CPL ?= 2;???????????????? /* 當(dāng)前運行級別為2 級*/?
DPL ?= 0;???????????????? /* 目標(biāo)代碼需要級別為 0 級 */

/* 根據(jù)目標(biāo)代碼需要的級別進行選取相應(yīng)的權(quán)限級別的stack結(jié)構(gòu) */?
switch ( DPL ) {?
case 0:?
???? SS = tss.ss0;?
???? ESP = tss.esp0;?
???? break;?
case 1:?
???? SS = tss.ss1;?
???? ESP = tss.esp1;?
???? break;?
case 2:?
???? SS = tss.ss2;?
???? ESP = tss.esp2;?
???? break;?
}
?
*--esp = SS;????????? /* 保存舊的stack結(jié)構(gòu) */?
*--esp = ESP;
?
/* 然后再作相當(dāng)?shù)谋4婀ぷ鳎绫4鎱?shù)等 */
*--esp = CS;???????? /* 最后保存返回地址 */?
*--esp = EIP;

?

?

?

?

■ 控制權(quán)的返回


當(dāng)目標(biāo)代碼執(zhí)行完畢,需要返回控制權(quán)給原代碼時,將產(chǎn)生返回控制權(quán)行為。返回控制權(quán)行為,比轉(zhuǎn)移控制權(quán)行為簡單得多。因為,一切條件已經(jīng)在交出控制權(quán)之前準(zhǔn)備完畢,返回時僅需出棧就行了。

1、 near call 的返回?
近調(diào)用情況下,段不改變,即CS不改變,權(quán)限級別不改變。僅需從棧中pop返回地址就可以了。


2、 直接控制權(quán)轉(zhuǎn)移的返回(far call或far jmp)?
?? 直接控制權(quán)的轉(zhuǎn)移是一種不改變當(dāng)前運行級別的行為。只是發(fā)生跨段的轉(zhuǎn)移。這時,CS被從棧中pop出來的CS值加載進去,處理器會檢查 CPL 與這個pop出來的選擇子中的 RPL 進行檢查,相符則返回。不相符則發(fā)生 #GP異常。?
???
總結(jié):假如當(dāng)前運行的目標(biāo)代碼執(zhí)行完畢后,將要返回。這時 CPL 為2

?

?

CPL ?= 2;????? /* 當(dāng)前代碼運行級別為 2 */?
… …?
EIP = *esp++;?? /* pop出原EIP 值 */?
CS = *esp++;??? /* pop出原CS值 */
?
if ( CPL ?== CS. RPL ) {???
/* CS. RPL ?代表是原來的運行級別,與 CPL 相符則返回 */?
return ;?
} else {?
/* #GP異常產(chǎn)生,執(zhí)行異常處理 */?
}

?

?

3、 利用各種門符進行向高權(quán)限代碼轉(zhuǎn)移后的返回?
從高權(quán)限代碼返回低權(quán)限代碼,須從stack中pop出原來的stack結(jié)構(gòu)。這個stack結(jié)構(gòu)屬于低權(quán)限代碼的stack結(jié)構(gòu)。然后直接pop 出原返回地址就可以了。?

總結(jié):

CPL ?= 0;??????? /* 當(dāng)前運行級別為 0 級 */?
… …?
EIP = *esp++;??? /* 恢復(fù)原地址 */?
CS = *esp++;???? /* 恢復(fù)原地址及運行級別 */

ESP = *esp ++;???? /* 恢復(fù)原stack結(jié)構(gòu) */?
SS = *esp++;???? /* 恢復(fù)原stack 結(jié)構(gòu),同時恢復(fù)了原stack訪問級別 */
?
return ;?????? /* 返回 */
?
************************************************************************************
3、DPL,RPL,CPL 之間的聯(lián)系和區(qū)別是什么?RPL和CPL是必須相同嗎?如果相同,為什么要釆用兩個而不改用一個呢??



答:特權(quán)級是保護模式下一個重要的概念,CPL,RPL和DPL是其中的核心概念,查閱資料無數(shù),總結(jié)如下:?
? 簡單解釋:?

--------------------------------------------------------------------------------?

? CPL是當(dāng)前進程的權(quán)限級別(Current Privilege Level),是當(dāng)前正在執(zhí)行的代碼所在的段的特權(quán)級,存在于cs寄存器的低兩位。?
? RPL說明的是進程對段訪問的請求權(quán)限(Request Privilege Level),是對于段選擇子而言的,每個段選擇子有自己的RPL,它說明的是進程對段訪問的請求權(quán)限,有點像函數(shù)參數(shù)。而且RPL對每個段來說不是固定的,兩次訪問同一段時的RPL可以不同。RPL可能會削弱CPL的作用,例如當(dāng)前CPL=0的進程要訪問一個數(shù)據(jù)段,它把段選擇符中的RPL設(shè)為3,這樣雖然它對該段仍然只有特權(quán)為3的訪問權(quán)限。?
DPL存儲在段描述符中,規(guī)定訪問該段的權(quán)限級別(Descriptor Privilege Level),每個段的DPL固定。?
當(dāng)進程訪問一個段時,需要進程特權(quán)級檢查,一般要求DPL >= max {CPL, RPL}?
下面打一個比方,中國官員分為6級國家主席1、總理2、省長3、市長4、縣長5、鄉(xiāng)長6,假設(shè)我是當(dāng)前進程,級別總理(CPL=2),我去聊城市(DPL=4)考察(呵呵),我用省長的級別(RPL=3 這樣也能嚇?biāo)浪麄?-))去訪問,可以吧,如果我用縣長的級別,人家就不理咱了(你看看電視上的微服私訪,呵呵),明白了吧!為什么采用RPL,是考慮到安全的問題,就好像你明明對一個文件用有寫權(quán)限,為什么用只讀打開它呢,還不是為了安全!?


? 全面解釋:?
--------------------------------------------------------------------------------?

? RPL是段選擇子里面的bit 0和bit 1位組合所得的值,但這里要首先搞清楚什么是段選擇子,根據(jù)Intel 的文件(IA-32 IntelR Architecture Software Developer's Manual, Volume 3System Programming Guide)它是一個16Bit identifier (原文:A segment selector is a 16-bit identifier for a segment). 但 identifier 又是什么. identifier 可以是一個變數(shù)的名字( An identifier is a name for variables), 簡單的說它可以就是一般意義的變數(shù). 這里 16-bit identifier for a segment 可以就是一個一般意義的16bit變數(shù)但同時要求對它的值解釋的時候必須跟據(jù)Intel定下的規(guī)則---也就是bit 0和bit 1位的組合值就是RPL等等… 因此在程序里如果有需要的話你可以聲明一個或者多個變數(shù)來代表這些段選擇子,這樣的話你的程序在某一時刻就可以有很多段選擇子,當(dāng)然有那么多段選擇子就有那么多RPL.可以這樣說程序有多少個是RPL是你怎樣看待你自己聲明的變數(shù). |?
? 程序的CPL(CS.RPL)是CS register 里bit 0和bit 1 位組合所得的值.在某一時刻就只有這個值唯一的代表程序的CPL.??

? 而DPL是段描述符中的特權(quán)級, 它的本意是用來代表它所描述的段的特權(quán)級. 一個程序可以使用很多段(Data,Code,Stack)也可以只用一個code段等.在正常的情況下當(dāng)程序的環(huán)境建立好后,段描述符都不需要改變-----當(dāng)然DPL也不需要改變.??


? 一、對數(shù)據(jù)段和堆棧段訪問時的特權(quán)級控制:?

要求訪問數(shù)據(jù)段或堆棧段的程序的CPL≤待訪問的數(shù)據(jù)段或堆棧段的DPL,同時選擇子的RPL≤待訪問的數(shù)據(jù)段或堆棧段的DPL,即程序訪問數(shù)據(jù)段或堆棧段要遵循一個準(zhǔn)則:只有相同或更高特權(quán)級的代碼才能訪問相應(yīng)的數(shù)據(jù)段。這里,RPL可能會削弱CPL的作用,訪問數(shù)據(jù)段或堆棧段時,默認(rèn)用CPU和RPL中的最小特權(quán)級去訪問數(shù)據(jù)段,所以max {CPL, RPL} ≤ DPL,否則訪問失敗。?


? 二、對代碼段訪問的特權(quán)級控制(代碼執(zhí)行權(quán)的特權(quán)轉(zhuǎn)移):?

? 讓我們先來記一些“定律”:?
所有的程序轉(zhuǎn)跳,CPU都不會把段選擇子的RPL賦給轉(zhuǎn)跳后程序的CS.RPL. .?

? 轉(zhuǎn)跳后程序的CPL(CS.RPL)只會有下面的倆種可能?
? 轉(zhuǎn)跳后程序的CPL(CS.RPL) = 轉(zhuǎn)跳前程序的CPL(CS.RPL)??
? 或?
? 轉(zhuǎn)跳后程序的CPL(CS.RPL) = 轉(zhuǎn)跳后程序的CodeDescriptor.DPL?

? 以 Call 為例(只能跳到等于當(dāng)前特權(quán)級或比當(dāng)前特權(quán)級更高的段):?
? 怎樣決定這兩種選擇,這就要首先知道轉(zhuǎn)跳后程序的段是一致代碼段還是非一致代碼段.其實也很簡單,規(guī)則如下:?
? 如果能成功轉(zhuǎn)跳到一致代碼段, 轉(zhuǎn)跳后程序的CPL(CS.RPL) = 轉(zhuǎn)跳前程序的CPL(CS.RPL),(轉(zhuǎn)跳后程序的CPL繼承了轉(zhuǎn)跳前程序的CPL)?
? 如果能成功轉(zhuǎn)跳到非一致代碼段, 轉(zhuǎn)跳后程序的CPL(CS.RPL) =轉(zhuǎn)跳后程序的Descriptor.DPL。(轉(zhuǎn)跳后程序的CPL變成了該代碼段的特權(quán)級.我在前面提到DPL是段描述符中的特權(quán)級, 它的本意是用來代表它所描述的段的特權(quán)級)怎樣才能成功轉(zhuǎn)跳啦??

? 這里有四個重要的概念:?

? 1).段的保護觀念是高特權(quán)級不找低特權(quán)級辦事,低特權(quán)級找高特權(quán)級幫忙,相同的一定沒問題.(這樣想邏輯是沒錯,事實對不對就不知道.)也就是縣長不找鄉(xiāng)長,鄉(xiāng)長不求農(nóng)民,反過來農(nóng)民求鄉(xiāng)長,鄉(xiāng)長找縣長.這個概念是最重要的。?
? 2) 一致代碼段的意義: 讓客人很方便的利用主人(一致代碼段)的東西為自己辦事.但客人這身份沒有改變NewCS.RPL=OldCS.RPL所以只能幫自己辦事。比方說鄉(xiāng)長有一頭牛,農(nóng)民可以借來幫自己種田,但不能種別人的田.但是如果你是鄉(xiāng)長當(dāng)然可以種鄉(xiāng)里所有的田。?
? 3) 非一致代碼段的意義:主人(非一致代碼段)可以幫客人但一定是用自己的身份NewCS.RPL= DestinationDescriptorCode.DPL這里可能有安全的問題, 搞不好很容易農(nóng)民變縣長。主人太頑固了一定要堅持自己的身份,有什么方法變通一下,來個妥協(xié)好不好。好的,它就是RPL的用處。?
? 4) RPL: 它讓程序有需要的時候可以表示一個特權(quán)級更低的身份Max(RPL,CPL)而不會失去本身的特權(quán)級CPL(CS.RPL),有需要的時候是指要檢查身份的時候。事實上RPL跟段本身的特權(quán)級DPL和當(dāng)前特權(quán)級CPL沒有什么關(guān)系,因為RPL的值在成功轉(zhuǎn)跳后并不賦給轉(zhuǎn)跳后的CS.RPL。?
? 還是要問怎樣才能成功轉(zhuǎn)跳啦?這里分兩種情況:?

? 普通轉(zhuǎn)跳(沒有經(jīng)過Gate 這東西):即JMP或Call后跟著48位全指針(16位段選擇子+32位地址偏移),且其中的段選擇子指向代碼段描述符,這樣的跳轉(zhuǎn)稱為直接(普通)跳轉(zhuǎn)。普通跳轉(zhuǎn)不能使特權(quán)級發(fā)生躍遷,即不會引起CPL的變化,看下面的詳細(xì)描述:?


目標(biāo)是一致代碼段:?
? 要求:CPL(CS.RPL)>=DestinationDescriptorCode.DPL ,其他RPL是不檢查的。?
? 轉(zhuǎn)跳后程序的CPL(NewCS.RPL) = 轉(zhuǎn)跳前程序的CPL( OldCS.RPL)?
? 上面的安排就是概念1,2的意思,此時,CPL沒有發(fā)生變化,縱使它執(zhí)行了特權(quán)級(DPL)較高的代碼。若訪問時不滿足要求,則發(fā)生異常。?
目標(biāo)是非一致代碼段:?
? 要求:CPL(CS.RPL)=DestinationDescriptorCode.DPL AND RPL≤CPL(CS.RPL)?
? 轉(zhuǎn)跳后程序的CPL(NewCS.RPL) = DestinationDescriptorCode.DPL?
? 上面的安排就是概念3的意思和部分1的意思----主人(一致代碼段)只幫相同特權(quán)級的幫客人做事。因為前提是CPL=DPL,所以轉(zhuǎn)跳后程序的CPL(NewCS.RPL) = DestinationDescriptorCode.DPL不會改變CPL的值,特權(quán)級(CPL)也沒有發(fā)生變化。如果訪問時不滿足前提CPL=DPL,則引發(fā)異常。


? 通過調(diào)用門的跳轉(zhuǎn):當(dāng)段間轉(zhuǎn)移指令JMP和段間轉(zhuǎn)移指令CALL后跟著的目標(biāo)段選擇子指向一個調(diào)用門描述符時,該跳轉(zhuǎn)就是利用調(diào)用門的跳轉(zhuǎn)。這時如果選擇子后跟著32位的地址偏移,也不會被cpu使用,因為調(diào)用門描述符已經(jīng)記錄了目標(biāo)代碼的偏移。使用調(diào)門進行的跳轉(zhuǎn)比普通跳轉(zhuǎn)多一個步驟,即在訪問調(diào)用門描述符時要將描述符當(dāng)作一個數(shù)據(jù)段來檢查訪問權(quán)限,要求指示調(diào)用門的選擇子的RPL≤門描述符DPL,同時當(dāng)前代碼段CPL≤門描述符DPL,就如同訪問數(shù)據(jù)段一樣,要求訪問數(shù)據(jù)段的程序的CPL≤待訪問的數(shù)據(jù)段的DPL,同時選擇子的RPL≤待訪問的數(shù)據(jù)段或堆棧段的DPL。只有滿足了以上條件,CPU才會進一步從調(diào)用門描述符中讀取目標(biāo)代碼段的選擇子和地址偏移,進行下一步的操作。?
? 從調(diào)用門中讀取到目標(biāo)代碼的段選擇子和地址偏移后,我們當(dāng)前掌握的信息又回到了先前,和普通跳轉(zhuǎn)站在了同一條起跑線上(普通跳轉(zhuǎn)一開始就得到了目標(biāo)代碼的段選擇子和地址偏移),有所不同的是,此時,CPU會將讀到的目標(biāo)代碼段選擇子中的RPL清0,即忽略了調(diào)用門中代碼段選擇子的RPL的作用。完成這一步后,CPU開始對當(dāng)前程序的CPL,目標(biāo)代碼段選擇子的RPL(事實上它被清0后總能滿足要求)以及由目標(biāo)代碼選擇子指示的目標(biāo)代碼段描述符中的DPL進行特權(quán)級檢查,并根據(jù)情況進行跳轉(zhuǎn),具體情況如下:?


目標(biāo)是一致代碼段:?
? 要求:CPL(CS.RPL)≥DestinationDescriptorCode.DPL ,RPL不檢查,因為RPL被清0,所以事實上永遠(yuǎn)滿足RPL≤DPL,這一點與普通跳轉(zhuǎn)一致,適用于JMP和CALL。?
? 轉(zhuǎn)跳后程序的CPL(NewCS.RPL) = 轉(zhuǎn)跳前程序的CPL( OldCS.RPL),因此特權(quán)級沒有發(fā)生躍遷。?
???
目標(biāo)是非一致代碼段:?
當(dāng)用JMP指令跳轉(zhuǎn)時:?
? 要求:CPL(CS.RPL)=DestinationDescriptorCode.DPL AND RPL<= CPL(CS.RPL)(事實上因為RPL被清0,所以RPL≤CPL總能滿足,因此RPL與CPL的關(guān)系在此不檢查)。若不滿足要求則程序引起異常。?
? 轉(zhuǎn)跳后程序的CPL(NewCS.RPL) = DestinationDescriptorCode.DPL?
? 因為前提是CPL=DPL,所以轉(zhuǎn)跳后程序的CPL(NewCS.RPL) = DestinationDescriptorCode.DPL不會改變CPL的值,特權(quán)級也沒有發(fā)生變化。如果訪問時不滿足前提CPL=DPL,則引發(fā)異常。?
當(dāng)用CALL指令跳轉(zhuǎn)時:?
要求:CPL(CS.RPL)≥DestinationDescriptorCode.DPL(RPL被清0,不檢查),若不滿足要求則程序引起異常。?
轉(zhuǎn)跳后程序的CPL(NewCS.RPL) = DestinationDescriptorCode.DPL?
當(dāng)條件CPL=DPL時,程序跳轉(zhuǎn)后CPL=DPL,特權(quán)級不發(fā)生躍遷;當(dāng)CPL>DPL時,程序跳轉(zhuǎn)后CPL=DPL,特權(quán)級發(fā)生躍遷,這是我們當(dāng)目前位置唯一見到的使程序當(dāng)前執(zhí)行憂先級(CPL)發(fā)生變化的跳轉(zhuǎn)方法,即用CALL指令+調(diào)用門方式跳轉(zhuǎn),且目標(biāo)代碼段是非一致代碼段。?


? 總結(jié):以上介紹了兩種情況的跳轉(zhuǎn),分別是普通跳轉(zhuǎn)和使用調(diào)用門的跳轉(zhuǎn),其中又可細(xì)分為JMP跳轉(zhuǎn)和CALL跳轉(zhuǎn),跳轉(zhuǎn)成功已否是由CPL,RPL和DPL綜合決定的。所有跳轉(zhuǎn)都是從低特權(quán)級代碼向同級或更高特權(quán)級(DPL)跳轉(zhuǎn),但保持當(dāng)前執(zhí)行特權(quán)級(CPL)不變,這里有點難于區(qū)別為什么說向高特權(quán)級跳轉(zhuǎn),又說特權(quán)級沒變,這里“高特權(quán)級”是指目標(biāo)代碼段描述符的DPL,它規(guī)定了可以跳轉(zhuǎn)到該段代碼的最高特權(quán)級;而后面的CPL不變才真正說明了特權(quán)級未發(fā)生躍遷。我們可以看到,只有用CALL指令+調(diào)用門方式跳轉(zhuǎn),且目標(biāo)代碼段是非一致代碼段時,才會引起CPL的變化,即引起代碼執(zhí)行特權(quán)級的躍遷,這是目前得知的改變執(zhí)行特權(quán)級的唯一辦法,如果各位讀者還知道其他方法請留言告訴我。

《Orange’s 一個操作系統(tǒng)的實現(xiàn)》3.保護模式5----特權(quán)級概述


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 91看片淫黄大片欧美看国产片 | 五月伊人网| 一个色综合亚洲伊人久久 | 色婷婷香蕉 | 欧洲成人精品 | 久久久久久成人精品 | 黄色av网站免费看 | 精品美女在线观看视频在线观看 | 国产一区二区三区免费 | 四虎国产在线 | 欧美亚洲激情在线 | 日本精品一区二区三区在线 | 成人午夜免费福利 | 福利片在线观看 | 国产精品久久久久免费视频 | 欧美很黄视频在线观看 | 久热久热 | 国产内谢| 日本高清色惰www在线视频 | av av在线| 精品国产欧美一区二区 | 亚洲一区二区久久 | 久久精品人人做人人看最新章 | 色综合欧美| 亚洲国产精品综合久久 | 男女下面一进一出无遮挡着 | 嘿嘿视频不良网站 | 日本一本视频 | 午夜在线免费观看 | 精品久久久久久久久久久久久久久 | 青草青在线| 亚洲欧美另类日韩 | 最新中文字幕在线 | 一级美女 | 青娱乐久草 | 舒淇三级浴室洗澡在线观看 | 91拍拍在线观看 | 一级做a爰片性色毛片中国 日本黄色免费片 | 欧美日韩国产精品 | 奇米影视7777久久精品人人爽 | 男女同床爽爽视频免费 |