"Undocumented windows 2000 secrets" translation --- Chapter 4 (6)

xiaoxiao2021-03-06  39

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_pde_Array

SPY_IO_PDE_ARRAY is another normal function, which is just simply copied the entire page directory (start at address 0xc0300000) to the output buffer provided by the caller. This buffer uses the SPY_PDE_ARRAY structure shown in Listing 4-21. You may have guess that the size of this structure is exactly 4KB, which consists of 1,024 32-bit PDE. The x86_pe structure will be used here, the X86_PE structure represents a generalized page (page entry), which can be found in the list 4-3, and the constant x86_pages_4m defines lists 4-5. SPY_PDE_ARRAY Structural Members Always Page (PDE), the X86_PE structure can be the X86_PDE_4M type, or the X86_PDE_4KB type, depending on the value of the PD bit of the PDE.

When the source data page cannot be guaranteed, the data in the memory is usually not a good idea. However, the page directory is one of a few listed. When the current task is running, its page directory always exists in physical memory. It will not be replaced into the page file unless another task is replaced. That's why the CPU's Page Catalog Foundation Register (PDBR) does not have a P (Present) bit, PDE and PTE are similar. Refer to the definition of the x86_pdbr structure in Listing 4-3 to verify this.

Typedef struct _sspy_pde_array

{

X86_PE APDE [x86_pages_4m];

}

SPY_PDE_ARRAY, * pspy_pde_array, ** PPSPY_PDE_ARRAY

#DEFINE SPY_PDE_ARRAY_ SIZEOF (SPY_PDE_ARRAY)

Listing 4-21. Definition of SPY_PDE_ARRY structure

IOCTL function spy_io_page_entry

If you are interested in the Page Entry for a given linear address, this function is a good choice. Listing 4-22 gives the internal details of SpyMemoryPagentry (), which is used to process spy_io_page_entry requests. The SPY_PAGE_ENTRY structure returned by this function is an x86_pe page entry (defined in list 4-3), but it adds two new members (for easy use): DSize and Fpresent. The DSize member is used to illustrate the size of the page (in bytes), its value is not the x86_page_4kb (4,096 bytes) is x86_page_4MB (4, 194, 304 bytes); Fpresent member is used to explain whether the page exists in physical memory. This flag must be compared to the return value of SpyMemoryPagentry () itself, even if Fpresent is false, the function itself can return value to TRUE. At this time, it is valid when the linear address is provided, but it points to the data page has been replaced into the page file. This can be represented by setting the 10th bit of the Page Entry (that is, the PageFile appearing in list 4-22). When the P bit (this bit belongs to the X86_PNPE structure) is set 0, PageFile will be set. Please refer to the details of the x86_pnpe structure in this chapter. The x86_pnpe structure represents a page-not-perent entry, which is defined in the list 4-3. SpyMemoryPageEntry () First, assume that the target page is a 4MB page, and then you copy the PDE of the specified linear address from the system's PDE array (this array start at 0xC0300000) PE member. If the P bit is not 0, there is certainly the next level of page or page table, so check the PS bit to determine the page size. If the PS bit is not 0, it means that this PDE points to a 4MB data page, and it can be ended. ------ SpyMemoryPageEntry () Returns True, and the Fpresent member of the spy_page_entry structure also is also set to TRUE. If the PS bit is 0, the PDE points to a PTE, so the code extracts the PTE from the array starting at 0xC0000000, and checks its P bit. If not 0, the 4KB page containing the specified linear address is in physical memory. At this point, SpyMemoryPagentry () and FPresent are reported. Otherwise, the found must be a page-not-present entry, so spymemorypagentry () returns True, but only when the PageFile bit is not 0, the Fpresent member will be set to false.

Typedef struct _sspy_page_entry

{

X86_PE PE;

DWORD DSIZE;

Bool fpresent;

}

SPY_PAGE_ENTRY, * PSPY_PAGE_ENTRY, ** PPSPY_PAGE_ENTRY

#DEFINE SPY_PAGE_ENTRY_ SIZEOF (SPY_PAGE_ENTRY)

/ / -------------------------------------------------------------------------------------------- -----------------

Bool SpyMemoryPagentry (pvoid pvirtual,

PSPY_PAGE_ENTRY PSPE)

{

SPY_PAGE_ENTRY SPE;

Bool fok = false; spe.pe = x86_pde_Array [x86_pdi (pvirtual);

SPE.DSIZE = X86_Page_4m;

SPE.FPRESENT = FALSE;

IF (spe .PE.PDE4M.P)

{

IF (Spe.PE.PDE4M.ps)

{

Fok = SPE.FPRESENT = TRUE;

}

Else

{

SPE.PE = X86_PTE_Array [x86_page (pvirtual)];

SPE.DSIZE = X86_Page_4K;

IF (spe .PE.PTE4K.P)

{

Fok = SPE.FPRESENT = TRUE;

}

Else

{

Fok = (spe.pe.pnpe.pagefile! = 0);

}

}

}

IF (PSPE! = null) * pspe = SPE;

Return fok;

}

Listing 4-22. Query PDE and PTE

It should be noted that spymemorypagentry () cannot recognize the 4MB page that is replaced by physical memory. If the 4MB page points to PDE does not exist, it will not be possible to determine if a given linear address is valid, and whether the page is also saved in the current page file. The 4MB page is only used for kernel memory range: 0x80000000 ---- 0x9ffffFFF. However, I have never seen such a page is replaced, even when the physical memory is extremely less, I don't need to check any Page-not-present entries related to this.

IOCTL function spy_io_memory_data

The SPY_IO_MEMORY_DATA function is one of the weight level functions because it can copy any number of memory data to the buffer provided by the caller. As you may still remember, the application in user mode is easy to pass in an invalid address. Therefore, the function will be very cautious before touching the source address. Remember, the blue screen can latenner anywhere in the kernel mode.

The calling program requests data in a memory block by incoming a spy_memory_block structure, and the definition of the structure is given at the top of the list 4-23, which specifies the address and size of the memory block. For convenience, the address here is defined as a Union to allow it to be interpreted as an array of byte types (PBYTE PBADDRESS) or interpreted as a PVOID PADDRESS. Listing 4-23 The SpyInputMemory () function will replicate this structure from the IOCTL's input buffer. Its partner function spyoutputMemory () (at the end of the list 4-23) is just an outsourcing of SpyMemoryReadblock (), and the list 4-24 gives the spymemoryreadblock () function. SPYOUTPUTMEMORY () The main responsibility is to return the appropriate NTSTATUS value after spymemoryreadblock () read the data.

SpyMemoryReadblock () returns the memory data it read through a spy_memory_data structure. This structure is defined in list 4-25. I have chosen a different definition method because SPY_MEMORY_DATA is a data type for variable size. Basically, it contains a SPY_MEMORY_BLOCK structure called SMB, followed by an array of Word type, named awdata []. The length of the array is given by the SMB's DBYTES member. In order to allow convenient to define global or local entities of SPY_MEMORY_DATA in a predetermined size, the definition of this structure uses a macro ---- spy_memory_data_n (). The unique parameter of the macro is used to specify the size of the AWDATA [] array. The actual structural definition After the macro definition, it includes an AWDATA [] array of lengths having a length of 0. Spy_memory_data __ () macro first calculates all the size of the SPY_MEMORY_DATA structure, then press the array in this large allocation structure, allowing the Word type data to be added to an array or from an array. Obviously, the low half bit of each word contains the number of bytes of memory data, and the high breeks are used as the flag. Now, there is only the meaning of the eighth bit, which is used to indicate whether the number of memory byout the 0-7 bit is valid. Typedef struct _sspy_memory_block

{

union

{

PBYTE PBADDRESS;

PVOID PADDRESS;

}

DWORD DBYTES;

}

SPY_MEMORY_BLOCK, * pspy_memory_block, ** ppspy_memory_block;

#DEFINE SPY_MEMORY_BLOCK_SEOF (SPY_MEMORY_BLOCK)

/ / -------------------------------------------------------------------------------------------- -----------------

NTSTATUS SPYINPUTMEMORY (PSPY_MEMORY_BLOCK PSMB,

PVOID PINPUT,

DWORD DINPUT)

{

Return SpyinputBinary (PSMB, SPY_MEMORY_BLOCK_, PINPUT, DINPUT;

}

/ / -------------------------------------------------------------------------------------------- -----------------

NTSTATUS SPYOUTPUTMEMORY (PSPY_MEMORY_BLOCK PSMB,

Pvoid ​​Poutput,

DWORD DOUTPUT,

PDWORD PDINFO)

{

NTSTATUS NS = status_buffer_too_small;

IF (* PDINFO = SpyMemoryReadblock (psmb, poutput, doutput)

{

NS = status_success;

}

Return ns;

}

Listing 4-23. Processing the memory block

DWORD SpymemoryReadblock (pspy_memory_block psmb,

PSPY_MEMORY_DATA PSMD,

DWORD DSIZE)

{

DWORD I;

DWORD N = SPY_MEMORY_DATA__ (PSMB-> DBYTES);

IF (dsize> = n)

{

PSMD-> SMB = * psmb;

For (i = 0; i dbytes; i )

{

PSMD-> AWDATA [I] =

(SpyMemoryTestAddress (psmb-> PBADDRESS I)? SPY_MEMORY_DATA_VALUE (PSMB-> Pbaddress [i], true)

: Spy_Memory_Data_Value (0, FALSE));

}

}

Else

{

IF (dsize> = SPY_MEMORY_DATA_)

{

PSMD-> SMB.PBADDRESS = NULL;

PSMD-> SMB.DBYTES = 0;

}

n = 0;

}

Return n;

}

/ / -------------------------------------------------------------------------------------------- -----------------

Bool SpymemoryTestAddress (PVOID PVIRTUAL)

{

Return SpyMemoryPageEntry (PVIRTUAL, NULL);

}

/ / -------------------------------------------------------------------------------------------- -----------------

Bool SpymemorytestBlock (Pvoid ​​Pvirtual,

DWORD DBYTES)

{

PBYTE PBDATA;

DWORD DDATA;

BOOL fok = true;

IF (Dbytes)

{

PBDATA = (PBYTE) (DWORD_PTR) PVIRTUAL & X86_PAGE_MASK);

DDATA = (((DBYTES X86_OFFSET_4K (PVIRTUAL) - 1)

/ PAGE_SIZE) 1) * Page_size;

Do {

Fok = SpyMemoryTestAddress (PBDATA);

PBDATA = Page_Size;

DDATA - = Page_Size;

}

While (Fok && DDATA);

}

Return fok;

}

Listing 4-24. Copy data in memory blocks

SpyMemoryTestAddress () is used to test the validity of the data, and spymemoryReadblock () calls SpyMemoryTestAddress () for each byte to be read. SpyMemoryTestAddress () gives the second half of the list 4-24, which is just a simple call spyMemoryPageEntry (), but the incoming second parameter is NULL. SpyMemoryPageEntry () has been introduced when discussing spy_io_page_entry (List 4-22). Setting its pspy_page_entry pointer parameter to NULL, meaning that the caller does not care about the page entry corresponding to the linear address, so if the linear address is valid, the function will return True. In the context of SpyMemoryPageEntry (), only when a linear address corresponds to the physical memory, or in the page file, the address is valid. Note that this behavior is inconsistent with the API function mmisaddressValid () in ntoskrnl.exe. When the specified page does not exist in physical memory, MMisaddressValid () always returns false, even if this valid data page is located in the page file. This is also the case. Tap 4-24 Another function spyMemoryTestBlock () is a SPYMEMORYTESTADDRESS () enhancement. It can test the validity of a memory area, which tests 4,096 bytes in the specified block each time until all the page in the test is tested.

#define spy_memory_data_n (_n) / struct _SPY_MEMORY_DATA _ ## _ n /

{/

SPY_MEMORY_BLOCK SMB; /

Word awdata [_n]; /

}

TYPEDEF SPY_MEMORY_DATA_N (0)

SPY_MEMORY_DATA, * PSPY_MEMORY_DATA, ** PPSPY_MEMORY_DATA;

#DEFINE SPY_MEMORY_DATA_ SIZEOF (SPY_MEMORY_DATA)

#define spy_memory_data __ (_ n) (SPY_MEMORY_DATA_ ((_n) * Word_))

#define spy_memory_data_byte 0x00ff

#define spy_memory_data_valid 0x0100

#define spy_memory_data_value (_B, _V) /

((Word) ((_B) & SPY_MEMORY_DATA_BYTE | /

((_V)? SPY_MEMORY_DATA_VALID: 0))))))))

Listing 4-25. Definition of SPY_MEMORY_DATA

There is a very important benefit of the page that will be replaced as a valid address: When SpyMemoryReadblock () tries to read the first byte in these pages, these pages will be quickly transferred to memory again. The memory DUMP tool given later relies on mmisaddressValid (), sometimes it will refuse data in the specified address range (even if it is 5 minutes, it can also display these data), and this is just because these pages may have been transferred. In the page file.

IOCTL function spy_io_memory_block

SPY_IO_MEMORY_BLOCK depends on SPY_IO_MEMORY_DATA because it is also a buffer to the caller from any address replication. The main difference is: spy_io_memory_data Attempts to copy all readable bytes, and for SPY_IO_MEMORY_BLOCK, as long as the request is included, it will fail, and one byte will not be copied. This function is required in Chapter 6 to pass the data structure in the kernel space to user mode. This requirement clearly limits this function, so if a structure contains the byte that cannot be read, skip them, only copy the readable bytes.

Similar to SPY_IO_MEMORY_DATA, SPY_IO_MEMORY_BLOCK expects to enter a SPY_MEMORY_BLOCK structure to specify the base address and size of the memory block to be copied. The returned data will be the 1: 1 replica of the original data. Output buffers must be sufficient to accommodate all content to be copied. Otherwise, an error will be reported and no data will be returned.

IOCTL function spy_io_handle_info

Similar to the SPY_IO_PHSICAL previously described, this function allows the program to call the user mode to call other passwordless kernel mode APIs. The kernel driver can obtain the pointer of the object described by the handle by NtoskRNL.exe's ObreferenceObjectByHandle (). There is no peer function in Win32. However, the application can command the SPY device to execute this function and return to the pointer of the object. Listing 4-26 shows the spyoutputHandleInfo () function called by SpyDispatcher (). The input handle can be taken (defined in List 4-10) by SpyInputHandle ().

Listing 4-26 The spy_handle_info structure at the top of the list contains pointers associated with the handle, and the attributes of the handle, both of which are returned by ObreferenceObjectByHandle (). Particularly important is: If OBReferenceObjectbyHandle () calls success, ObdereferenceObject () must be called to restore the reference counter's reference counter to the previous value. If there is no doing this, it will lead to "object reference vulnerabilities". Typedef struct _sspy_handle_info

{

PVOID POBJECTBODY;

DWORD DHANDLEATTRIBUTES;

}

SPY_HANDLE_INFO, * pspy_handle_info, ** ppspy_handle_info;

#define spy_handle_info_ sizeof (spy_handle_info)

/ / -------------------------------------------------------------------------------------------- -----------------

NTSTATUS SPYOUTPUTHANDLEINFO (Handle Hobject,

Pvoid ​​Poutput,

DWORD DOUTPUT,

PDWORD PDINFO)

{

SPY_HANDLE_INFO SHI;

Object_handle_information ohi;

NTSTATUS NS = Status_INVALID_PARAMETER;

IF (HOBJECT! = NULL)

{

NS = ObreferenceObjectByHandle (HOBJECT,

STANDARD_RIGHTS_READ,

Null, kernelmode,

& shi.PObjectBody, & ohi);

}

IF (ns == status_success)

{

Shi.dhandleAttributes = ohi.handleattribute;

ns = spyoutputbinary (& shi, spy_handle_info_,

Poutput, doutput, pdinfo;

ObdereferenceObject (shi.pobjectbody);

}

Return ns;

}

Listing 4-26. An object is referenced by handle

………………to be continued…………………

转载请注明原文地址:https://www.9cbs.com/read-57116.html

New Post(0)