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 DpcQueue:
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 nt!_KPCR +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 .text:004085B6 var_8 = dword ptr -8 .text:004085B6 var_4 = dword ptr -4 .text:004085B6 .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 .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 .text:0040867B loc_40867B: ; CODE XREF: _KiTrap02+ACโj .text:0040867B ; _KiTrap02+B5โj ... .text:0040867B jmp short loc_40867B .text:0040867D ; --------------------------------------------------------------------------- .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 .text:0040969D var_4 = dword ptr -4 .text:0040969D .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 .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 .text:00439980 PS = dword ptr -8 .text:00439980 HPDE = dword ptr -4 .text:00439980 VirtualAddress = dword ptr 8 .text:00439980 .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 .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 .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 .text:00439A15 loc_439A15: ; CODE XREF: MmIsAddressValid(x)+41โj .text:00439A15 ; MmIsAddressValid(x)+72โj ... .text:00439A15 mov al, 1 .text:00439A17 .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() { _asm { push 0x30; pop fs; pushad; pushfd; 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]; end: 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; popfd; popad; retf; } } 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!!!"); VirtualFree(page1,0,MEM_FREE); VirtualFree(page2,0,MEM_FREE); system("pause"); return 0; } *(int*)page1 = 0x12345; *(int*)page2 = 0x67890; puts("Clean cache?"); scanf("%d",&isinv); const char buffer[6]={0,0,0,0,0x9B,0}; _asm { 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); VirtualFree(page1,0,MEM_FREE); VirtualFree(page2,0,MEM_FREE); system("pause"); 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?
epilogue
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.
Next
Yuxia looks at Win system kernel - Driver