寫在前面
此係列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統核心的複雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閒錢,可以打賞支援我的創作。如想轉載,請把我的轉載資訊附在文章後面,並宣告我的個人資訊和本人部落格地址即可,但必須事先通知我。
你如果是從中間插過來看的,請仔細閱讀 羽夏看Win系統核心——簡述 ,方便學習本教程。
看此教程之前,問幾個問題,基礎知識儲備好了嗎?保護模式篇學會了嗎?練習做完了嗎?沒有的話就不要繼續了。
? 華麗的分割線 ?
概述
在64位下,有兩種CPU
模式,一種是IA-32e
模式,是IA-32
模式的擴充套件,另一個是Legacy
模式。IA-32e
模式是指核心64位,使用者64或32位均可,它強制平坦段,不支援任務切換;而Legacy
模式指核心32位,使用者32位支援非平坦段、任務切換、虛擬8086、真實模式等。
在IA-32e
模式下,程式碼段和資料段仍使用64位描述符,強制平坦(FS
,GS
除外);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
結構如下所示:
索引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
。但也有例外,比如GS
和FS
,因為它們指向一些結構體,這些結構體的首地址肯定不是0。
64位的段和32位的段大體上就上述變化,但有一個段比較特別,那就是程式碼段,我們來看一下它的結構:
程式碼段比32位的多了個位L
,它的作用是來指示是32位的還是64位的,如果是0表示相容模式(x86
模式),為1則表示x64
模式。
裡面還有一個D
位,它的作用是指示預設大小的。如果L == 0
,如果該位是0,預設的資料和地址大小為16位;反之為32位。如果L == 1
,D
位必須為0,否則會觸發通用保護異常。
最後要介紹的段就是TSS
段,被擴充套件為128位,用於滿足定址要求,如下結構所示:
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,則它的結構如下:
如果CR4.PCIDE
為1,則它的結構如下:
其中M
是MAXPHYADDR
的縮寫,最大是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位:
這個位被置1了,說明是第二種形式。有關這部分細節,對於我們不寫作業系統的人來說似乎沒啥用處,就不追究了。
下面我們來看看4級分頁線性地址的結構,如下是線性地址如何翻譯成實體地址的:
按照之前的說法套路,給一個非正式的名字9-9-9-9-12
分頁。又了之前的底子,我就不贅述內部的詳細結構,直接用一張圖進行總結(在Intel
白皮書的第2832頁):
細心的你可能會發現protection key
這個東西,這是啥呢?這個就是4級分頁的一個新的機制,提供更完全的保護,當CR4.PKE == 1
時有效,不幸的是,在我的虛擬機器的作業系統並不使用該位,所以無效,如果對這塊技術感興趣請仔細閱讀Intel
白皮書的第2835頁。
當然上述分頁還有大頁的情形,如下所示:
下面我們就用x86
下的實驗方式來學習分頁,首先你得有新版的CheatEngine
,下面開始:
如上圖所示,我開啟了一個CheatEngine
和一個Notepad
,並且在記事本里面寫上hello,world!
。然後我們開始老套路搜尋記憶體:
把,
改為%
,定位到指定的地址,如下圖所示:
我們先用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
下,呼叫門的結構如下:
可以看出,呼叫門被擴充套件為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位,其餘的功能並沒有發生任何變化。
然後我們再看一下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
x64
的CPU
又增加了一些標識來增加安全性:SMEP
與SMAP
。這兩個標識在CR4
中,我們再拿來看看:
SMEP
英文全稱是supervisor-mode execution prevention
,看意思就是限制執行程式碼的。SMAP
英文全稱為supervisor-mode access prevention
,看意思就是限制訪問記憶體的。這些限制是限制誰的,限制核心的。在WinXP
下,只要是核心就具有至高無上的許可權,我想讀哪塊記憶體就讀,想執行啥程式碼就執行。
說這兩個標誌位的功能倒是挺容易的,為什麼要說這個東西呢?之前的實驗應用層程式通過呼叫門提權並執行程式碼就會出現問題,我們需要對這兩個標誌位進行清0操作,否則就會出錯。
有一個特殊的彙編指令CLAC
,它的作用是清除EFLAGS
暫存器中的AC
標誌位。這將禁用使用者模式資料訪問的任何對齊檢查。如果在CR4
暫存器中設定了SMAP
位,這個位就不會起作用;STAC
可以恢復使用SMAP
。有關這兩個位介紹就這麼多。
下一篇
x64 番外篇—— WOW64