Protection mode chapter - summary and improvement

Posted by Karlos94 on Sun, 31 Oct 2021 02:21:12 +0100

Write in front

  this series is written word by word, including examples and experimental screenshots. Due to the complexity of the system kernel, there may be errors or incompleteness. If there are errors, criticism and correction are welcome. This tutorial will be updated for a long time. If you have any good suggestions, welcome feedback. Code words are not easy. If this article is helpful to you, if you have spare money, you can reward and support my creation. If you want to reprint, please attach my reprint information to the back of the article and state my personal information and my blog address, but you must notify me in advance.

If you look from the middle, please read it carefully Yu Xia's view of Win system kernel -- a brief introduction , easy to learn this tutorial.

  before reading this tutorial, ask a few questions. Have you prepared the basic knowledge? Did you learn in the last tutorial? Have you done the exercises of the last class? If not, don't continue.


๐Ÿ”’ Gorgeous dividing line ๐Ÿ”’


Exercises and references

The answers are for reference and may not be consistent with my answers, but they must be passed successfully.

   before looking at the reference answer, we need to know what fs stores in the 0 ring and what the first address is. First, let's take a look at the first address of fs:

   as can be seen from the above figure, the first address of ring 0 is 0xFFDFF000, so what is stored in this address? We use it! Take a look at the pcr command:

KPCR for Processor 0 at ffdff000:
    Major 1 Minor 1
    NtTib.ExceptionList: 8054a4b0
        NtTib.StackBase: 8054acf0
       NtTib.StackLimit: 80547f00
     NtTib.SubSystemTib: 00000000
          NtTib.Version: 00000000
      NtTib.UserPointer: 00000000
          NtTib.SelfTib: 00000000

                SelfPcr: ffdff000
                   Prcb: ffdff120
                   Irql: 0000001c
                    IRR: 00000000
                    IDR: ffff20f8
          InterruptMode: 00000000
                    IDT: 8003f400
                    GDT: 8003f000
                    TSS: 80042000

          CurrentThread: 80553740
             NextThread: 00000000
             IdleThread: 80553740


  it can be seen that this address stores the KPCR structure, so what is KPCR? It is a structure. Because Windows needs to support multiple CPUs, the windows kernel defines a set of data structure with the processor control area, namely KPCR, as the hub, so that each CPU has a KPCR. There is a domain PRCB structure in the KPCR structure, which extends KPCR. These two structures are used to store global information related to thread switching. The specific details will be explained in the process thread of this series of tutorials.

  in order to view the details of the structure more conveniently, let dt:

dt _KPCR ffdff000
   +0x000 NtTib            : _NT_TIB
   +0x01c SelfPcr          : 0xffdff000 _KPCR
   +0x020 Prcb             : 0xffdff120 _KPRCB
   +0x024 Irql             : 0x1c ''
   +0x028 IRR              : 0
   +0x02c IrrActive        : 0
   +0x030 IDR              : 0xffff20f8
   +0x034 KdVersionBlock   : 0x80546ab8 Void
   +0x038 IDT              : 0x8003f400 _KIDTENTRY
   +0x03c GDT              : 0x8003f000 _KGDTENTRY
   +0x040 TSS              : 0x80042000 _KTSS
   +0x044 MajorVersion     : 1
   +0x046 MinorVersion     : 1
   +0x048 SetMember        : 1
   +0x04c StallScaleFactor : 0x64
   +0x050 DebugActive      : 0 ''
   +0x051 Number           : 0 ''
   +0x052 Spare0           : 0 ''
   +0x053 SecondLevelCacheAssociativity : 0 ''
   +0x054 VdmAlert         : 0
   +0x058 KernelReserved   : [14] 0
   +0x090 SecondLevelCacheSize : 0
   +0x094 HalReserved      : [16] 0
   +0x0d4 InterruptMode    : 0
   +0x0d8 Spare1           : 0 ''
   +0x0dc KernelReserved2  : [17] 0
   +0x120 PrcbData         : _KPRCB

    with these foreshadowing, you can continue to look at the answers. Note that the requirement is to analyze the execution process, not to understand every detail in reverse, and generally know how to deal with it:

one ๏ธโƒฃ Analyze the execution process of interrupt 0x2 in IDT table.

๐Ÿ”’ Click to see the answer ๐Ÿ”’
.text:004085B6 _KiTrap02       proc near               ; DATA XREF: KiSystemStartup(x)+143โ†“o
.text:004085B6                                         ; INIT:005DD510โ†“o
.text:004085B6 var_8           = dword ptr -8
.text:004085B6 var_4           = dword ptr -4
.text:004085B6                 cli                     ; Shielding can shield interrupts. Don't disturb me
.text:004085B7                 push    dword ptr ds:0FFDFF040h ; fs:[40h] TSS
.text:004085BD                 mov     eax, ds:0FFDFF03Ch ; fs:[3ch] GDT
.text:004085C2                 mov     ch, [eax+5Fh]   ; eax = GDT
.text:004085C5                 mov     cl, [eax+5Ch]
.text:004085C8                 shl     ecx, 10h
.text:004085CB                 mov     cx, [eax+5Ah]   ; utilize ecx Get 8003 f058 The first address of the segment descriptor, with a value of 8054 AF68 ๏ผŒIt's a TSS Segment descriptor
.text:004085CF                 mov     ds:0FFDFF040h, ecx ; fs:[40h]๏ผŒswitch TSS
.text:004085D5                 pushf
.text:004085D6                 and     [esp+8+var_8], 11111111111111111011111111111111b
.text:004085DD                 popf                    ; take NT Position 0
.text:004085DE                 mov     ecx, ds:0FFDFF03Ch ; fs:[3ch]๏ผŒGDT
.text:004085E4                 lea     eax, [ecx+58h]  ; Get current TSS
.text:004085E7                 mov     byte ptr [eax+5], 89h ; Modify current TSS Properties of
.text:004085EB                 mov     eax, [esp+4+var_4]
.text:004085EE                 push    0
.text:004085F0                 push    0
.text:004085F2                 push    0
.text:004085F4                 push    0
.text:004085F6                 push    dword ptr [eax+50h]
.text:004085F9                 push    dword ptr [eax+38h]
.text:004085FC                 push    dword ptr [eax+24h]
.text:004085FF                 push    dword ptr [eax+4Ch]
.text:00408602                 push    dword ptr [eax+20h]
.text:00408605                 push    0
.text:00408607                 push    dword ptr [eax+3Ch]
.text:0040860A                 push    dword ptr [eax+34h]
.text:0040860D                 push    dword ptr [eax+40h]
.text:00408610                 push    dword ptr [eax+44h]
.text:00408613                 push    dword ptr [eax+58h]
.text:00408616                 push    dword ptr ds:0FFDFF000h
.text:0040861C                 push    0FFFFFFFFh
.text:0040861E                 push    dword ptr [eax+28h]
.text:00408621                 push    dword ptr [eax+2Ch]
.text:00408624                 push    dword ptr [eax+30h]
.text:00408627                 push    dword ptr [eax+54h]
.text:0040862A                 push    dword ptr [eax+48h]
.text:0040862D                 push    dword ptr [eax+5Ch]
.text:00408630                 push    0
.text:00408632                 push    0
.text:00408634                 push    0
.text:00408636                 push    0
.text:00408638                 push    0
.text:0040863A                 push    0
.text:0040863C                 push    0
.text:0040863E                 push    0
.text:00408640                 push    0
.text:00408642                 push    0
.text:00408644                 push    dword ptr [eax+20h]
.text:00408647                 push    dword ptr [eax+3Ch]
.text:0040864A                 mov     ebp, esp
.text:0040864C                 cmp     ds:dword_47A2DC, 0
.text:00408653                 jz      short loc_40867D
.text:00408655                 jmp     short loc_408659
.text:00408657 ; ---------------------------------------------------------------------------
.text:00408657                 jmp     short loc_40867D
.text:00408659 ; ---------------------------------------------------------------------------
.text:00408659 loc_408659:                             ; CODE XREF: _KiTrap02+9Fโ†‘j
.text:00408659                 cmp     ds:dword_47A2DC, 8
.text:00408660                 jb      short loc_40867D
.text:00408662                 jnz     short loc_40867B
.text:00408664                 cmp     ds:_KdDebuggerNotPresent, 0
.text:0040866B                 jnz     short loc_40867B
.text:0040866D                 cmp     ds:_KdDebuggerEnabled, 0
.text:00408674                 jz      short loc_40867B
.text:00408676                 call    _KeEnterKernelDebugger@0 ; KeEnterKernelDebugger()
.text:0040867B loc_40867B:                             ; CODE XREF: _KiTrap02+ACโ†‘j
.text:0040867B                                         ; _KiTrap02+B5โ†‘j ...
.text:0040867B                 jmp     short loc_40867B
.text:0040867D ; ---------------------------------------------------------------------------
.text:0040867D loc_40867D:                             ; CODE XREF: _KiTrap02+9Dโ†‘j
.text:0040867D                                         ; _KiTrap02+A1โ†‘j ...
.text:0040867D                 inc     ds:dword_47A2DC
.text:00408683                 push    0
.text:00408685                 call    ds:__imp__HalHandleNMI@4 ; HalHandleNMI(x)
.text:0040868B                 dec     ds:dword_47A2DC
.text:00408691                 jnz     short loc_4086C6
.text:00408693                 mov     eax, ds:0FFDFF040h ; fs:[40h]
.text:00408698                 cmp     word ptr [eax], 58h ; 'X'
.text:0040869C                 jz      short loc_4086C6
.text:0040869E                 add     esp, 8Ch
.text:004086A4                 pop     dword ptr ds:0FFDFF040h ; fs:[40h]
.text:004086AA                 mov     ecx, ds:0FFDFF03Ch ; fs:[3ch]
.text:004086B0                 lea     eax, [ecx+28h]
.text:004086B3                 mov     byte ptr [eax+5], 8Bh ; modify TSS Attribute is Busy
.text:004086B7                 pushf
.text:004086B8                 or      [esp+4+var_4], 4000h
.text:004086BF                 popf                    ; take NT Position 1
.text:004086C0                 iret                    ; TSS return

two ๏ธโƒฃ Analyze the execution process of interrupt 0x8 in IDT table.

๐Ÿ”’ Click to see the answer ๐Ÿ”’
.text:0040969D _KiTrap08       proc near               ; DATA XREF: KiSystemStartup(x)+CBโ†“o
.text:0040969D                                         ; INIT:005DD540โ†“o
.text:0040969D var_4           = dword ptr -4
.text:0040969D                 cli                     ; Maskable interrupt
.text:0040969E                 mov     ecx, ds:0FFDFF03Ch ; take GDT First address
.text:004096A4                 lea     eax, [ecx+50h]  ; take TSS Segment descriptor address, eax = 8003f050
.text:004096A7                 mov     byte ptr [eax+5], 89h ; set up TSS attribute
.text:004096AB                 pushf
.text:004096AC                 and     [esp+4+var_4], 0FFFFBFFFh
.text:004096B3                 popf                    ; empty NT position
.text:004096B4                 mov     eax, ds:0FFDFF03Ch ; take GDT First address
.text:004096B9                 mov     ch, [eax+57h]
.text:004096BC                 mov     cl, [eax+54h]
.text:004096BF                 shl     ecx, 10h
.text:004096C2                 mov     cx, [eax+52h]   ; take TSS The first address pointed to by the segment descriptor
.text:004096C6                 mov     eax, ds:0FFDFF040h ; take TSS Backup to eax
.text:004096CB                 mov     ds:0FFDFF040h, ecx ; switch TSS
.text:004096D1 loc_4096D1:                             ; CODE XREF: .text:004096E1โ†“j
.text:004096D1                 push    0
.text:004096D3                 push    0
.text:004096D5                 push    0
.text:004096D7                 push    eax             ; Wrong TSS
.text:004096D8                 push    8
.text:004096DA                 push    7Fh
.text:004096DC                 call    _KeBugCheck2@24 ; KeBugCheck2(x,x,x,x,x,x)
.text:004096DC _KiTrap08       endp

Section / page / door

  when you practice and study step by step and see this article, congratulations. Your foundation is almost the same. You can study the details of other protection modes independently. It's really not easy to learn the protection mode.
  in this chapter, we explain what is a paragraph and what is a page. You should have a more in-depth understanding of the suffering of many blue screens. Section is the first line of defense, mainly the inspection of authority. Page is the last line of defense and further protection of memory. If you want to read the contents of an address in memory in protected mode, you must pass the double test of segment and page to succeed. For example, why the 0 address of fs can be accessed, and the address greater than 0xFFF cannot be accessed, is due to segment restrictions. Why code segments can only be read but not written is also the limitation of code segments. In addition, we talked about the 0 address before. Even if you have the permission of the 0 ring, you can't access it because you don't hang the correct physical page. The high 2G memory cannot be accessed with low permissions due to page restrictions.
  when we started learning pages, we first started with 10-10-12 pages, and then 2-9-9-12 pages. Their structures are basically similar, but there is an additional layer of nesting, but the latter supports larger and more physical pages. It can be said that the development of paging depends on demand.
The status of the door in the protection mode is also not low. There are also many kinds of gates: call gate, interrupt gate, trap gate, task gate and so on. The main function of the door is to raise rights. In the operating system, all code implementations are implemented in the kernel, including the so-called API and so on.
  it seems that the operating system is not omniscient. It needs to have a good relationship with the CPU to achieve such a complex system. Of course, other hardware is also indispensable.

Deep PAE paging

  when talking about 10-10-12 paging, we explained the directory table base address and page table base address, and knew their uses. However, I did not introduce it in 2-9-9-12 pages, but it can be done by means of reverse analysis. Let's talk about how the operating system hangs physical pages in 2-9-9-12 paging mode.
  let's take a look at WinDbg first. I open a Notepad and view its PDPTT in the usual way, as shown in the figure below! dq 129001a0, look at the four members first, because it has only four.

    then let's take a look at the last item and see its members, as shown in the figure below! dq 3b303000. As like as two peas, you will be surprised to find that the first four items are exactly the same. We draw a graph based on our findings:

   we can say that the first four members of the PDT of the fourth member of the PDPTT are all members of the PDPTT, and then we use IDA to see its role through reverse:

.text:00439980 ; BOOLEAN __stdcall MmIsAddressValid(PVOID VirtualAddress)
.text:00439980                 public _MmIsAddressValid@4
.text:00439980 _MmIsAddressValid@4 proc near           ; CODE XREF: IopIsAddressRangeValid(x,x)+2Fโ†‘p
.text:00439980                                         ; IopGetMaxValidMemorySize(x,x)+29โ†‘p ...
.text:00439980 PS              = dword ptr -8
.text:00439980 HPDE            = dword ptr -4
.text:00439980 VirtualAddress  = dword ptr  8
.text:00439980                 mov     edi, edi
.text:00439982                 push    ebp
.text:00439983                 mov     ebp, esp
.text:00439985                 push    ecx
.text:00439986                 push    ecx
.text:00439987                 mov     ecx, [ebp+VirtualAddress] ; ecx = VirtualAddress
.text:0043998A                 push    esi
.text:0043998B                 mov     eax, ecx
.text:0043998D                 shr     eax, 18
.text:00439990                 mov     esi, 11111111111000b
.text:00439995                 and     eax, esi        ; esi = 11111111111000b
.text:00439997                 sub     eax, -0C0600000h ; Directory table base address
.text:0043999C                 mov     edx, [eax]      ; take PDE Lower four bytes of
.text:0043999E                 mov     eax, [eax+4]    ; take PDE High four bytes of
.text:004399A1                 mov     [ebp+HPDE], eax ; HPDE: PDE High four bytes of
.text:004399A4                 mov     eax, edx        ; eax = LPDE(PDE Lower four bytes of)
.text:004399A6                 push    edi
.text:004399A7                 and     eax, 1          ; obtain PDE of P position
.text:004399AA                 xor     edi, edi
.text:004399AC                 or      eax, edi        ; judge P Is it 1
.text:004399AE                 jz      short loc_439A11 ; Skip if invalid
.text:004399B0                 mov     edi, 10000000b  ; edi = 10000000b
.text:004399B5                 and     edx, edi        ; judge PS Whether the bit is 1, that is, whether it is a large page
.text:004399B7                 push    0
.text:004399B9                 mov     [ebp+PS], edx
.text:004399BC                 pop     eax             ; eax = 0
.text:004399BD                 jz      short loc_4399C3
.text:004399BF                 test    eax, eax
.text:004399C1                 jz      short loc_439A15 ; jmp
.text:004399C3 loc_4399C3:                             ; CODE XREF: MmIsAddressValid(x)+3Dโ†‘j
.text:004399C3                 shr     ecx, 9
.text:004399C6                 and     ecx, 7FFFF8h
.text:004399CC                 mov     eax, [ecx+0C0000004h] ; take PTE The upper four bytes of, 0 xC0000000+0x4
.text:004399D2                 sub     ecx, -0C0000000h ; ecx += 0xC0000000
.text:004399D8                 mov     edx, [ecx]      ; take PTE Lower four bytes of
.text:004399DA                 mov     [ebp+HPDE], eax
.text:004399DD                 push    ebx
.text:004399DE                 mov     eax, edx        ; eax = LPTE
.text:004399E0                 xor     ebx, ebx
.text:004399E2                 and     eax, 1
.text:004399E5                 or      eax, ebx
.text:004399E7                 pop     ebx
.text:004399E8                 jz      short loc_439A11 ; If PTE of P Bit invalid skip
.text:004399EA                 and     edx, edi        ; edi = 10000000b
.text:004399EC                 push    0
.text:004399EE                 mov     [ebp+PS], edx
.text:004399F1                 pop     eax             ; eax = 0
.text:004399F2                 jz      short loc_439A15
.text:004399F4                 test    eax, eax
.text:004399F6                 jnz     short loc_439A15 ; jmp
.text:004399F8                 and     ecx, esi
.text:004399FA                 mov     ecx, [ecx+0C0600000h] ; PDE Lower four bytes of
.text:00439A00                 mov     eax, 10000001b
.text:00439A05                 and     ecx, eax
.text:00439A07                 xor     edx, edx        ; Clear flag bit
.text:00439A09                 cmp     ecx, eax
.text:00439A0B                 jnz     short loc_439A15
.text:00439A0D                 test    edx, edx        ; jmp
.text:00439A0F                 jnz     short loc_439A15
.text:00439A11 loc_439A11:                             ; CODE XREF: MmIsAddressValid(x)+2Eโ†‘j
.text:00439A11                                         ; MmIsAddressValid(x)+68โ†‘j
.text:00439A11                 xor     al, al
.text:00439A13                 jmp     short loc_439A17
.text:00439A15 ; ---------------------------------------------------------------------------
.text:00439A15 loc_439A15:                             ; CODE XREF: MmIsAddressValid(x)+41โ†‘j
.text:00439A15                                         ; MmIsAddressValid(x)+72โ†‘j ...
.text:00439A15                 mov     al, 1
.text:00439A17 loc_439A17:                             ; CODE XREF: MmIsAddressValid(x)+93โ†‘j
.text:00439A17                 pop     edi
.text:00439A18                 pop     esi
.text:00439A19                 leave
.text:00439A1A                 retn    4
.text:00439A1A _MmIsAddressValid@4 endp

  since we have the reverse experience of 10-10-12 paging, it is not difficult to reverse them. Through the reverse result: virtualaddress > > 18 + c0600000 is the PDE pointed to. That is, the resulting index is 2 ^ 14, with a maximum value of 4000H. 0C0600000 is the first address of the first PDT table, C0601000 is the first address of the second PDT table, C0602000 is the first address of the third PDT table, and C0603000 is the first address of the fourth PDT table.
  maybe you don't know the function of PDPTE. Let me give you an example to understand why I have a linear address that is the first PDT table: when we were looking for PDE, we just went to the next 21 bits by 8 bytes.

  PDPTI can be seen from the above figure × 1000H + PDI × 8, and one page is 1000h, indicating that they are contiguous. This is the role of PDPTE in finding physical addresses.
  now that you know its structure, write the code yourself. There are corresponding topics in practice and thinking.

Practice and thinking

one ๏ธโƒฃ In the 2-9-9-12 paging mode, use code to hang the physical page for the 0 address, not Windbg, and verify the existence of TLB.

๐Ÿ”’ Click to see the answer ๐Ÿ”’

  this question is similar to the previous 10-10-12 page question, the answer is similar, and the effect is the same.

   first construct a call gate: eq 8003f098 0040EC0000081250. Note that the address of the call gate is consistent with that of the raw function.

  the criteria for judging the success of the experiment are the same as the previous similar topics, so I won't repeat them.

โ€ƒ ๐Ÿ”’ Click to view the code ๐Ÿ”’
#include "stdafx.h"
#include <iostream>
#include <windows.h>

int isinv=0;
int num1=0;
int num2=0;

void __declspec(naked) callgate()
        push 0x30;
        pop fs;


        mov edi,0xC0000000;

        mov eax,0x10000;
        shr eax,9;
        add eax,edi;
        mov edx,dword ptr ds:[eax];
        mov dword ptr ds:[edi],edx;
        add eax,4;
        mov edx,dword ptr ds:[eax];
        mov dword ptr ds:[edi+4],edx;

        mov edx,dword ptr ds:[0];
        mov [num1],edx;

        mov eax,isinv ;
        test eax,eax;
        jz end;

        invlpg dword ptr ds:[0];


        mov eax,0x20000;
        shr eax,9;
        add eax,edi;
        mov edx,dword ptr ds:[eax];
        mov dword ptr ds:[edi],edx;
        add eax,4;
        mov edx,dword ptr ds:[eax];
        mov dword ptr ds:[edi+4],edx;

        mov edx,dword ptr ds:[0];
        mov [num2],edx;


int main(int argc, char* argv[])

    LPVOID page1 = VirtualAlloc((LPVOID)0x10000,0x1000,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
    LPVOID page2 = VirtualAlloc((LPVOID)0x20000,0x1000,MEM_COMMIT,PAGE_EXECUTE_READWRITE);

    if (!page1||!page2)
        puts("Failed to allocate memory!!!");
        return 0;

    *(int*)page1 = 0x12345;
    *(int*)page2 = 0x67890;

    puts("Clean cache?");

    const char buffer[6]={0,0,0,0,0x9B,0};

        push fs;
        call fword ptr [buffer];
        pop fs;

    printf("Value of the first hanging page:%x\n Value after page change:%x\n",num1,num2);


    return 0;

two ๏ธโƒฃ In the XP virtual machine of VirtualBox, I enter the operating system with 2-9-9-12 paging. However, it is found through PCHunter and WinDbg that the paging mode is 10-10-12 paging. Why?

๐Ÿ”’ Click to see the answer ๐Ÿ”’
Is it enabled when configuring the virtual machine PAE/NX Is this option?


  this concludes the chapter on protection mode. Review what you have learned before. Next, we will move on to the next chapter. When we learned the protection mode, we were toddlers, and then we could trot. In the following tutorials, I will speed up the text according to my spare time.


  Yuxia looks at Win system kernel - Driver