Chapter 4 Exploring the Memory Management Mechanism of WINDOWS 2000
Translation: kendiv (fcczj@263.net)
Update:
Sunday, February 17, 2005
Disclaimer: Please indicate the source and guarantee the integrity of the article, and all rights to the translation.
IOCTL function spy_io_interrupt
SPY_IO_INTERRUP is similar to SPY_IO_SEGEMT, but the function only affects the interrupt descriptor stored in the system interrupt descriptor table (IDT) without involve the LDT or GDT descriptor. IDT can accommodate up to 256 descriptors, which can be used to describe task doors, interrupt gate or trap gates (see Intel 1999C, PP. 5-11FF). By the way, the interruption and traps are inherently very similar, and the two only have a small difference: after entering an interrupt processing routine, it will always block other interrupts; and enter the trap processing routine will not modify the interrupt flag. The caller of SPY_IO_ITERRUPT provides a interrupt number between 0 and 255, which will be in the input buffer, and a spy_interrupt structure is stored as an output data to the output buffer, and if successful returns, the structure will contain The attribute of the corresponding interrupt processing routine. The help function called by Dispatcher is just a simple outsourcing function. It actually calls the spyinterrupt () function and copy the data returned to the output buffer. Listing 4-18 gives these two functions and the SPY_ITERRUPT structure they operate. Someth, the spyinterrupt () function will populate the following items:
l Selector is used to specify a selector of a task status segment (TSS) or code segment. The code segment selector is used to determine the segment of the interrupt or trap processing routine.
l Gate uses a 64-bit task door, interrupt door or trap door descriptor, determines its address by Selector.
l Segment contains the properties of the segment, which is given by the previous Gate.
l Poffset Specifies the offset of the inlet address of the interrupt or trap processing routine. The base site here refers to the starting address of the code segment where the interrupt or trap process routine is located. Because the task door does not include an offset, if the input selector points to a TSS, ignore the member.
l FOK a logo variable to indicate whether the data in the spy_interrupt structure is valid.
Typically, TSS is used to ensure that an error can be handled by an effective task. This is a special system segment type (SYSTEM Segment Type) that saves 104 bytes of process status information, which is used to recover to task, as shown in Table 4-3. When interrupted with task occurs, the CPU always enforces this task and saves all CPU registers to TSS. Windows 2000 saves task door at interrupt location 0x02 (non-mask interrupt [NMI], 0x08 [Double Fault], and 0x12 [Stack Segment Fault]). The remaining position points to the interrupt processing routine. Unused interrupts are processed by a dummy routine - kiunexpectedInterruptnnn (), where NNN is a decimal number. These dummy routines are finally collected to the internal function KienduneXpectedRange (), where these routines will enter KiuNexpectedInterruPttail ().
Typedef struct _sspy_interrupt {
X86_Selector Selector;
X86_gate Gate;
SPY_SEGMENT segment;
PVOID POFFSET;
BOOL FOK;
}
Spy_interrupt, * pspy_interrupt, ** PPSPY_INTERRUPT;
#define spy_interrupt_ sizeof (Spy_Interrupt)
/ / -------------------------------------------------------------------------------------------- -----------------
NTSTATUS SPYOUTINTERRUPT (DWORD DINTERRUPT,
Pvoid Poutput,
DWORD DOUTPUT,
PDWORD PDINFO)
{
SPY_Interrupt Si;
SpyINTERRUPT (DINTERRUPT, & SI);
Return Spyoutputbinary (& Si, SPY_INTERRUPT_,
Poutput, doutput, pdinfo;
}
/ / -------------------------------------------------------------------------------------------- -----------------
Bool SpyInterrupt (DWORD DINTERRUPT,
PSPY_ITERRUPT PINTERRUPT)
{
BOOL fok = false;
IF (PinterRupt! = NULL)
{
IF (DinterRupt <= x86_selector_limit)
{
Fok = true;
IF (! spyselector (x86_segment_other,
DINTERRUPT << x86_selector_shift,
& pinterrupt-> selector))
{
Fok = false;
}
IF (! SpyIdtgate (& Pinterrupt-> Selector,
& pinterrupt-> Gate)))
{
Fok = false;
}
IF (! spysegment (x86_segment_other,
Pinterrupt-> Gate.Selector,
& pinterrupt-> segment)))))
{
Fok = false;
}
Pinterrupt-> Poffset = SpygateOffset (& PinterRupt-> Gate);
}
Else
{
RTLZERMEMORY (PinterRupt, Spy_Interrupt_);
}
Pinterrupt-> fok = fok;
}
Return fok;
}
/ / -------------------------------------------------------------------------------------------- -----------------
PVOID SpygateOffset (PX86_GATE PGATE)
{
Return (PVOID) (pgate-> offset1 | (pgate-> offset2 << 16));
}
Listing 4-18. Query Interrupt Properties
Table 4-3. CPU status in the task status section (TSS)
Offset
Bit number
Id
Describe
0x00 16
Link of the previous task 0x04 32 ESP0 RING0 stack pointer register 0x08 16 SS0 RING0 stack segment register 0x0c 32 ESP1 RING1 stack pointer register 0x10 16 SS1 RING1 stack segment register 0x14 32 ESP2 RING2 stack pointer register 0x18 16 SS2 RING2 stack segment register 0x1c 32 CR3 page directory base address register (PDBR) 0x20 32 EIP instruction pointer register 0x24 32 EFLAGS processor flag register 0x28 32 ECX general register 0x2c 32 ECX universal register 0x30 32 EDX General Register 0x34 32 EBX Universal Register 0x38 32 ESP Stack Pointer Register 0x3C 32 EBP Base Searner Pointer Register 0x40 32 ESI Source Source Source Index Register 0x48 16 ES Extension Segment Register 0x4c 16 CS Code Segment Register 0x50 16 SS Stack Segment Register 0x54 16 DS Data Segment register 0x58 16 FS additional data segment register # 1 0x5c 16 GS additional data segment register # 2 0x60 16 LDT local descriptor segment selector 0x64 1 1 debug trap flag 0x66 16i / o map base address 0x68 -
CPU status information ends
SpySEGMENT () called SPYINTERRUPT (), the spyselector () function is already given in Listing 4-5 and List 4-16. SpygateOffset () is located at the end of the list 4-18, its work and spydescriptorbase (), SpyDescriptorLimit () similar to the OFFSET1 and OFFSET2 bit fields from the x86_gate structure, and organize them to form a 32-bit address. SpyIDTGAET () is defined in List 4-19. It is very similar to SpyDescriptor (). The assembly instruction SIDT stores a 48-bit value that is the content of the CPU IDT register, which is constructed of a 16-bit table size limit value and IDT 32-bit linear site. The remaining code in Listing 4-19 compares the size limit value of the descriptor index of the selector, and if OK, the corresponding interrupt descriptor will be copied to the X86_gate structure provided by the caller. Otherwise, all members of the door structure will be set to 0.
Bool spiidtgate (px86_selector pselector,
PX86_GATE PGATE
{
X86_Table IDT;
PX86_GATE PGATES = NULL;
BOOL fok = false;
IF (pgate! = NULL)
{
IF (pselector! = null)
{
__ASM
{
SIDT IDT.WLIMIT
}
IF ((pselector-> wvalue & x86_selector_index)
<= idt.wlimit)
{
pgates = idt.pgates;
}
}
IF (pgates! = null)
{
RTLCopyMemory (pgate,
pgates pselector-> index,
X86_gate_);
Fok = true;
}
Else
{
RTLzeromeMory (pgate, x86_gate_);
}
}
Return fok;
}
Listing 4-19. Get the value of the IDT door
IOCTL function spy_io_physical
The spy_io_physical function is simple, it is completely dependent on the mmgetphysicaladdress () function exported by ntoskrnl.exe. The IOCTL function acquires a linear address that needs to be converted by simply calling SpyInputPointer () (see List 4-10), and then lets MMGETPHYSICALALADDRESS () look up the corresponding physical address, and finally returns the result as the Physical_Address structure to the caller. Note that Physical_Address is a 64-bit Large_integer. In most I386 systems, its high 32 bits are always 0. However, if the physical address extension, PAE is enabled, and the installed memory is greater than 4GB, these bits may be non-zero. MMGETPHYSICALADDRESS () uses the PTE array starting from the linear address 0xC0000000 to find a physical address. The basic working mechanism is as follows:
l If the linear address is located at: 0x80000000 ---- 0x9fffffff, its high 3 bits will be set to zero, the final generated physical address is located at: 0x00000000 ----- 0x1ffffFFF.
l Otherwise, the high 20 bits of the linear address will be an index of the PTE array (starting at 0xC0000000).
l If the P bit of the target PTE has been set, this indicates that its corresponding data page is stored in physical memory. In addition to 20-bit PFNs, all PTE bits can be stripped, and the lowest linear address is 12 bits as the offset in the data page is added to the last 32-bit physical address.
l If the data page does not exist in physical memory, MMGETPHYSICALALADDRESS () returns 0.
MmgetPhysicalAddress () assumes that the kernel memory range: 0x80000000 ---- All linear addresses other than 0x9FFFFF use 4KB page. Other functions, such as mmisaddressvalid (), will first load PDE of the linear address, and check the PD bit of the PDE to check the page size 4KB or 4MB. This is a very common method that can handle any memory configuration. However, the above two functions will return the correct result because Windows 2000 uses only the memory range: 0x80000000 ----- 0x9fffffff, use 4MB pages. However, some kernel API functions are clearly designed to be much flexible than other flexible.
IOCTL function spy_io_cpu_info
Individual CPUs are only valid for code running on Ring Level 1. Ring 0 is one of five privilege levels (Intel series only supports two privileges: Ring0 and Ring3). RING 0 means the kernel-mode. These prohibited instructions are: read control registers CR0, CR2, and CR3 content. Because these registers are saved, the application may want to find a way to access them, the solution is the SPY_IO_CPU_INFO function. As shown in Listing 4-20, the spyoutputcpuinfo () function called IOCTL Processing routine uses some embedded assembly to read the control register, and some other valuable information, such as IDT content, GDT, and LDT registers and storage. Segment selectors in registers CS, DS, ES, FS, GS, SS, and TR. Task Register (TR) also contains a selector for TSS involving current tasks.
Typedef struct _sspy_cpu_info {
X86_register CR0;
X86_register cr2;
X86_register cr3;
Spy_segment CS;
Spy_segment ds;
Spy_segment es;
Spy_segment fs;
Spy_segment GS;
Spy_segment ss;
Spy_segment TSS;
X86_Table IDT;
X86_Table GDT;
X86_Selector LDT;
}
Spy_cpu_info, * pspy_cpu_info, ** PPSPY_CPU_INFO;
#define spy_cpu_info_ sizeof (spy_cpu_info)
/ / -------------------------------------------------------------------------------------------- -----------------
NTSTATUS SPYOUTPUTCPUINFO (PVOID POUTPUT,
DWORD DOUTPUT,
PDWORD PDINFO)
{
SPY_CPU_INFO SCI;
PSPY_CPU_INFO PSCI = & sci;
__ASM
{
Push EAX
Push EBX
MOV EBX, PSCI
MOV Eax, Cr0
MOV [ebx.cr0], EAX
Mov Eax, CR2
MOV [EBX.CR2], EAX
Mov Eax, CR3
MOV [EBX.CR3], EAX
SIDT [EBX.IDT.WLIMIT]
MOV [ebx.idt.wreserved], 0
SGDT [EBX.GDT.WLIMIT]
MOV [ebx.gdt.wreserved], 0
SLDT [EBX.LDT.WValue]
MOV [ebx.ldt.wreserved], 0
POP EBX
POP EAX
}
Spysegment (x86_segment_cs, 0, & sci.cs);
Spysegment (x86_segment_ds, 0, & sci.ds);
Spysegment (x86_segment_es, 0, & sci.es);
Spysegment (x86_segment_fs, 0, & sci.fs);
Spysegment (x86_segment_gs, 0, & sci.gs);
Spysegment (x86_segment_ss, 0, & sci.ss);
Spysegment (x86_segment_tss, 0, & sci.tss);
Return Spyoutputbinary (& sci, spy_cpu_info_,
Poutput, doutput, pdinfo;
}
Listing 4-20. Query CPU status information
You can use the help function spysegement () to get the segment selector, in front, we have discussed the function. See List 4-15.
………………to be continued…………………