x64 番外篇——保護模式相關

寂靜的羽夏發表於2022-04-02

寫在前面

  此係列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統核心的複雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閒錢,可以打賞支援我的創作。如想轉載,請把我的轉載資訊附在文章後面,並宣告我的個人資訊和本人部落格地址即可,但必須事先通知我

你如果是從中間插過來看的,請仔細閱讀 羽夏看Win系統核心——簡述 ,方便學習本教程。

  看此教程之前,問幾個問題,基礎知識儲備好了嗎?保護模式篇學會了嗎?練習做完了嗎?沒有的話就不要繼續了。


? 華麗的分割線 ?


概述

  在64位下,有兩種CPU模式,一種是IA-32e模式,是IA-32模式的擴充套件,另一個是Legacy模式。IA-32e模式是指核心64位,使用者64或32位均可,它強制平坦段,不支援任務切換;而Legacy模式指核心32位,使用者32位支援非平坦段、任務切換、虛擬8086、真實模式等。
  在IA-32e模式下,程式碼段和資料段仍使用64位描述符,強制平坦(FSGS除外);TSS段描述符擴充套件到128位,TSS段不用來任務切換,主要儲存一堆rsp備用指標;中斷門描述符擴充套件到128位。
  有些暫存器我們需要知道的,如下所示:

MSR 暫存器名 索引數值
IA32_EFER_MSR C0000080H
IA32_FS_BASE C0000100H
IA32_GS_BASE C0000101H
IA32_KERNEL_GS_BASE C0000102H

IA32_EFER_MSR

  該MSR結構如下所示:

x64 番外篇——保護模式相關

  索引0位表示SYSCALL/SYSRET這類指令是否啟用,索引8位表示IA-32e模式是否啟用,索引10位表示IA-32e模式是否處於處於活動狀態,索引11位知識是否啟用PAE分頁的XD位是否有效。如果到這裡有不會的地方,請回去複習。
  下面我們在虛擬機器中讀取該暫存器的值:

kd> rdmsr C0000080
msr[c0000080] = 00000000`00000d01
kd> .formats 00000000`00000d01
Evaluate expression:
  Hex:     00000000`00000d01
  Decimal: 3329
  Octal:   0000000000000000006401
  Binary:  00000000 00000000 00000000 00000000 00000000 00000000 00001101 00000001
  Chars:   ........
  Time:    Thu Jan  1 08:55:29 1970
  Float:   low 4.66492e-042 high 0
  Double:  1.64474e-320

  從上面的值我們可以看出暫存器裡面的所有的功能處於啟用狀態。

IA32_FS_BASE

  這個MSR暫存器儲存的是fs的基址,由於是64位核心,直接讀取的話該值是0,我們可以讀取一下:

kd> rdmsr C0000100
msr[c0000100] = 00000000`00000000

IA32_GS_BASE

  這個MSR暫存器儲存的是gs的基址,核心指向KPCR結構體(和32位的fs一樣的作用),我們可以嘗試以下:

kd> rdmsr C0000101
msr[c0000101] = fffff805`5e089000

kd> dt _KPCR fffff805`5e089000
nt!_KPCR
   +0x000 NtTib            : _NT_TIB
   +0x000 GdtBase          : 0xfffff805`65690fb0 _KGDTENTRY64
   +0x008 TssBase          : 0xfffff805`6568f000 _KTSS64
   +0x010 UserRsp          : 0
   +0x018 Self             : 0xfffff805`5e089000 _KPCR
   +0x020 CurrentPrcb      : 0xfffff805`5e089180 _KPRCB
   +0x028 LockArray        : 0xfffff805`5e089870 _KSPIN_LOCK_QUEUE
   +0x030 Used_Self        : 0x00000079`22c2a000 Void
   +0x038 IdtBase          : 0xfffff805`6568e000 _KIDTENTRY64
   +0x040 Unused           : [2] 0
   +0x050 Irql             : 0 ''
   +0x051 SecondLevelCacheAssociativity : 0xc ''
   +0x052 ObsoleteNumber   : 0 ''
   +0x053 Fill0            : 0 ''
   +0x054 Unused0          : [3] 0
   +0x060 MajorVersion     : 1
   +0x062 MinorVersion     : 1
   +0x064 StallScaleFactor : 0xb58
   +0x068 Unused1          : [3] (null) 
   +0x080 KernelReserved   : [15] 0
   +0x0bc SecondLevelCacheSize : 0x900000
   +0x0c0 HalReserved      : [16] 0xad178600
   +0x100 Unused2          : 0
   +0x108 KdVersionBlock   : (null) 
   +0x110 Unused3          : (null) 
   +0x118 PcrAlign1        : [24] 0
   +0x180 Prcb             : _KPRCB

IA32_KERNEL_GS_BASE

  這個MSR的作用是作為GS基址的交換目標。說人話的話就是一個快取,交換比較方便。我們從3環到0環。從0環到3環,GS核心指向KPCR,3環指向TEB,是如何做到呢?這個就是一個非常重要的暫存器.進0環的時候,把3環的交給它;出0環的時候,把0環的交給它。
  我們來讀一下:

kd> rdmsr C0000102
msr[c0000102] = 00000079`22c2a000

  按照上述說法,這個就是TEB了,但由於這個是3環的地址,我們無法讀取裡面的結構體內容。
  由於現在64位CPU一般安裝的系統都是IA-32e模式下的,即核心64位的,故後續所有介紹都會以IA-32e模式為主。

段與段描述符

  在64位系統下,段的保護被進一步弱化。絕大多數的段被強制平坦。何為強制平坦,就是段的首地址和界限的值不再起作用,被保留置0。首地址預設是0,界限預設十六進位制全是F。但也有例外,比如GSFS,因為它們指向一些結構體,這些結構體的首地址肯定不是0。
  64位的段和32位的段大體上就上述變化,但有一個段比較特別,那就是程式碼段,我們來看一下它的結構:

x64 番外篇——保護模式相關

  程式碼段比32位的多了個位L,它的作用是來指示是32位的還是64位的,如果是0表示相容模式(x86模式),為1則表示x64模式。
  裡面還有一個D位,它的作用是指示預設大小的。如果L == 0,如果該位是0,預設的資料和地址大小為16位;反之為32位。如果L == 1D位必須為0,否則會觸發通用保護異常。
  最後要介紹的段就是TSS段,被擴充套件為128位,用於滿足定址要求,如下結構所示:

x64 番外篇——保護模式相關

  TSS和32位的作業系統一樣,只是用來儲存一堆暫存器的,並不用於任務切換。

  在64位下分頁不再是PAE分頁,而是被Intel稱之為4級分頁(4-LEVEL PAGING)的東西。當CR0.PG == 1 && CR4.PAE == 1 && IA32_EFER.LME == 1時會啟用該分頁模式。4級分頁將48位線性地址轉換為52位實體地址。雖然52位對應於4 PBytes,但線性地址僅限於48位,最多可以訪問256 TBytes的線性地址空間。也就是說剩餘的16位是被保留的。
  4級分頁使用分頁結構的層次結構來生成線性地址的轉換。CR3用於定位第一個分頁結構,即PML4表。將CR3與4級分頁一起使用取決於是否已通過設定CR4.PCIDE啟用程式上下文識別符號。
  這個CR3也是結構的,並不直接是實體地址的,如果CR4.PCIDE為0,則它的結構如下:

x64 番外篇——保護模式相關

  如果CR4.PCIDE為1,則它的結構如下:

x64 番外篇——保護模式相關

  其中MMAXPHYADDR的縮寫,最大是52,這個值通過CPUID.80000008H:EAX[7:0]進行查詢,在我的虛擬機器作業系統這個值為十進位制的39

kd> r cr4
cr4=0000000000070678

kd> .formats 0000000000070678
Evaluate expression:
  Hex:     00000000`00070678
  Decimal: 460408
  Octal:   0000000000000001603170
  Binary:  00000000 00000000 00000000 00000000 00000000 00000111 00000110 01111000
  Chars:   .......x
  Time:    Tue Jan  6 15:53:28 1970
  Float:   low 6.45169e-040 high 0
  Double:  2.27472e-318

  首先我們得確認這個CR4.PCIDE是在哪個位上,發現是在索引17位:

x64 番外篇——保護模式相關

  這個位被置1了,說明是第二種形式。有關這部分細節,對於我們不寫作業系統的人來說似乎沒啥用處,就不追究了。
  下面我們來看看4級分頁線性地址的結構,如下是線性地址如何翻譯成實體地址的:

x64 番外篇——保護模式相關

  按照之前的說法套路,給一個非正式的名字9-9-9-9-12分頁。又了之前的底子,我就不贅述內部的詳細結構,直接用一張圖進行總結(在Intel白皮書的第2832頁):

x64 番外篇——保護模式相關

  細心的你可能會發現protection key這個東西,這是啥呢?這個就是4級分頁的一個新的機制,提供更完全的保護,當CR4.PKE == 1時有效,不幸的是,在我的虛擬機器的作業系統並不使用該位,所以無效,如果對這塊技術感興趣請仔細閱讀Intel白皮書的第2835頁。
  當然上述分頁還有大頁的情形,如下所示:

x64 番外篇——保護模式相關

x64 番外篇——保護模式相關

  下面我們就用x86下的實驗方式來學習分頁,首先你得有新版的CheatEngine,下面開始:

x64 番外篇——保護模式相關

  如上圖所示,我開啟了一個CheatEngine和一個Notepad,並且在記事本里面寫上hello,world!。然後我們開始老套路搜尋記憶體:

x64 番外篇——保護模式相關

  把,改為%,定位到指定的地址,如下圖所示:

x64 番外篇——保護模式相關

  我們先用vtop執行偷懶看看結果:

kd> !process 0 0 notepad.exe
PROCESS ffff8f0fec1be080
    SessionId: 1  Cid: 091c    Peb: 4e42d08000  ParentCid: 0b70
    DirBase: 9bd65002  ObjectTable: ffffdd8291064c40  HandleCount: 356.
    Image: notepad.exe

kd> !vtop 9bd65000 1DEFB0044B0 
Amd64VtoP: Virt 000001defb0044b0, pagedir 000000009bd65000
Amd64VtoP: PML4E 000000009bd65018
Amd64VtoP: PDPE 000000009d581bd8
Amd64VtoP: PDE 00000000a3882ec0
Amd64VtoP: PTE 00000000827ae020
Amd64VtoP: Mapped phys 0000000027fca4b0
Virtual address 1defb0044b0 translates to physical address 27fca4b0.
kd> !db 27fca4b0
#27fca4b0 68 00 65 00 6c 00 6c 00-6f 00 25 00 77 00 6f 00 h.e.l.l.o.%.w.o.
#27fca4c0 72 00 6c 00 64 00 21 00-0d 00 0a 00 00 00 00 00 r.l.d.!.........
#27fca4d0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#27fca4e0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#27fca4f0 00 00 00 00 00 00 00 00-08 00 79 fe de 01 00 00 ..........y.....
#27fca500 00 00 00 00 00 00 00 00-2a f8 73 48 50 71 00 10 ........*.sHPq..
#27fca510 30 48 00 fb de 01 00 00-00 42 00 fb de 01 00 00 0H.......B......
#27fca520 40 48 00 fb de 01 00 00-10 42 00 fb de 01 00 00 @H.......B......

  為什麼我在遍歷程式的後面加上notepad.exe呢?是因為在Win10上,就算你原裝最低配置,也有一大串的程式,帶來很大的煩惱。我明確是記事本的程式叫這個名字,我就在後面限定它。
  你或許還有一個疑問,為什麼命名目錄表基址的值為9bd65002,但為什麼vtop的時候後面的2沒了呢?那麼我們再回過頭來看看上圖的Cr3結構你就會明白為什麼。
  在做這個實驗的時候,發現了一個十分有意思的現象,當你恢復虛擬機器執行其他操作的時候,再次中斷回到Windbg,注意我沒有退出記事本,回來發現它的DirBase發生了變化。
  好,我們繼續我們的實驗:

kd> !process 0 0 notepad.exe
PROCESS ffff8f0fec1be080
    SessionId: 1  Cid: 091c    Peb: 4e42d08000  ParentCid: 0b70
    DirBase: 9c065002  ObjectTable: ffffdd8291064c40  HandleCount: 359.
    Image: notepad.exe

kd> !vtop 9c065000 1DEFB0044B0
Amd64VtoP: Virt 000001defb0044b0, pagedir 000000009c065000
Amd64VtoP: PML4E 000000009c065018
Amd64VtoP: PDPE 0000000036881bd8
Amd64VtoP: PDE 00000000a3982ec0
Amd64VtoP: PTE 00000000828ae020
Amd64VtoP: Mapped phys 0000000027fca4b0

  好我們手動拆分虛擬地址並用訪問實體地址的方式進行該實驗。首先拆分以下我們的虛擬地址:

000000011 = 0x3
101111011 = 0x17B
111011000 = 0x1D8
000000100 = 0x4
010010110000 = 0x4B0

  拆解完畢後,我們來開始進行測試:

kd> !dq 9c065000
#9c065000 8a000000`13a79867 00000000`00000000
#9c065010 00000000`00000000 8a000000`36881867
#9c065020 00000000`00000000 00000000`00000000
#9c065030 00000000`00000000 00000000`00000000
#9c065040 00000000`00000000 00000000`00000000
#9c065050 00000000`00000000 00000000`00000000
#9c065060 00000000`00000000 00000000`00000000
#9c065070 00000000`00000000 00000000`00000000
kd> !dq 36881000+17b*8
#36881bd8 0a000000`a3982867 00000000`00000000
#36881be8 00000000`00000000 00000000`00000000
#36881bf8 00000000`00000000 00000000`00000000
#36881c08 00000000`00000000 00000000`00000000
#36881c18 00000000`00000000 00000000`00000000
#36881c28 00000000`00000000 00000000`00000000
#36881c38 00000000`00000000 00000000`00000000
#36881c48 00000000`00000000 00000000`00000000
kd> !dq A3982000+1D8*8
#a3982ec0 0a000000`828ae867 0a000000`27aa6867
#a3982ed0 0a000000`1222f867 00000000`00000000
#a3982ee0 00000000`00000000 00000000`00000000
#a3982ef0 00000000`00000000 00000000`00000000
#a3982f00 00000000`00000000 00000000`00000000
#a3982f10 00000000`00000000 00000000`00000000
#a3982f20 0a000000`99493867 0a000000`91a7b867
#a3982f30 00000000`00000000 00000000`00000000
kd> !dq 828ae000+4*8
#828ae020 81000000`27fca867 81000000`10be0867
#828ae030 81000000`892f5867 81000000`980f7847
#828ae040 81000000`2aef6867 81000000`93dfe867
#828ae050 81000000`12f06867 81000000`5210f867
#828ae060 81000000`98211867 81000000`a4310847
#828ae070 81000000`b011f867 81000000`2b920867
#828ae080 81000000`95d23847 81000000`6da22847
#828ae090 81000000`0e728847 81000000`88827867
kd> !db 27fca000+4b0
#27fca4b0 68 00 65 00 6c 00 6c 00-6f 00 25 00 77 00 6f 00 h.e.l.l.o.%.w.o.
#27fca4c0 72 00 6c 00 64 00 21 00-0d 00 0a 00 00 00 00 00 r.l.d.!.........
#27fca4d0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#27fca4e0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#27fca4f0 00 00 00 00 00 00 00 00-08 00 79 fe de 01 00 00 ..........y.....
#27fca500 00 00 00 00 00 00 00 00-2a f8 73 48 50 71 00 10 ........*.sHPq..
#27fca510 30 48 00 fb de 01 00 00-00 42 00 fb de 01 00 00 0H.......B......
#27fca520 40 48 00 fb de 01 00 00-10 42 00 fb de 01 00 00 @H.......B......

  當然上述我們有不嚴謹的地方,我們並沒有判斷是否是大頁的情況,但對於一般變數來說,大頁的情況還是比較少的。
  學過我之前寫的教程都知道,作業系統可是不能這麼直接訪問實體記憶體的,它依舊通過線性地址進行訪問內容,必然在某個地址放著這幾張表。按照之前的學習路線,我們必然得需要逆向MmIsAddressValid,下面我們開始繼續:

; BOOLEAN __stdcall MmIsAddressValid(PVOID VirtualAddress)
                public MmIsAddressValid
MmIsAddressValid proc near              ; CODE XREF: KiMarkBugCheckRegions+B3↑p
                                        ; KiMarkBugCheckRegions+1E8↑p ...
                sub     rsp, 28h
                call    MmIsAddressValidEx
                add     rsp, 28h
                retn
MmIsAddressValid endp

  可以看出,這個啥也沒做,只是呼叫了MmIsAddressValidEx這個函式,我們點進去看看:

; BOOLEAN __fastcall MmIsAddressValidEx(PVOID VirtualAddress)
MmIsAddressValidEx proc near            ; CODE XREF: MiIncreaseUsedPtesCount+64↑p
                                        ; MiCommitExistingVad+65D↓p ...

PXE             = qword ptr -28h
PPE             = qword ptr -20h
PDE             = qword ptr -18h
PTE             = qword ptr -10h
VirtualAddress  = qword ptr  8

; FUNCTION CHUNK AT .text:00000001402234DC SIZE 00000056 BYTES

                mov     [rsp+VirtualAddress], rbx
                push    rdi
                sub     rsp, 20h
                mov     rax, rcx        ; VirtualAddress
                mov     r10, rcx
                sar     rax, 47         ; 實現的虛擬地址是48位的,如果是核心地址,則最後是-1,使用者地址是0
                inc     rax             ; 自增1
                cmp     rax, 1          ; 如果大於1,有問題
                ja      InvalidAddr
                mov     rdx, rcx        ; VirtualAddress
                mov     rdi, 0FFFFF68000000000h ; PML4 表基址
                shr     rdx, 9
                mov     rcx, 7FFFFFFFF8h
                and     rdx, rcx
                mov     rax, rdi
                add     rdx, rax
                mov     [rsp+28h+PXE], rdx ; 獲取 PXE
                shr     rdx, 9
                and     rdx, rcx
                mov     rax, rdi
                add     rdx, rax
                mov     [rsp+28h+PPE], rdx ; 獲取 PPE
                shr     rdx, 9
                and     rdx, rcx
                mov     rax, rdi
                add     rdx, rax
                mov     [rsp+28h+PDE], rdx ; 獲取 PDE
                shr     rdx, 9
                and     rdx, rcx
                mov     rax, rdi
                add     rdx, rax
                mov     r11, 0FFFFF6FB7DBED000h
                mov     [rsp+28h+PTE], rdx ; 獲取 PTE
                mov     edx, 4
                mov     rbx, 0FFFFF6FB7DBED7F8h

loc_1400AB248:                          ; CODE XREF: MmIsAddressValidEx+BF↓j
                mov     r9, [rsp+rdx*8-8] ; 初始值 rdx = 4
                dec     rdx
                mov     rcx, [r9]
                mov     rax, r11
                cmp     r9, rax
                jb      short loc_1400AB263
                mov     rax, rbx
                cmp     r9, rax
                jbe     short loc_1400AB27E

loc_1400AB263:                          ; CODE XREF: MmIsAddressValidEx+A9↑j
                                        ; MmIsAddressValidEx+D9↓j ...
                test    cl, 1
                jz      short InvalidAddr
                test    cl, cl
                js      short loc_1400AB2BD
                test    rdx, rdx
                jnz     short loc_1400AB248

loc_1400AB271:                          ; CODE XREF: MmIsAddressValidEx+113↓j
                                        ; MmIsAddressValidEx+122↓j
                mov     al, 1

loc_1400AB273:                          ; CODE XREF: MmIsAddressValidEx+126↓j
                mov     rbx, [rsp+28h+VirtualAddress]
                add     rsp, 20h
                pop     rdi
                retn
; ---------------------------------------------------------------------------

loc_1400AB27E:                          ; CODE XREF: MmIsAddressValidEx+B1↑j
                mov     eax, cs:MiFlags
                test    eax, 0C00000h
                jz      short loc_1400AB263
                mov     rax, gs:_KPCR.Prcb.CurrentThread
                mov     r8, [rax+_KTHREAD.___u25.ApcState.Process]
                cmp     [r8+_EPROCESS.Pcb.AddressPolicy], 1
                jz      short loc_1400AB263
                test    cl, 1
                jz      short InvalidAddr
                test    cl, 20h
                jz      loc_1402234DC
                test    cl, 42h
                jnz     short loc_1400AB263
                jmp     loc_1402234DC
; ---------------------------------------------------------------------------

loc_1400AB2BD:                          ; CODE XREF: MmIsAddressValidEx+BA↑j
                mov     rax, rdi
                cmp     r10, rax
                jb      short loc_1400AB271
                mov     rax, 0FFFFF6FFFFFFFFFFh
                cmp     r10, rax
                ja      short loc_1400AB271

InvalidAddr:                            ; CODE XREF: MmIsAddressValidEx+1B↑j
                                        ; MmIsAddressValidEx+B6↑j ...
                xor     al, al
                jmp     short loc_1400AB273
MmIsAddressValidEx endp

  但不幸的是,我根本無法弄懂這塊程式碼的所有細節,完全弄懂的部分均用註釋標註,主要是如下程式碼看起來十分奇怪:

loc_1400AB248:                          ; CODE XREF: MmIsAddressValidEx+BF↓j
                mov     r9, [rsp+rdx*8-8] ; 初始值 rdx = 4
                dec     rdx
                mov     rcx, [r9]
                mov     rax, r11
                cmp     r9, rax
                jb      short loc_1400AB263
                mov     rax, rbx
                cmp     r9, rax
                jbe     short loc_1400AB27E

  根據堆疊圖,它是在該函式直接提棧的區域性變數區,而我未發現任何初始化該區域資料的彙編程式碼。該程式碼裡面還有ShadowMapping這個東西,從名字上來看是影子對映相關的,但我同樣弄不出來相關細節。網路上有關x64的資料少之又少,我無法解決,所以有關頁的相關內容,只能在此告一段落。

  門在x86上是十分重要的東西,尤其是中斷門在Win系統扮演了十分重要的角色。鑑於之前在基礎篇保護模式介紹的內容基礎,這裡就只講呼叫門和中斷門。
  注意:我不太建議做和學WinXP一樣的核心實驗,因為PG有可能會被觸發導致藍屏,搞懂原理即可。

呼叫門

  在x64下,呼叫門的結構如下:

x64 番外篇——保護模式相關

  可以看出,呼叫門被擴充套件為128位,基本內容並沒有發生任何變化。我們看看GDT表裡面的內容:

kd> dq gdtr
fffff803`86c90fb0  00000000`00000000 00000000`00000000
fffff803`86c90fc0  00209b00`00000000 00409300`00000000
fffff803`86c90fd0  00cffb00`0000ffff 00cff300`0000ffff
fffff803`86c90fe0  0020fb00`00000000 00000000`00000000
fffff803`86c90ff0  86008bc8`f0000067 00000000`fffff803
fffff803`86c91000  0040f300`00003c00 00000000`00000000
fffff803`86c91010  00000000`00000000 00000000`00000000
fffff803`86c91020  00000000`00000000 00000000`00000000

  裡面的內容少了不少,相比於XP系統來說。有關呼叫門細節就介紹這些。

中斷門

  中斷門也被同樣的擴充套件為128位,其餘的功能並沒有發生任何變化。

x64 番外篇——保護模式相關

  然後我們再看一下IDT表:

kd> dq idtr
fffff803`86c8e000  81158e00`00101100 00000000`fffff803
fffff803`86c8e010  81158e04`00101180 00000000`fffff803
fffff803`86c8e020  81158e03`00101200 00000000`fffff803
fffff803`86c8e030  8115ee00`00101280 00000000`fffff803
fffff803`86c8e040  8115ee00`00101300 00000000`fffff803
fffff803`86c8e050  81158e00`00101380 00000000`fffff803
fffff803`86c8e060  81158e00`00101400 00000000`fffff803
fffff803`86c8e070  81158e00`00101480 00000000`fffff803

  我們可以用!idt獲取每一個索引對應的函式,如下為部分展示,後面有stack的說明該函式有自己獨佔的棧。

kd> !idt

Dumping IDT: fffff80386c8e000

00: fffff80381151100 nt!KiDivideErrorFaultShadow
01: ffff80381151180 nt!KiDebugTrapOrFaultShadow Stack = 0xFFFFF80386C929D0
02: fffff80381151200 nt!KiNmiInterruptShadow Stack = 0xFFFFF80386C927D0
03: fffff80381151280 nt!KiBreakpointTrapShadow
04: fffff80381151300 nt!KiOverflowTrapShadow
05: fffff80381151380 nt!KiBoundFaultShadow
06: fffff80381151400 nt!KiInvalidOpcodeFaultShadow
07: fffff80381151480 nt!KiNpxNotAvailableFaultShadow
08: fffff80381151500 nt!KiDoubleFaultAbortShadow Stack = 0xFFFFF80386C923D0
……

  有關中斷門相關內容,暫時就這麼多。

SMEP 與 SMAP

  x64CPU又增加了一些標識來增加安全性:SMEPSMAP。這兩個標識在CR4中,我們再拿來看看:

x64 番外篇——保護模式相關

  SMEP英文全稱是supervisor-mode execution prevention,看意思就是限制執行程式碼的。SMAP英文全稱為supervisor-mode access prevention,看意思就是限制訪問記憶體的。這些限制是限制誰的,限制核心的。在WinXP下,只要是核心就具有至高無上的許可權,我想讀哪塊記憶體就讀,想執行啥程式碼就執行。
  說這兩個標誌位的功能倒是挺容易的,為什麼要說這個東西呢?之前的實驗應用層程式通過呼叫門提權並執行程式碼就會出現問題,我們需要對這兩個標誌位進行清0操作,否則就會出錯。
  有一個特殊的彙編指令CLAC,它的作用是清除EFLAGS暫存器中的AC標誌位。這將禁用使用者模式資料訪問的任何對齊檢查。如果在CR4暫存器中設定了SMAP位,這個位就不會起作用;STAC可以恢復使用SMAP。有關這兩個位介紹就這麼多。

下一篇

  x64 番外篇—— WOW64

相關文章