• x64 番外篇——保护模式相关


    写在前面

      此系列是本人一个字一个字码出来的,包括示例和实验截图。由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我

    你如果是从中间插过来看的,请仔细阅读 羽夏看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结构如下所示:

      索引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位的段大体上就上述变化,但有一个段比较特别,那就是代码段,我们来看一下它的结构:

      代码段比32位的多了个位L,它的作用是来指示是32位的还是64位的,如果是0表示兼容模式(x86模式),为1则表示x64模式。
      里面还有一个D位,它的作用是指示默认大小的。如果L == 0,如果该位是0,默认的数据和地址大小为16位;反之为32位。如果L == 1D位必须为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,则它的结构如下:

      其中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位:

      这个位被置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

      x64CPU又增加了一些标识来增加安全性:SMEPSMAP。这两个标识在CR4中,我们再拿来看看:

      SMEP英文全称是supervisor-mode execution prevention,看意思就是限制执行代码的。SMAP英文全称为supervisor-mode access prevention,看意思就是限制访问内存的。这些限制是限制谁的,限制内核的。在WinXP下,只要是内核就具有至高无上的权限,我想读哪块内存就读,想执行啥代码就执行。
      说这两个标志位的功能倒是挺容易的,为什么要说这个东西呢?之前的实验应用层程序通过调用门提权并执行代码就会出现问题,我们需要对这两个标志位进行清0操作,否则就会出错。
      有一个特殊的汇编指令CLAC,它的作用是清除EFLAGS寄存器中的AC标志位。这将禁用用户模式数据访问的任何对齐检查。如果在CR4寄存器中设置了SMAP位,这个位就不会起作用;STAC可以恢复使用SMAP。有关这两个位介绍就这么多。

    下一篇

      x64 番外篇—— WOW64

  • 相关阅读:
    聚观早报 | 菜鸟上市;荣耀Magic Vs2定档
    Springboot 开发env
    MacOS - Cpolar 在 Mac 上如何使用?
    Docker的网络模式
    最长的可整合子数组的长度
    关于webWorker未解问题
    设计模式02 结构型模式
    HSRP热备份路由器协议的解析和配置
    【数据结构】堆排序和Top-k问题
    王杰C++day6
  • 原文地址:https://www.cnblogs.com/wingsummer/p/16092862.html