-------- Core Rootkit Technology-use nt! _ MDL breaks through the KiServiceTable read-only access restriction Part II, _ mdlkiservicetable

Source: Internet
Author: User

-------- Core Rootkit Technology-use nt! _ MDL breaks through the KiServiceTable read-only access restriction Part II, _ mdlkiservicetable

Bytes -------------------------------------------------------------------------------------------

At the beginning of this article, I entered the topic. Because MDL is involved, related background knowledge is required:

 

Nt! _ MDL represents a "memory descriptor linked list" structure, which describes the user or kernel mode virtual memory (that is, the buffer), and the corresponding physical pages are locked,

Cannot be swapped out.

Because of a virtual IP address, consecutive users or kernel buffers may be mapped to multiple discontinuous physical pages, so nt! _ MDL fixed length (0x1c byte) header Postfix

Page Frame Numbers. Each physical Page described by MDL has a Page number,

Therefore, the physical address ranges referenced by these page boxes correspond to a specific user or kernel mode buffer.

Generally, the size of the virtual and physical pages is 4 KB, and the number of system services in the KiServiceTable is 401. Each function's entry point occupies 4 bytes, and the size of the entire call table

The value is 1.6 KB. You only need one physical page to describe the buffer zone through MDL. In this case, the MDL has only one page box number.

 

Although nt! _ MDL is a semi-transparent structure, but it is removed from the kernel debugger and WRK source code, such as WRK source code.

The definition in the "ntosdef. h" header file, as you can see, is called "linked list" because its first field "Next" is a pointer pointing to the Next nt! _ MDL structure.

For the scenario where we hook KiServiceTable, the Next field is not required. Under what circumstances will the Next field be used?

 

Some types of drivers in Windows, such as network stacks, support the MDL chain. The buffers described by multiple MDL are actually scattered,

Assume that each driver in the stack is allocated with an MDL, followed by some physical page box numbers to describe the virtual buffers they use, then these buffers pass through

The Next field (pointing to the Next MDL) of each _ MDL is linked.

 


 

------------------------------------------------------------

 

The Process field in the MDL describing the user mode buffer points to the EPROCESS structure of the Process, and this virtual address space in the Process is locked by MDL.

Live.

If the buffer described by MDL is mapped to the kernel virtual address space, the MappedSystemVa field of _ MDL points to the base address of the kernel mode buffer.

Only when the MDL_MAPPED_TO_SYSTEM_VA or MDL_SOURCE_IS_NONPAGED_POOL bits are set in the MdlFlags field of _ MDL,

The MappedSystemVa field is valid.

The Size field of _ MDL contains the total Size of the entire PFN array after the MDL header is added.

It also contains all the macro definitions of the MdlFlags field. The two-byte field can be a combination of any macro to describe the status and attributes of MDL.

The StartVa and ByteOffset fields of MDL define the starting address of the original buffer locked by the MDL.

(The original buffer may be mapped to another kernel buffer or user buffer)

StartVa points to the starting address of the virtual page. ByteOffset contains the actual buffer offset starting from StartVa.

The ByteCount field of MDL describes the buffer size locked by the MDL (in bytes)

For the KiServiceTable we want to hook, the starting point of the virtual page of the kernel buffer is carried by the StartVa field;

The ByteOffset field carries the page offset of the KiServiceTable, And the ByteCount field carries the size of the kernel buffer.

 

If you can see the cloud in the fog, don't worry. We will introduce an nt that describes KiServiceTable during debugging later! _ MDL structure instance for analysis,

Then you will suddenly understand the design ideas of these fields.

------------------------------------------------------------

 

To use MDL to bypass the read-only attribute of KiServiceTable, you must use the I/O manager and

The general process of some functions exported by the Memory Manager is as follows:

 

IoAllocateMdl ()Assign an MDL to describe KiServiceTable->MMP robeandlockpages ()Set the KiServiceTable described by the MDL

The physical page is locked in the memory, and the read and write permissions are granted to this page (actually, the "R" flag in the PTE content that describes this page is changed to "W ")

->MmGetSystemAddressForMdlSafe ()Map the KiServiceTable to another kernel virtual address area (generally, the rootkit is loaded

To the kernel address range ).

In this way, the original virtual address of KiServiceTable and the new mapped virtual address are all translated to the same physical address, and the PTE content that describes the new virtual address is marked

Write Permission bit, so that we can achieve security hook KiServiceTable by modifying the system service routine in this new virtual address, without causing

BugCheck.

As shown below, I encapsulate all the operations involved in the above into a custom function.MapMdl (). Due to the long logic, there are multiple descriptions:

MapMdl () is called in DriverEntry () of our rootkit, while several global variables related to MDL are declared externally in DriverEntry,

They are shared by MapMdl () and DriverEntry.

Note that OS _ki_service_table stores the address of KiServiceTable (see the previous code for positioning KiServiceTable ),

It is converted from DWORD to generic pointer to meet the IoAllocateMdl () parameter requirements in MapMdl (); the last parameter-expression

0x191*4 -- the size of the entire KiServiceTable Buffer: If MapMdl () is returned successfully, the global variable mapped_ki_service_table

Hold the kernel virtual address newly reflected by KiServiceTable; these global variables are "self-Annotated", and the content of the address held by pfn_array_follow_mdl

Is the physical page number described by MDL:

 


--------------------------------------------------------------

 

As shown in the logic of the first part of MapMdl (), the local variable mapped_addr is expected to store the kernel virtual address newly reflected by the KiServiceTable and act

Return Value of MapMdl () to DriverEntry () to further initialize the global variable mapped_ki_service_table.

Note that PVOID can be assigned to any type of pointer, which is legal.

IoAllocateMdl () returns a pointer pointing to the allocated MDL, which describes the physical memory layout of the KiServiceTable. This pointer is used for initialization.

Global variables passed in as real parametersMdl_ptr(Mdl_pointer is a form parameter ).

The first software breakpoint I added is to study the MDL allocated by IoAllocateMdl (), including MappedSystemVa, StartVa, and MdlFlags.

Field content-in fact, these field values are

IoAllocateMdl ()-> fig ()-> MmGetSystemAddressForMdlSafe ()

Each stage of the call chain changes, so I added a total of three breakpoints in the relevant check area, which helps us to understand nt in the subsequent debugging process! _ MDL

Design Idea.

 


 

I put the operations performed by using the Windows executor component routine into oneTry-try tBlock in order to handle possible exceptions. The logic in the block is as follows:

Figure: When illegal access occurs, call IoFreeMdl () to release our MDL pointer, and then MapMdl () returns NULL, resulting in DriverEntry () Printing

Error message.

 


 

------------------------------------------------------------

We need to know more about the second parameter of IoAllocateMdl (), so I have translated the related snippets in the MSDN document, as shown below:

 The second parameter of IoAllocateMdl () specifies the size of the buffer to be described by the allocated MDL. If the length is less than 4 kb,

Then the MDL mapped to it only describes a locked physical page;

If the length is an integer multiple of 4 kb, the MDL mapped to it describes the corresponding number of physical pages (through the PFN array following the MDL)

For Windows Server 2003, Windows XP, and Windows 2000,

The maximum buffer length (in bytes) supported by this routine is:

PAGE_SIZE * (65535-sizeof (MDL)/sizeof (ULONG_PTR) (about 67 MB)

 

 

For Windows Vista and Windows Server 2008, the maximum buffer size that can be passed in is:

(2 gigabytes-PAGE_SIZE)

 

 

For Windows 7 and Windows Server 2008 R2, the maximum buffer size that can be passed in is:

(4 gigabytes-PAGE_SIZE)

The IRQL requirement for executing this routine is <= DISPATCH_LEVEL

 

------------------------------------------------------------

 

The second part of the MapMdl () logic is shown in. It follows the first software breakpoint. We check that the MDL_ALLOCATED_FIXED_SIZE flag in MDL is

No bit. This flag is used to call IoAllocateMdl () and the second parameter is used to indicate a fixed size.

The key to write access is whether or not to lock the memory. Because the calling table of the system scope such as KiServiceTable is very important, if it is switched out of the physical

Storage, the system will not crash, so frankly speaking, we only need to write permission to call it.

 

The second breakpoint is followed by it. In this way, you can check in the debugger how matrix labels () are modified in MDL. You can also use programming techniques.

Check, the second if block logic in, in fact, the call to the Message Queue () will add MDL_WRITE_OPERATION to the MdlFlags Field

And MDL_PAGES_LOCKED. This is the result we want!

Finally, we call MmGetSystemAddressForMdlSafe () to map the original virtual address described by the MDL to another location in the kernel space. The new address is usually located

Somewhere in the kernel space loaded by the driver. The local variable mapped_addr holds this new address and is used to return and initialize the global variable.

Mapped_ki_service_table.

Similarly, we can check which MDL structure members modified by MmGetSystemAddressForMdlSafe (), which is critical for understanding the working mechanism of MDL.

 


------------------------------------------------------------

The logic of the third part of MapMdl () is shown in. We check whether one more MmGetSystemAddressForMdlSafe () is added.

The MDL_MAPPED_TO_SYSTEM_VA flag and print the information using the DBG_TRACE macro.

Global VariablesBackup_mdl_ptrIt is the MDL pointer for backup when IoAllocateMdl () is called. It points to the same nt as mdl_ptr! _ MDL structure.

The following logic helps you understand the PFN array behind the MDL header: mdl_ptr points to nt! _ MDL structure header. Adding 1 to it means holding

The memory address is added with 1 * sizeof (MDL) bytes, so the start address of the PFN array after the MDL header is located-now the global variable

Pfn_array_follow_mdl (a pointer of the PPFN_NUMBER type) holds this address. In the center, the last and third dbuplint () calls the address --

The offset xx (0x1b) address after the MDL structure is a PFN array to store the physical page box number mapped to the virtual buffer described by the MDL.

The last dbuplint () is called to extract pfn_array_follow_mdl to output the physical page number stored in the address.

The exception capture logic behind the return mapped_addr; statement is the try-catch t block. See the preceding parameter.

 


------------------------------------------------------------

Now, the program accesses the writable mapped_ki_service_table and the read-only OS _ki_service_table are all translated into the same physical memory,

The latter is where KiServiceTable is actually stored.. Next, we use a function pointer to save an original system service in KiServiceTable,

Then we replace the original system service at this location with the address of our hook routine, and the hook routine only calls the original system service internally to implement secure forwarding.

For the sake of demonstration simplicity, I select the 0x39 (57) routine in KiServiceTable, because it has only one parameter, so that our hook routine can follow the same

Parameter Declaration-kernel system service scheduler (Nt! KiFastCallEntry ()) The target system service that it calls has been replaced with our hook routine,

Therefore, he will use the return value and output parameters of the hook routine in an established way. In this case, as long as our hook routine prototype declares that there are

Nuances may lead to unexpected Kernel errors and blue screens. Obviously, system services with many and complex parameters are not suitable for demonstration.

In addition, the parameter types received by some system services are not defined in the wdm. h/ntddk. h header file, which indicates that these data types are not used by driver developers,

It is only used by kernel components. To introduce the header file containing this definition, you may encounter a complicated nested header file inclusion problem, which is no less troublesome than the Linux platform.

Binary Package dependency hell ".

System Service Routine No. 57, that is, nt! NtCompleteConnectPort () has only one documented parameter. related definitions in the WRK Source Code are as follows:

 


------------------------------------------------------------

Therefore, our hook routine only needs to completely emulate its return value type and parameter type, and then internally call the function pointer pointing to the original routine to implement redirection.

Define a function pointer using typedef. the return value type is the same as the parameter type and NtCompleteConnectPort (), and then declare a function pointer.

Instance. Related code is as follows:

 


------------------------------------------------------------

The global variable ori_sys_service_ptr holds the endpoint address of NtCompleteConnectPort (). The former is in our rootkit endpoint.

Initialized in DriverEntry (). After saving this pointer, you can replace NtCompleteConnectPort () with the hook routine, as shown in:

 


 

It should be noted that although the pointer name mapped_ki_service_table is used as an array name to access KiServiceTable,

However, the Code generates a warning in the compiler as follows:

 


1 1>warnings in directory d:\kmdsource_use_mdl_mapping_ssdt
2 1>d:\kmdsource_use_mdl_mapping_ssdt\usemdlmappingssdt.c(155) : warning C4047: '=' : 'OriginalSystemServicePtr' differs in levels of indirection from 'DWORD'
3 1>d:\kmdsource_use_mdl_mapping_ssdt\usemdlmappingssdt.c(157) : warning C4047: '=' : 'DWORD' differs in levels of indirection from 'NTSTATUS (__stdcall *)(HANDLE)'

 

Ori_sys_service_ptr is an OriginalSystemServicePtr function pointer (NTSTATUS (_ stdcall *) (HANDLE), while

Mapped_ki_service_table is a common pointer. Its array name representation is combined with an array subscript. In fact, it is regarded as a DWORD variable that stores the corresponding elements,

The two have different levels of indirect addressing.

 

Currently, we can ignore these two warnings, because the rootkit source code containing this code can safely hook the target system service functions after compilation.

Normal operation is normal. Similar warnings can be filtered out by specifying the compilation options at the warning level.

----------------------------------------------------------------------------------

At this point, you will surely think that I am both arrogant and mother-in-law, so let's take a look at the following simple global overview, which explains how MDL puts a buffer zone

Map to another place and describe the same physical layout,Note that the organizational structure in the figure is generated after MmGetSystemAddressForMdlSafe () is executed..


 

Note: I did not provide the specific 20-bit physical page number carried by the first member in the PFN array, the original and mapped new kernel buffer, and the actual RAM

In the physical page, and "byte within page" is the byte sequence starting at a specific offset in the page, that is, the actual physical address of the entry point of the system service routine!

These "placeholders" will be given in the debugging unit of the third part. After all, driver development and debugging complement each other, and only the source code can be used without theory or practice.

I don't know the truth about debugging. Otherwise, no one needs more than 640 KB of memory .....

Bytes --------------------------------------------------------------------------------------

To be continued

 


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.