亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
樓主: mik
打印 上一主題 下一主題

【x86 & x64 沉思錄】(6.7 更新) [復制鏈接]

論壇徽章:
0
31 [報告]
發(fā)表于 2009-04-12 02:29 |只看該作者
題外話: 選擇 conforming 還是 non-conforming

  code segment descriptor 的 C 位表示 code segment 是 conforming 還是 non-conforming 類型,C = 1 代表 conforming 類型,C = 0 代表 non-conforming。conforming 類型可以允許低權(quán)限向高權(quán)限轉(zhuǎn)移,而不需要通過 call-gate。低權(quán)限向高權(quán)限的 non-conforming 轉(zhuǎn)移則必須通過 call-gate。

顯然,使用 non-conforming 比使用 conforming 更為安全,因為:

原因 1、當 far call/jmp 到 code segment 直接跳轉(zhuǎn)時(使用 code segment selector):

(1)選擇 conforming 類型僅需要 CPL >= DPL 權(quán)限就可以了,也就是說:可以直接從低權(quán)限向高權(quán)限的 code segment 轉(zhuǎn)移,但是 CPL 是不改變的。
  這樣存在著隱患,由于 CPL(當前的 processor 權(quán)限)不改變,但是代碼卻從低權(quán)限轉(zhuǎn)到了高權(quán)限,導致:若高權(quán)限的代碼之中使用了高權(quán)限的指令,而 CPL 還停留在低權(quán)限上,執(zhí)行這樣的指令會導致異常發(fā)生,這是其一。
  其二:高權(quán)限的代碼使用的是高權(quán)限的 stack,但由于 CPL 不變,導致不能發(fā)生正確的 stack 切換。

(2)而選擇 non-conforming 類型需要更嚴格的權(quán)限,需:CPL == DPL 且 RPL <= DPL。
  很明顯,使用 non-conforming 類型不能從低權(quán)限轉(zhuǎn)向高權(quán)限,必須 CPL == DPL,只能要同級權(quán)限之中轉(zhuǎn)移。這樣就避免了不必要的隱患。



原因 2:當通過 call-gate 來 far call/jmp 到 code segment 時:

(1)選擇 conforming 類型的需要權(quán)限:(CPL <= DPLg) && (RPL <= DPLg) 并且 (CPL >= DPLs)
(2)選擇 non-conforming 雖然需要同樣的權(quán)限:(CPL <= DPLg) && (RPL <= DPLg) 并且 (CPL >= DPLs)。
  但是,在 non-conforming 下僅允許使用 call 指令轉(zhuǎn)移到高權(quán)限,不允許使用 jmp 指令轉(zhuǎn)移到高權(quán)限代碼。
使用 jmp call-gate 導致產(chǎn)生兩個問題:
其一:jmp 指令不能正確返回到原調(diào)用的代碼。由于沒有將原 CS:RIP 入棧保存,系統(tǒng)服務例程 ret 時,將會出錯。
其二:由于不能正確返回,可能導致不能從高權(quán)限代碼返回低權(quán)限代碼,processor 將一直運行在高權(quán)限代碼中。

  使用 jmp call-gate 指令到 non-conforming 僅允許同級轉(zhuǎn)移,即:CPL <= DPLg && RPL<= DPLg 并且 CPL == DPLs
  jmp 指令只能同級轉(zhuǎn)移(不能調(diào)用系統(tǒng)服務例程),使用 non-conforming 類型將不允許 jmp 指令轉(zhuǎn)移到高權(quán)限代碼,避免隱患! 

-------------------------------------------------------------------------------

  綜上所述兩個原因,使用 non-conforming 類型更符合 x86 / x64 的保護機制。




:wink:

[ 本帖最后由 mik 于 2009-4-12 23:37 編輯 ]

論壇徽章:
0
32 [報告]
發(fā)表于 2009-04-12 17:24 |只看該作者
7.1.3.4、 使用 TSS selector 進行任務切換

指令:
  call 0x20:00000000
  jmp 0x20:00000000
-----------------------------------
  selector 0x20 是個 TSS descriptor 的 selector。


  TSS descriptor 與 segment descriptor 的格式一致,不同的是 type,在 x86 和 long mode 的 compatibility 模式下有: available/busy 16-TSS 和 available/busy 32-TSS。
  當成功將 TSS descriptor 加載到 TR 寄存器后,processor 將 available TSS 置為 busy TSS,在返回時將 busy TSS 置為 available TSS。


1、索引查找 TSS descriptor
  TSS selector 在 GDT 里索引 TSS descriptor,其方法和 segment / gate dscriptor 的查找一致。



2、TSS 的 limit & type check

  TSS segment 大小必須能容納所有的 processor state,整個 TSS segment 的大小是 104 個字節(jié),在加載 TSS descriptor 進入 TR 之前,processor 檢查 TSS descriptor 中的 limit 必須大于或等于 67h(104 字節(jié))。若否,則產(chǎn)生 #TS 異常。
  processor 也檢查 TSS descriptor 的 type 是否為 available 類型。若為 busy 類型,則產(chǎn)生 #GP 異常。


if (temp_descriptor.type == BUSY)
{
  goto do_#GP_exception;
}

if (temp_descriptor.limit < 0x67)
{
  goto do_#TS_exception;
}




3、權(quán)限的 check

  所需的權(quán)限是:CPL <= DPL && RPL <= DPL

if (RPL <= DPL && CPL <= DPL) {
   /* 通過,允許訪問 */
} else {
   /* 失敗,#GP 異常 */
}





4、保存 old-task 的 processor  state

  在執(zhí)行加載 TSS descriptor 到 TR 之前,processor 將執(zhí)行一項很重要的工作:保存 old-task 的執(zhí)行環(huán)境(processor 的狀態(tài))在當前的 TSS segment 中。
  當前的 TSS segment 就是 old-task 的 TSS segment,在沒加載 new-task 的 TSS 進入 TR 之前。

執(zhí)行環(huán)境包括:
(1)GPRs
(2)segment registers
(3)Eflags
(4)LDTR 和 CR3
(5)EIP



5、加載 TSS descriptor

  通過一系列的 processor 檢查后,processor 加載 TSS descriptor 進入 TR 寄存器。

TR.selector = TSS_selector;
TR.limit = temp_descriptor.limit;
TR.base = temp_descriptor.base;
TR.attribute = temp_descriptor;

TSS_descriptor.type = BUSY_32;

CR0.TS = 1;


TSS descriptor 加載到 TR 寄存器相應的域。
并且,processor 將 TSS descriptor 的 type 置為 BUSY 狀態(tài),防止再調(diào)用該 TSS descriptor
CR0.TS 也被置為 1,防止在 task 切換時執(zhí)行 x87 or SSE(media)指令。



6、加載 processor state 信息

  TSS descriptor 加載進 TR,processor 將加載新任務保存在 TSS 的 processor 狀態(tài)信息。
包括:
(1)GPRs 加載
(2)segment registers 加載
(3)Eflags 加載
(4)LDTR 和 CR3 加載



7、設置 EFLAGS.NT 和 TSS.link 域

  使用 call 指令調(diào)用 TSS selector,processor 將置 Eflags.NT 為 1,表示當前的 task 被嵌套調(diào)用。而使用 jmp 指令則不會置 Eflags.NT 為 1。

if (opcode == CALL) {           /* 使用 call 指令 */
  Eflags.NT = 1;                 /* 置 NT 位為 1 */
  TSS.link = old_TSS;         /* link 域為 old task 的 TSS selector */
}


  jmp 指令不支持 Task-Nesting 因此不改變 Eflags.NT 域,使用 call 指令還將當前 TSS 的 link 域設為 old-task 的 TSS selector,這樣,當前使用 iret 指令返回時,將通過 old-task 的 TSS selector 返回到 old-task。





7、執(zhí)行新 task

  同樣,processor 加載新的 task EIP 進入 EIP 寄存器,執(zhí)行新的 task 代碼。新的 CS 被加載時,CPL 就是新的 CS.RPL。




8、返回 old-task

  new-task 執(zhí)行完畢通過 ret 或 iret 指令返回到 old-task。

在 caller 中:

... ...
  call 0x20:0x0000000
next:
... ...

使用 call TSS_selector 來切換到 new-task。

(1)使用 ret 指令返回
  在 new-task 中使用 ret 指令返回和一般的 caller / callee 之間返回并無兩樣。程序?qū)⒎祷氐?next 處。

(2)使用 iret 指令返回
  使用 Eflags.NT 的目的是讓 iret 來返回,當使用 iret 指令返回時,processor 將檢查 Eflags.NT 是否為 1 ,若 Eflags.NT = 1 則表示當前 task 被嵌套。
  程序?qū)⒎祷氐?TSS.link 的 TSS selector 指向的 TSS descriptor,這個 TSS descriptor 就是 old-task 的 TSS descriptor,這里又進行另一次的任務切換到 old-task。
----------------------------------------------------------
  使用 ret 與 iret 的不同就是:用 ret 返回時,當前的 processor 還是當前的 state,使用 iret 返回時,將加載保存在 TSS 的 processor state。
  也就是說,ret 返回 old-task 還是沿用 new-task 的 processor 狀態(tài)。iret 返回到 old-task 使用的是原來保存在 old-task 的 TSS 的 processor 狀態(tài)。


使用的 iret 的另一個功能:
  當 Eflags.NT = 1 時:由于 iret 將返回到 TSS.link 的 TSS selector,進行一次任務切換。若這個 TSS selector 是刻意構(gòu)造的別有用途的 TSS 時,可以進行一次別有用途的 task 切換。






7.1.3.4.1、 long mode 下的 TSS 切換情形

  在 long mode 下已經(jīng)不支持使用 TSS 來切換 task ,包括 TSS selector 和 task gate。使用 call / jmp TSS_selector 將產(chǎn)生 #GP 異常。








:wink:

[ 本帖最后由 mik 于 2009-4-17 23:52 編輯 ]

論壇徽章:
0
33 [報告]
發(fā)表于 2009-04-18 00:10 |只看該作者
7.1.3.5、 通過 task gate 進行 task 切換


指令:
  call 0x20:00000000
  jmp 0x20:00000000
-----------------------------------
  selector 0x20 是個 task gate 的 selector


  這里使用 task gate 任務切換與使用 TSS selector 的情形基本一樣。


值得注意的是,使用 task gate 在權(quán)限的 check 方面與 call gate 不同的是:

(1)call-gate 的權(quán)限 check 中:CPL <= DPLg && RPL <= DPLg  并且 CPL >= DPLs(或 CPL == DPLs)
(2)task-gate 的權(quán)限 check 中:CPL <= DPLg && RPL <= DPLg,忽略和 DPLs 的校驗。


task-gate 的權(quán)限 check:
  CPL <= DPLg && RPL <= DPLg
 

論壇徽章:
0
34 [報告]
發(fā)表于 2009-04-18 00:58 |只看該作者
7.1.3.6、 使用 INT n  調(diào)用系統(tǒng)例程


  IDT(Interrupt Descriptor Table)僅能存放 interrupt-gate、trap-gate 和 task-gate。


指令:
  int 0x80
-----------------------------------
  0x80 是 vector (中斷向量號)

  在 x86 下,gate-descriptor 是 8 個字節(jié),所以:gate = IDTR.base + vector * 8,在 long mode 下,gate-descrptor 是 16 字節(jié),所以:gate = IDTR.base + vector * 16。

  在 real-mode 下,IVT(Interrupt Vector Table)是 IDT 在 real-mode 下的表現(xiàn)形式。IVT entry 是 4 個字節(jié),構(gòu)成 CS:offset 這種形式,offset 在低端,CS 在高端。所以: entry = IDTR.base + vector * 4。 IDTR.base 初始為 0。




1、索引 gate 和 descriptor

  在 IDT 中查找 gate descriptor 是以 vector 為索引,不存在 selector,vector 從 0 ~ 255 共 1 個字節(jié)。

gate = IDTR.base + vector * 8;

selctor = gate.selector;

temp_descriptor = get_descriptor(selector, GDT /* or LDT);


  用 IDTR.base + vector * 8 方法得取 gate-descriptor,再用 gate_descriptor 的 selector 來獲取目 code segment descriptor。



2、權(quán)限 check

  在訪問 IDT 中不使用 selector,所以不存在 RPL 檢查,僅需要檢查 CPL 、DPLg 和 DPLs 就行了。DPLg 代表 gate-descriptor 的 DPL,DPLs 代表 code segment descriptor 的 DPL。

  若 gate 是個 task-gate 僅需判斷 CPL <= DPLg 就行了


if (gate.type == TASK_GATE) {                         /* task gate */
  if (CPL <= DPLg) {
     /* 通過,允許訪問 */
  } else {
     /* 失敗,#GP 異常 */
  }

} else if (CPL <= DPLg && CPL >= DPLs) {           /* interrupt / trap gate */

  /* 通過,允許訪問 */
} else {
  /* 失敗,#GP 異常 */
}


  無論 code segment 是 conforming 還是 non-conforming 都是可以的。





3、stack 切換

  interrupt / trap 服務例程必然在運行在 supervisor 權(quán)限下,若切入 interrupt/trap 例程的代碼運行在 user 權(quán)限下,這將導致權(quán)限改變,stack 也發(fā)生切換。


DPL = temp_descriptor.DPL;                          /* code segment descriptor's DPL */

old_SS = SS;
old_ESP = ESP;

SS = TSS.stack[DPL].SS;                                /* 加載目標 stack */
ESP = TSS.stack[DPL].ESP;


push(old_SS);
push(old_ESP);

push(Eflags);

push(CS);
push(EIP);

if (ERROR_CODE) {
  push(error_code);
}


stack 切換時,processor 做以下工作:
(1)以目標 code segment 的 DPL 為索引在 TSS 獲取相應權(quán)限級別的 stack pointer 加載到 SS & ESP
(2)將舊的 SS & ESP 保存在當前 stack (新 stack) 中。
(3)EFLAGS 寄存器入棧。
(4)返回地址(CS & EIP)入棧。
(5)由異常引發(fā)的 interrupt/trap 例程,還將異常碼入棧,以供 interrupt/trap 例程使用。

 SS.RPL 會被更新為 DPL,表示當前 stack 的權(quán)限級別就是 SS.RPL。


當發(fā)生同級權(quán)限的轉(zhuǎn)移時,不會發(fā)生 stack 切換,使用的是當前的 stack:

push(Eflags);
push(CS);
push(EIP);

if (ERROR_CDOE)
  push(error_code)





4、Eflags 寄存器的處理

  processor 將舊的 Eflags 入棧保存,將修改當 Eflags 的標志

if (gate == TASK_GATE)                    /* task gate */
  Eflags.NT = 1;
else                                    /* interrupt/trap gate */
  Eflags.NT = 0;

Eflags.TF = 0;
Eflags.VM = 0;
Eflags.RF = 0;

if (gate == INTERRUPT_GATE) {                 /* interrupt gate */
  Eflags.IF = 0;
} else if (gate == TRAP_GATE) {        /* trap gate */
  /* do nothing */
}                                            /* task gate */


(1)若是 task gate,processor 將置 Eflags.NT = 1 進入 Nest Task 模式,在 Iret 指令返回時將檢查 Eflags.NT = 1 進入 task 切換。若是 interrupt/trap 的話,將 Eflags.NT 清 0,防止 IRET 指令回時進行 task 切換。
(2)Eflags.TF 將被清 0,將 Eflags.TF = 1 將引發(fā) #DB 異常,processor 將進入單步調(diào)試模式,在進入響應 #DB 異常的例程前,processor 會將 Eflags.TF 清 0。
(3)Eflags.RF 清 0,在 IRET 指令返回時,processor 將 Eflags.RF 置為 1,使得 #DB 異常斷點下一條指令能順利執(zhí)行。




5、加載 code segment descriptor

  同樣,processor 會加載 code segment selector & descriptor 到 CS 寄存器

CS.selector = gate.selector;
CS.base = temp_descriptor.base;
CS.attribute = temp_descriptor.attribute;
CS.limit = temp_descriptor.limit;


  加載后,CS.RPL 就是新的 CPL,


  
6、執(zhí)行服務例程

  processor 將加載 EIP = CS.base + gate.offset,然后執(zhí)行 CS:EIP 處的中斷服務例程。




7、中斷例程返回

  中斷例程使用 iret 指令返回時,processor 同樣會進行一系統(tǒng)的權(quán)限檢查,中斷例程不能向高權(quán)限的代碼返回。


if (Eflags.NT == 1) {
  /* 發(fā)生 task 切換 */
}


pop(temp_EIP);
pop(temp_CS);

if (temp_CS.RPL == CPL) {                        /* CPL == RPL */

  goto return_same;       /* 同級返回 */

} else if (temp_CS.RPL > CPL)                  /* CPL < RPL */

  goto return_less;        /* 向低權(quán)限代碼返回

else {                                         /* CPL > RPL */
  /* 異常,#GP 異常 */
  /* 拒絕向高權(quán)限代碼返回 */
}



return_same:
  CS = temp_CS;
  EIP = temp_EIP;

  pop(Eflags);

  return;


return_less:

  pop(temp_Eflags);

  pop(temp_ESP);
  pop(temp_SS);

  if (temp_SS.RPL == temp_CS.RPL) {
    Eflags = temp_Eflags;

    SS = temp_SS;
    ESP = temp_ESP;

      CS = temp_CS;
    EIP = temp_EIP;

  } else {
    /* 失敗,#GP 異常 */
  }

  return;



(1)iret 指令檢查 Eflags.NT 是否為 1,為 1 時,直接進行任務切換出去,這個 task 就是 TSS.link 里的 TSS selector。

(2)pop 出舊的 CS & EIP,并進行權(quán)限檢查,temp_CS.RPL 代表原來的 CPL 運行級別,必須 CPL <= temp_CS.RPL,若 pop 出的 CS.RPL 是低于 CPL 的,則會產(chǎn)生 #GP 異常,processor 防止向高權(quán)限的代碼返回。

  當向同級的代碼返回時,僅需加載回原來的 CS & EIP 以及 EFLAGS 寄存器就行了。

  當向低權(quán)限代碼返回時,processor 會檢查 pop 出來的 SS.RPL 是否等于 temp_CS.RPL(原來的 CS.RPL),不相同則產(chǎn)生 #GP 異常,在 x86 下,無論什么情況 CPL.RPL 必須等于 SS.RPL。通過后,processor 相應地加載 SS & ESP、CS & EIP 以及 EFLAGS 寄存器,然后返回原程序。










:)

[ 本帖最后由 mik 于 2009-4-22 15:13 編輯 ]

論壇徽章:
0
35 [報告]
發(fā)表于 2009-04-22 16:29 |只看該作者
7.1.3.7、 long mode 下的中斷服務例程切換

  在 long mode 下,gate 是 16 字節(jié)的,并取消了對 task gate 的支持。即 IDT 的 entry 是 16 字節(jié)的,所以:gate = IDTR.base + vector * 16。
  在 long mode 下,code segment descriptor 的 L、D、C 以及 DPL 有效,其它域無效。由于 base 強制為 0,中斷服務例程的入口地址就是 gate.offset,這個 offset 是 64 位值。code segment descriptor 的 L = 1 && D = 0,代表 code segment 是 64 位代碼。



1、索引 gate 和 code segment descriptor

gate = IDTR.base + vector * 16;

temp_descriptor = get_descriptor(gate.selector, GDT /* LDT */);


同樣,以 gate.selector 可以找到 code segment descriptor。



2、gate descriptor 和 code segment descriptor 的檢查

  processor 將對 gate 和 code segment descriptor 進行檢查

if (gate.type == INTERRUPT_GATE || gate.type == TRAP_GATE) {

} else {
  /* #GP 異常 */
}  

if (IS_CANONICAL(gate.offset)) {                /* is canonical-address ? */

} else {
  /* #GP */
}

if (temp_descriptor.L == 1 && temp_descriptor.D == 0) {            /* 64 bit */

} else {
  /* #GP 異常 */
}


  processor 對 gate 額外檢查 offset 是否是 canonical-address 地址形式。processor 還將對 code segment descriptor 的 L 和 D 屬性進行檢查,是否為 64 位代碼。



3、權(quán)限 check

  DPLg 是 gate.DPL,DPLs 是 code segment descripotr.DPL
if (CPL <= DPLg && D >= DPLs) {
  /* 通過,允許訪問 */
} else {
  /* 失敗, #GP 異常 */
}





4、stack 切換
  
  在 long mode 下的 interrupt/trap gate 增加了 IST 域,代表 1 ~ 7 stack pointer ID 值。允許在 interrupt/trap 提供自定義的 stack pointer,分別為:IST1 ~ IST7。


old_SS = SS;
old_RSP = RSP;

if (gate.IST)
{
  SS = NULL;
  RSP = TSS.IST[gate.IST].RSP;         /* Intrrupt Stack Table */

} else {

  SS = NULL:
  RSP = TSS.stack[temp_descriptor.DPL].RSP;       /* stack pointer */
}

RSP |= 0xFFFFFFFF_FFFFFFF0;                /* 調(diào)整 stack 為 16 字節(jié)對齊 */

push64(old_SS);
push64(old_RSP);

push64(RFlags);

push64(CS);
push64(RIP);

if (ERROR_CODE)
{
  push64(error_code);
}


(1)如果,interrupt/trap gate 的 IST 不為 0 無論是否有權(quán)限的改變,都將發(fā)生 stack 切換,以便使用 gate 提供的 IST 指針,若 gate.IST 為 0 的話,將沿有以前的的 stack 切換模式:在權(quán)限改變時發(fā)生 stack 切換。

(2)long mode 下的 stack 以 16 字節(jié)對齊,processor 額外將 RSP 調(diào)整為 16 字節(jié)對齊。

(3)SS 將被加載為 NULL selector,即:0x0000(selector),在 long mode 下的 stack 切換中 SS 被加載為 NULL-selector 是不會產(chǎn)生 #GP 異常的。

(4)舊的 SS & RSP、Eflags、CS & RIP 將被入棧,SS、CS 在 8 字節(jié)邊界上,多余補 0 。Rflags 的高 32 位補 0 入棧。

(5)同樣若是 exception 例程則入棧 error code

---------------------------------------------------------------------------------
  在 long mode 下的 TSS segment 是 64 位的,其中有 6 個 RSP 指針域,構(gòu)成一個 Interrupt Stack Table 表,分別為:IST1 ~ IST7,這 6 個 RSP 由 interrupt / trap gate descriptor 中的 IST 域來索引獲取。
  gate 中的 IST 域值為 001 ~ 111,相應地指出在 TSS 中的 IST 值。當 gate.IST 為 0 時,表示在 gate 中不提供對 IST 的索引,還是按原來的獲取 stack pointer 方式。




5、Rflags 寄存器的處理

Rflags.NT = 0;
Rflags.RF = 0;
Rflags.TF = 0;

if (gate == INTERRUPT_GATE) {
  Rflags.IF = 0;
} else if (gate == TRAP_GATE) {

}

同樣,processor 需將 NT、TF、RF 清為 0,在 interuupt gate 下 IF 清 0, trap-gate 下不修改 IF 標志。long mode 下不支持 virtual-8086 mode,對 VM 標志不修改。




6、加載 code segment descriptor

  和 x86 下一樣,code segment selector 和 descriptor 被加載到 CS 中,但是僅 CS.L、CS.D、CS.P、CS.C 和 CS.DPL 有效,其它域無效,CS.base 被強制為 0,CS.limit 被強制為 FFFFFFFF_FFFFFFFF
  CS.RPL 被更新為 code segment descriptor 的 DPL,即為當前的 CPL。  


7、執(zhí)行中斷服務例程
  
  由于 CS.base 被強制為 0,因此 gate.offset 就是實際的中斷服務例程入口地址。

  RIP = gate.offset 然后執(zhí)行 CS:RIP 處理的中斷服務例程。



8、中斷服務例程的返回

  若返回到 64 bit 模式時,processor 處理將和 x86 的情形一樣。但是在 64 bit 的中斷服務例程里需使用 iretq 指令。

情景提示:
  由于 iret 指令在 64 bit 模式下 default operand-size 是 32 位的,這不同于 ret 指令。ret 指令的 default operand-size 是 64 位。所以,中斷返回時需使用 iretq 指令,即:使用指令前綴 REX.W(48) 將 iret 調(diào)整為 iretq








7.1.3.7.1、 compatibility 模式與 64 bit 模式之間的切換

  由于 long mode 下有 compatiblity 與 64 bit 兩種子模式,那么在 x64 版本的 64 位 OS 里就有可能存在 compatibility 與 64 bit 模式之間的切換情況發(fā)生。
  64 位 OS 核心運行在 64 bit 模式下,當系統(tǒng)加載的是原 x86 下的 32 位用戶程序,processor 將從 64 bit 切換到 compatiblity 模式。32 位軟件返回 OS 時,從 compatibility 模式切換回 64 bit 模式。









----- 待續(xù)

[ 本帖最后由 mik 于 2009-5-2 00:47 編輯 ]

論壇徽章:
0
36 [報告]
發(fā)表于 2009-05-31 00:30 |只看該作者
34、使用 sysenter/sysexit 指令快速切換



  通過 GDT / IDT 表索引查找 target code-segment descriptor 顯得不夠迅速。
* 原因1:經(jīng)過多重的索引查找
* 原因2:經(jīng)過相關(guān)的權(quán)限檢查

  因此 Intel 推出了 sysenter 指令來快速陷入 0 層,同時免去了相關(guān)的權(quán)限檢查環(huán)節(jié)。sysexit 指令快速返回。



1、sysenter/sysexit 的使用前提

* sysenter/sysexit 必須在 protected 模式下執(zhí)行。在 real 模式下使用將產(chǎn)生 #GP 異常。
* sysenter/sysexit 必須執(zhí)行在 flat 內(nèi)存模式,即:CS.base = 0 & SS.base = 0
* 在 AMD 的 processor 中,sysenter/sysexit 在 long mode 是無效的,在 long mode 下使用將產(chǎn)生 #UD 異常。
  在 Intel 的 processor 中,sysenter/sysexit 在 long mode 是有效的。



2、sysenter 的使用環(huán)境

  前面說過,sysenter/sysexit 須使用在 flat 內(nèi)存模式下。

  雖然,在相應的 MSR 寄存器中提供了 CS 和 SS selector,但實際上這個 CS 和 SS selector 將被忽略。而以硬性的 code segment descriptor 和 stack segment descriptor 來代替。processor 自動負責這個具體工作的實施。

processor 會重設 code / stack segment descriptor
* CS.base = 0  &  SS.base = 0
* CS.limit = FFFFF  &  SS.limit = FFFFF    (4G 的 limit,實際等于 CS.limit = FFFFFFFF)
* CS.G = 1  &  SS.G = 1
* CS.DPL = 0  &  SS.DPL = 0  (強制為 0 級權(quán)限)
* CS.type = 1011。╡xecute/read  & accessed)
* SS.type = 0011   (read/write & accessed)
* CS.P = 1  &  SS.P = 1

無論提供的 code/stack segment descriptor 是怎樣,processor 將硬性按上面的重設 CS 與 SS 環(huán)境。




3、sysenter 執(zhí)行環(huán)境的獲取

  processor 會按規(guī)則去獲取執(zhí)行環(huán)境,包括:CS 和 SS selector、eip 和 esp(代碼入口和棧頂入口)。

* MSR_SYSENTER_CS[15:0]
    獲取 CS selector。這個 MSR 的 bit15 ~ bit0 提供了 CS selector,而 bit64 ~ bit16 須為 0。MSR_SYSENTER_CS[15:0] 將載入 CS 中。

* MSR_SYSENTER_EIP[31:0]
  提供 target code segment 的入口 EIP 值,MSR_SYSENTER_EIP[31:0] 將載入 EIP 中。

* MSR_SYSENTER_ESP[31:0]
  提供 target stack segment 的入口 ESP 值,MSR_SYSENTER_ESP[31:0] 將載入 ESP 中。

* SS selector 的獲取
  沒有直接 MSR 寄存器去提供 SS selector,但是,Intel 將視提供的 CS descriptor 的下一個 descriptor 為 SS descriptor。
  也就是說:由 MSR_SYSENTER_CS[15:0] 提供的 CS selector 的下一個就是 SS selctor。

XXXXXXXXXXXXX   0  00
-----------------
         |
         +---------------> CS_selector.SI + 1(加上8)


由上面可見,CS seelctor 的下一個 selector 就是 SS selector。

其結(jié)果就是:SS selector = MSR_SYSENTER_CS + 8
---------------------------------------------------------
  前面說過,MSR 雖然提供了 CS 和 SS selector,但最終會被 processor 重設。




4、 sysenter 執(zhí)行環(huán)境的設置

  由于 CS & SS selector、EIP 以及 ESP 在相應的 MSR 寄存器中獲取,所以使用前須由指令 WRMSR 指令來設定相應的值。

* MSR_SYSENTER_CS 的地址在 174H
* MSR_SYSENTER_ESP 的地址在 175H
* MSR_SYSENTER_EIP 的地址在 176H


實例:
  xor edx, edx                              /* MSR_SYSENTER_EIP[63:32] 為 0 */
  mov eax, 0x80100400                  /* eip 為 0x80100400 */
  mov ecx, 0x176                          /* MSR_SYSENTER_EIP 地址 */
  wrmsr                                       /* 寫 MSR */
-------------------------------------
  上面這段代碼將設置 MSR_SYSENTER_EIP[31:0] 值為 0x80100400





5、 sysexit 執(zhí)行環(huán)境的獲取

  當 sysenter 指令執(zhí)行到的 0 級代碼使用 sysexit 返回時,sysexit 的執(zhí)行,processor 同樣按規(guī)則去獲取返回執(zhí)行環(huán)境。


* 返回 CS selector 的獲取
  MSR_SYSENTER_CS[15:0] 提供的 CS slector 的下第二個 selector 為返回的 CS selector。

XXXXXXXXXXXXX   0  00
-----------------
         |
         +---------------> CS_selector.SI + 2(加上16)


MSR_SYSENTER_CS[15:0] 的下第二個 selector 就是 CS selector。

其結(jié)果就是:返回 CS selector = MSR_SYSENTER_CS + 16



* 返回 SS selector 的獲取
  同樣根據(jù)規(guī)則 MSR_SYSENTER_CS[15:0] 提供的 selector 的下第三個 selector 就是返回的 SS selector。

XXXXXXXXXXXXX   0  00
-----------------
         |
         +---------------> CS_selector.SI + 3(加上24)


MSR_SYSENTER_CS[15:0] 接下來的第 3  個 selector 就是返回的 SS selector。

其結(jié)果就是:返回的 SS selector = MSR_SYSENTER_CS+24



* 返回的 EIP 和 ESP

EIP 和 ESP 簡單得多:
  EIP 放在 EDX 寄存器中。
  ESP 放在 ECX 寄存器中。



6、 sysexit 的使用環(huán)境

  和 sysenter 指令一樣, sysexit 指令的使用環(huán)境 CS 和 SS 同樣會被 processor 重新設定,雖然給出了返回的 CS 和 SS。

除了 DPL 外,sysexit 和 sysenter 的環(huán)境是一樣的。

* CS.DPL = 3(CS.CPL = 3)  &   SS.DPL =3

-------------------------------------------------------------
  區(qū)別就是:sysenter 目標代碼必須是 0 級,sysexit 目標代碼必須是 3 級。





7、 long mode 下的 sysenter/sysexit 指令

  僅在 Intel 的 processor 下才有效,AMD 的 processor 在 long mode 下使用 syscall/sysret 來代替。

  在 long mode 下,MSR_SYSENTER_ESP[63:32] 和 MSR_SYSENTER_EIP[63:32] 是有效的,擴展成 64 位,變成了 MSR_SYSENTER_RSP 和 MSR_SYSENTER_RIP。

* sysenter 進入的 target code segment descriptor 的 L 必須是 1,D = 0
  即:目標的 CS.L = 1 & CS.D = 0 表示目標為 64 位代碼。

* MSR_SYSENTER_RSP 和 MSR_SYSENTER_RIP 的值必須的 canonical address 形式。





8、sysenter 的執(zhí)行

if (CR0.PE == 0)                                   /* non-proected mode */
        goto do_GP_exception;        

if (MSR_SYSENTER_CS[15:2] == 0)         /* NULL-selector */
        goto do_GP_exception;               

eflags.VM = 0;
eflags.IF = 0;
eflags.RF = 0;


if (EFER.LMA == 0)                               /* x86-legacy mode */
         goto x86_sysenter;
else
         goto x64_sysenter;                     /* long mode */



x86_sysenter:

        CS.selector = MSR_SYSENTER_CS;               /* load CS selector with MSR_SYSENTER_CS[15:0] */
        CS.selector.RPL = 0;
        CS.base = 0x00000000;
        CS.base = 0xFFFFFFFF;
        CS.type = 0x0B;                                              /* type = 1011 */
                                                                            /* non-conforming, excecute/readable, accessed */
        CS.D = 1;                                                       /* 32 bit code segment */
        CS.P = 1;
        CS.G = 1;
        CS.S = 1;                                                       /* non-system descriptor */


        SS.selector = MSR_SYSENTER_CS + 8;               /* next-selector with MSR_SYSENTER_CS + 8 */
        SS.selector.RPL = 0;
        SS.base = 0x00000000;
        SS.limit = 0xFFFFFFFF;
        SS.type = 0x03;                                            /* type = 0011 */
                                                                          /* expand-up, write/read, accessed */
        SS.D = 1;                                                     /* 32 bit stack pointer */
        SS.P = 1;
        SS.G = 1;
        SS.S = 1;


        esp = MSR_SYSENTER_ESP;
        eip = MSR_SYSENTER_EIP;




x64_sysenter:

        CS.selector = MSR_SYSENTER_CS;               /* load CS selector with MSR_SYSENTER_CS[15:0] */
        CS.selector.RPL = 0;
        CS.base = 0x00000000_00000000;
        CS.base = 0xFFFFFFFF_FFFFFFFF;
        CS.type = 0x0B;                                              /* type = 1011 */
                                                                            /* non-conforming, excecute/readable, accessed */
        CS.L = 1;
        CS.D = 0;                                                       /* 64 bit code segment */
        CS.P = 1;
        CS.G = 1;
        CS.S = 1;                                                       /* non-system descriptor */


        SS.selector = MSR_SYSENTER_CS + 8;               /* next-selector with MSR_SYSENTER_CS + 8 */
        SS.selector.RPL = 0;
        SS.base = 0x00000000_00000000;
        SS.limit = 0xFFFFFFFF_FFFFFFFF;
        SS.type = 0x03;                                            /* type = 0011 */
                                                                          /* expand-up, write/read, accessed */
        SS.D = 1;                                                     /* 64 bit stack pointer */
        SS.P = 1;
        SS.G = 1;
        SS.S = 1;


        rsp = MSR_SYSENTER_RSP;
        rip = MSR_SYSENTER_RIP;

---------------------------------------------------------------------------------------------
  processor 設置必要的執(zhí)行環(huán)境:CS 和 SS 都是 0 級的 flat 內(nèi)存模式。




9、sysexit 的執(zhí)行

if (CR0.PE == 0)                                   /* non-proected mode */
        goto do_GP_exception;        

if (MSR_SYSENTER_CS[15:2] == 0)         /* NULL-selector */
        goto do_GP_exception;               

if (CS.RPL != 0)                                   /* CPL != 0 */
        goto do_GP_exception;               


if (EFER.LMA == 0)                               /* x86-legacy mode */
         goto x86_sysexit;
else
         goto x64_sysexit;                     /* long mode */



x86_sysexit:

        CS.selector = MSR_SYSENTER_CS + 16;           /* MSR_SYSENTER_CS[15:0] + 16 */
        CS.selector.RPL = 3;
        CS.base = 0x00000000;
        CS.base = 0xFFFFFFFF;
        CS.type = 0x0B;                                              /* type = 1011 */
                                                                            /* non-conforming, excecute/readable, accessed */
        CS.D = 1;                                                       /* 32 bit code segment */
        CS.P = 1;
        CS.G = 1;
        CS.S = 1;                                                       /* non-system descriptor */


        SS.selector = MSR_SYSENTER_CS + 24;               
        SS.selector.RPL = 3;
        SS.base = 0x00000000;
        SS.limit = 0xFFFFFFFF;
        SS.type = 0x03;                                            /* type = 0011 */
                                                                          /* expand-up, write/read, accessed */
        SS.D = 1;                                                     /* 32 bit stack pointer */
        SS.P = 1;
        SS.G = 1;
        SS.S = 1;


        esp = ecx;
        eip = edx;



x64_sysexit:

        if (opcode[0] == REX.W)                          /* return to 64 bit */
                goto 64bit_sysexit;
        else
                goto 32bit_sysexit;


64bit_sysexit:

        CS.selector = MSR_SYSENTER_CS + 32;               /* MSR_SYSENTER_CS[15:0] + 32 */
        CS.selector.RPL = 3;
        CS.base = 0x00000000_00000000;
        CS.base = 0xFFFFFFFF_FFFFFFFF;
        CS.type = 0x0B;                                              /* type = 1011 */
                                                                            /* non-conforming, excecute/readable, accessed */
        CS.L = 1;
        CS.D = 0;                                                       /* 64 bit code segment */
        CS.P = 1;
        CS.G = 1;
        CS.S = 1;                                                       /* non-system descriptor */


        SS.selector = MSR_SYSENTER_CS + 40;               /* next-selector with MSR_SYSENTER_CS + 40 */
        SS.selector.RPL = 0;
        SS.base = 0x00000000_00000000;
        SS.limit = 0xFFFFFFFF_FFFFFFFF;
        SS.type = 0x03;                                            /* type = 0011 */
                                                                          /* expand-up, write/read, accessed */
        SS.D = 1;                                                     /* 64 bit stack pointer */
        SS.P = 1;
        SS.G = 1;
        SS.S = 1;


        rsp = rcx;
        rip = rdx;



32bit_sysexit:

         CS.L = 0
         goto x86_sysexit;





:wink:

[ 本帖最后由 mik 于 2009-6-7 00:31 編輯 ]

論壇徽章:
0
37 [報告]
發(fā)表于 2009-05-31 23:51 |只看該作者
35、使用 syscall/sysret 指令快速切換



  syscall/sysret 由 AMD 提出替代 sysenter/sysret 指令用來解決 64 位下的 fast call。其原理和 sysenter/sysexit 差不多。



1、syscall/sysret 與 sysenter/sysexit 不同之處

* 在 AMD 的 processor 中,syscall/sysret 在 long mode 和 legacy mode 都是有效的。sysenter/sysexit 僅在 legacy mode 才有效。
  在 Intel 的 processor 中,syscall/sysret 僅在 long mode 才有效,sysenter/sysexit 在 long mode 和 legacy mode 都是有效的。

* 在 Intel processor 的 long mode 中,sysenter/sysexit 的使用其原理和 x86 模式中一樣,區(qū)別僅在于 MSR_SYSENTER_EIP[63:0] 和 MSR_SYSENTER_ESP[63:0] 都是有效的。
 在 AMD processor 的 long mode 中,syscall/sysret 新增了 LSTAR 、CSTAR 以及 SFMASK 寄存器。用來存放目標的 RIP 值。



2、syscall/sysret 使用環(huán)境

  和 sysenter/sysexit 一樣,syscall 的目標代碼是 0 級的平坦模式代碼。sysret 的目標代碼是 3 級的平坦模式代碼。


(1) MSR_STAR[63:0] 寄存器

  這個寄存器分為 3 部分:MSR_STAR[31:0]、MSR_STAR[47:32] 以及 MSR_STAR[63:48]
  MSR_STAR 寄存器的地址在 C000_0081

* MSR_STAR[31:0]
  STAR 寄存器的低 32 位提供目標代碼的 EIP 值。將被加載到 EIP 寄存器。

* MSR_STAR[47:32]
  這部分的 16 位為 syscall 的目標代碼提供 CS selector
  
  syscall 指令獲取目標代碼的 CS selector 由 MSR_STAR[47:32] 直接提供,而 SS selector 將是 CS selector 的下一個 selector,也就是 CS selector + 8

下一個 selector 是這樣形成的:

XXXXXXXXXXXXX   0  00
-----------------
         |
         +---------------> CS_selector.SI + 1(即:selector 加上 8 )



* MSR_STAR[63:48]
  STAR 寄存器的高 16 位,為 sysret 提供目標代碼的 CS selector

  sysret 指令獲取目標代碼的 CS selector 與 SS selector 稍為復雜些,這個復雜的情況是由模式的切換產(chǎn)生。


當 sysret 切換到 x86 或 compatiblity 模式時:

  返回的目標代碼的 CS selector 由 MSR_STAR[63:48] 直接提供,SS selector 由 CS selector 的下一個 selector 提供,即:SS selector = CS selector + 8


SS selector 是這樣產(chǎn)生的:

XXXXXXXXXXXXX   0  00
-----------------
         |
         +---------------> CS_selector.SI + 1(即:selector 加上 8 )




當 sysret 切換到 64 bit 模式時:

  返回的目標代碼的 CS selector 則是 MSR_STAR[63:48] + 16,也就是接著的第 2 個 selector。


此時,CS selector 是這樣產(chǎn)生的:

XXXXXXXXXXXXX   0  00
-----------------
         |
         +---------------> CS_selector.SI + 2(即:selector 加上 16 )




SS selector 則是:MSR_STAR[63:48] + 8,即:

XXXXXXXXXXXXX   0  00
-----------------
         |
         +---------------> CS_selector.SI + 1(即:selector 加上 8 )



---------------------------------------------------------------------

  因此,MSR_STAR[63:48] 實則表示為:MSR_STAR.SYSRET_CS。

  無論什么時候,MSR_STAR.SYSRET_CS 提供的是 x86/compatibility 模式的 CS selector,64 bit 模式的 CS selector 則是 MSR_STAR.SYSRET_CS + 16。
  x86/compatibility 與 64 bit 模式的 SS selector 都為 MSR_STAR.SYSRET_CS + 8





(2) MSR_LSTAR[63:0] 寄存器

  MSR_LSTAR 寄存器 64 位寬,使用在 64 bit 模式下。當 processor 處于 64 bit 模式下,syscall 指令從 MSR_LSTAR 寄存器讀取 RIP 值。
  MSR_LSTAR 寄存器的地址在 C000_0082



(3) MSR_CSTAR[63:0] 寄存器

  MSR_CSTAR 寄存器 64 位寬,使用在 compatibility 模式下。當 processor 處于 compatibility 模式下,syscall 指令將從 MSR_CSTAR 寄存器讀取 RIP 值。
  MSR_CSTAR 寄存器的地址在 C000_0083


(4) MSR_SFMASK[63:0] 寄存器

  MSR_SFMASK 寄存器是 RFLAGS 寄存器的掩碼。MSR_SFMASK 寄存器每一位對應于 RFLAGS 寄存器的每一位。
* 當 MSR_SFMASK 寄存器的位被置 1 ,相應的 RFLAGS 寄存器位為 0
* 當 MSR_SFMASK 寄存器的位被置 0 ,相應的 RFLAGS 寄存器位沒改變

  MSR_SFMASK 寄存器的地址在 C000_0084
  




3、syscall 執(zhí)行

下面 syscall 執(zhí)行流程的 C 代碼:
------------------------------------------------------------------------------------------------

if (EFER.SCE == 0)                        /* syscall support ? */
        goto do_UD_exception         /*  #UD exception */


if (EFER.LMA == 0)                         /* x86-legacy mode */
        goto x86_syscall;                    /* do x86-legacy syscall */
else
        goto x64_syscall;                    /* do x64-long mode syscall */



x86_syscall:

        eflags.VM = 0;
        eflags.IF = 0;
        eflags.RF = 0;

        ecx = eip;                                                    /* save EIP for sysret return */

        CS.selector = MSR_STAR.SYSCALL_CS;             /* load CS selector with MSR_STAR[47:32] */
        CS.selector.CPL = 00;                                    /* target code-segment's CPL = 00 */
        CS.base = 0x00000000;
        CS.limit = 0xFFFFFFFF;
        CS.type = 0x0B;                                           /* CS.type = 1011 */
                                                                         /* non-conforming, execute/readable, accessed */

        SS.selector = MSR_STAR.SYSCALL_CS + 8;       /* CS selector next selector */
        SS.selector.RPL = 00;                                    /* target stack-segment's DPL = 00 */
        SS.base = 0x00000000;
        SS.limit = 0xFFFFFFFF;
        SS.type = 0x03;                                           /* SS.type = 0011 */
                                                                         /* expand-up, read/write, accessed */

        eip = MSR_STAR.EIP;                                    /* load into EIP with MSR_STAR[31:0] */



x64_syscall:

        rcx = rip;                              /* save rip for sysret return */        
        r11 = rflags;                          /* save rflags for syscall routine */


        rflags = rflags | ~MSR_SFMASK;        /* mask with MSR_SFMASK */
        rflags.RF = 0;


        if (CS.L == 1 && CS.D == 0) {            /* it's 64 bit mode */

                 temp_rip = MSR_LSTAR;

        } else if (CS.L == 0) {                      /* it's compatibility mode */
                 temp_rip = MSR_CSTAR;
        }



        CS.selector = MSR_STAR.SYSCALL_CS;           /* load CS.selector with MSR_STAR[47:32] */
        CS.selector.CPL = 00;                                  /* CPL = 00 */
        CS.base = 0x00000000_00000000;                 
        CS.limit = 0xFFFFFFFF_FFFFFFFF;
        CS.type = 0x0B;                                         /* CS.type = 1011 */
        CS.L = 1;
        CS.D = 0;


        SS.selector = MSR_STAR.SYSCALL_CS + 8;     /* next selector */


/* the follow is ignored and imlicit processor set follow force to ... ... */

        SS.selector.DPL = 00;
        SS.base = 0x00000000_00000000;
        SS.limit = 0xFFFFFFFF_FFFFFFFF;
        SS.type = 0x03;                                         /* SS.type = 0011 */



        rip = temp_rip;


-----------------------------------------------------------------------------------------------------
  syscall 指令執(zhí)行目標的 CPL = 0,processor 強制將 CPL = 0 以及 CS 和 SS 的屬性全部強制為 flat 內(nèi)存模式。在 long mode 下,syscall 目標的 CS & SS 絕大部分屬性都是無效的。processor 會自動設為 flat 的 64 位地址模式。
  值得注意的是:syscall 指令并沒提供目標的 esp/rsp 值,這將導致沿用當前的 esp/rsp 值。而 sysenter 指令提供了目標的 esp/rsp 值。




4、sysret 的執(zhí)行

  sysret 當前執(zhí)行 CPL = 0,返回目標的 CPL = 3,返回的 eip/rip 將從 ecx/rcx 中獲取。


if (EFER.SCE == 0)
        goto do_UD_exception;

if (CS.RPL != 0)                           /* CPL != 0 */
        goto do_GP_exception;


if (EFER.LMA == 0)                        /* x86-legacy mode */
        goto x86_sysret;
else
        goto x64_sysret;                  /* x64-long mode */



x86_sysret:

        CS.selector = MSR_STAR.SYSRET_CS;                  /* load CS selector with MSR_STAR[63:48]    */
        CS.selector.RPL = 3;                                          /* target's CPL = 3 */
        CS.base = 0x00000000;
        CS.limit = 0xFFFFFFFF;
        CS.type = 0x0B;                                           /* CS.type = 1011 */
                                                                         /* non-conforming, execute/readable, accessed */

        SS.selector = MSR_STAR.SYSRET_CS + 8;              /* load SS selector with next selector */

        eflags.IF = 1;
        eip = ecx;



x64_sysret:

        if (Opcode[0] == REX.W) {                     /* return to 64 bit mode */

                CS.selector = MSR_STAR.SYSRET_CS + 16;            
                CS.selector.RPL = 3;         
                CS.base = 0x00000000_00000000;
                CS.limit = 0xFFFFFFFF_FFFFFFFF;
                CS.type = 0x0B;
                CS.L = 1;
                CS.D = 0;

        } else {                                                  /* return to compatibility */

                CS.selector = MSR_STAR.SYSRET_CS;                 
                CS.base = 0x00000000;
                CS.limit = 0xFFFFFFFF;
                CS.type = 0x0B;
        }

        SS.selector = MSR_STAR.SYSRET_CS + 8;

        temp_rip = rcx;
        rflags = r11;                                 /* restore rflags */


        rip = rcx;


------------------------------------------------------------------------------------
  sysret 指令執(zhí)行在 long mode 和 x86 mode 之間存在差異:

在 x86-legacy mode 下:
  返回的 CS selector 從 MSR_STAR[63:48](即上述的 MSR_STAR.SYSRET_CS)處加載,SS selector 由 MSR_STAR[63:48] + 8 所得。


在 x64-long mode 下:
  processor 將視乎返回到 64 bit 模式還是 compatibility 模式?返回 64 bit 模式時,CS selector 將由 MSR_STAR[63:48] + 16 所得。SS selector 還是由 MSR_STAR[63:48] + 8 所得。

  由于 sysret 指令缺省的 operand size 是 32 位。
  當 sysret 指令使用 REX prefix 來返回到 64 bit 模式,當 REX.W = 1 時,sysret 指令的 operand size 是 64 位,表示 sysret 將返回到 64 位代碼。
  當 sysret 指令不使用 REX prefix 或者 REX.W = 0 時,sysret 指令將返回到 32 位或 compatibility 模式。






:wink:

[ 本帖最后由 mik 于 2009-6-6 23:20 編輯 ]

論壇徽章:
0
38 [報告]
發(fā)表于 2009-06-07 03:24 |只看該作者
36、使用 ret/iret 指令進行切換


  使用 ret 和 iret 指令只能切換到同級或低級權(quán)限的代碼,若使用 ret 和 iret 切換到高權(quán)限代碼,將產(chǎn)生 #GP 異常。


一、far ret

















--- 待續(xù)

論壇徽章:
0
39 [報告]
發(fā)表于 2009-07-11 00:41 |只看該作者
37、TSS 詳解


  這里以 xp 的實例 TSS 來觀察


1、TSS 的地址和長度

  TSS descriptor 指出 TSS 塊的地址和長度。

<bochs:4> info gdt 0x14
Global Descriptor Table <base=0x000000008003f000, limit=1234>:
GDT[0x14]=32-Bit TSS <Available> at 0x863b02d8, length=0x00068

在 GDT 的 0x14 entry 是 32-TSS descriptor,它描述 TSS 塊在 0x863b02d8 地址上,長度為 0x68

TSS 的長度必須不低于 0x68,否則產(chǎn)生 #GP 異常。



2、觀察 TSS 塊的內(nèi)容

<bochs:7> x /26wx 0x863b02d8
[bochs ]:
0x00000000863b02d8 <bogus+        0>: 0x00000000  0x80547100  0x00000010  0x00000000
0x00000000863b02e8 <bogus+       16>: 0x00000000  0x00000000  0x00000000  0x00ad7000
0x00000000863b02f8 <bogus+       32>: 0x806cfbe8  0x00000000  0x00000000  0x00000000
0x00000000863b0308 <bogus+       48>: 0x00000000  0x00000000  0x80547100  0x00000000
0x00000000863b0318 <bogus+       64>: 0x00000000  0x00000000  0x00000023  0x00000008
0x00000000863b0328 <bogus+       80>: 0x00000010  0x00000023  0x0000030  0x00000000
0x00000000863b0338 <bogus+       96>: 0x00000000  0x20da0000


從上面可以看出:

Link  = 0x0000
esp0 = 0x80547100
ss0   = 0x00000010
esp1 = 0x00000000
ss1   = 0x00000000
esp2 = 0x00000000
ss2   = 0x00000000
cr3   = 0x00ad7000
eip   = 0x806cfbe8
eflags = 0x00000000
eax    = 0x00000000
ecx    = 0x00000000
edx    = 0x00000000
ebx    = 0x00000000
esp    = 0x80547100
ebp    = 0x00000000
esi    = 0x00000000
edi    = 0x00000000
es    = 0x0023
cs    = 0x0008
ss    = 0x0010
ds    = 0x0023
fs    = 0x0030
gs    = 0x0000
ldt    = 0x0000
IOPBBA  = 0x20da

----------------------------------------------
ss1/esp1 和 ss2/esp2 都設為 0

IOPB 的 base 地址在 0x863b02d8+0x20da

[ 本帖最后由 mik 于 2009-7-20 01:34 編輯 ]

論壇徽章:
0
40 [報告]
發(fā)表于 2012-03-14 21:22 |只看該作者
:wink:
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP