----------- Core Rootkit Technology bypassing IopParseDevice () calls the source detection logic,

Source: Internet
Author: User

----------- Core Rootkit Technology bypassing IopParseDevice () calls the source detection logic,

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

In the previous article, we have seen how IopParseDevice () verifies the passed-In OPEN_PACKET structure. Assuming that the caller of ObReferenceObjectByName () does not allocate and initialize the seventh parameter ParseContext, but simply imports "NULL", when the call chain goes deep into the IopParseDevice, c0000024 (STATUS_OBJECT_TYPE_MISMATCH) is returned because of verification failure ).

We track the allocation of the OPEN_PACKET structure based on the hint in the source code. As described above, the call chainNtCreateFile-> IoCreateFile ()-> IopCreateFile ()The end of IopCreateFile () is the initialization of OPEN_PACKET. The following code snippet uses the source code of the NT 5.2 kernel as an example:

 


That is to say, simply copy the OPEN_PACKET structure initialization logic in IopCreateFile?

Here is another problem. The IopAllocateOpenPacket () routine for allocating the kernel memory of this struct is a macro, which is defined by ExAllocatePoolWithTag () in Visual C ++ 2015. This is easy to do. In our own driver source code, add the corresponding definition, as shown in:

 

 

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

Because the OPEN_PACKET structure is not described in public documents, you can either use"# Include"Include the header file that defines it, or copy and paste the part of the definition directly. Obviously, the latter is relatively easy-OPEN_PACKET in the kernel source code"Iomgr. hThe header file is nested with a bunch of kernel header files. It is very troublesome to clarify these nested inclusion relationships, and most importantly,Some of the data types defined in header files will repeat "ntddk. h" and "wdm. h" in driver development, causing complaints from the compiler.Therefore,Iomgr. hSearch for the string "typedef struct _ OPEN_PACKET" and copy the defined block.

However, the only field in the OPEN_PACKET structure is not defined as "native"-this is the "PDUMMY_FILE_OBJECT" type, and other header files must be included before the compiler reports an error.

My solution is to comment out the row where the field declaration is located, and display the specific location of the field (in the"Iomgr. h") To help you quickly find:

 


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

Note that the OPEN_PACKET structure of the NT 6.1 kernel during compilation is obviously not"Malicious"Modified, so the compiler calculates the value of 0x70 for its" sizeof (OPEN_PACKET) "expression, however, we removed one of the OPEN_PACKET fields in our own driver so that the compiler pre-calculates the value of 0x58 for the expression "sizeof (OPEN_PACKET)" (which will be verified later in the debugging phase ), this causes"Size"The field is not expected by the IopParseDevice () internal logic.0x70, Resulting in C0000024 (STATUS_OBJECT_TYPE_MISMATCH) returned ).

The solution is also very simple. In our driver, do not rely on the computing at the time of compilation to directlySize"Isn't the field value hard-coded to 0x70?

As shown in, you will also notice that I have changed the constant "IO_TYPE_OPEN_PACKET" of the "Type" field to the corresponding value to ensure that the value is correct.

 

 

In addition, because IopAllocateOpenPacket () is equivalent to ExAllocatePoolWithTag (), the latter usually returnsPVOID", That is"Void *"),
So I forced it to be the same type as "openPacket.
Everything is ready,"Dongfeng"When ObReferenceObjectByName () is called, "openPacket" is passed in for the seventh parameter, which is clearly displayed.

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

Unfortunately, I put the compiled driver into a virtual machine (Windows 7, based on the NT 6.1 kernel) to dynamically load the test, but I still cannot get it.

"\ Device \ QQProtect" refers to the corresponding Device object pointer, ObReferenceObjectByName () returns C0000024.

To find out the cause of the fault, I used it before allocating the OPEN_PACKET logic.Inline assemblyAdded a Soft Interrupt"_ Asm {int 3 ;}", Start the kernel debugger kd.exe on the host machine. My startup parameters are as follows:

Kd.exe-n-v-logo d: \ virtual_machine_debugging.txt-y SRV * C: \ Symbols * http://msdl.microsoft.com/download/symbols-k com: pipe, port = \\. \ pipe \ com_1, baud = 115200, reconnect

 

The parameter "logo" specifies that the output information of the entire debugging process should be written into the log;

"-Y" specifies the location of the symbol file (The machine command does not contain symbols of kernel functions and variables. Therefore, the debugger needs to look for additional symbols to display human readable names to users.);
The "-k" parameter specifies that the debugging type is"Named pipeline simulation Serial Port 1", The higher the baud rate value, the faster the response.


Place the re-compiled driver in the Virtual Machine and executeBcdedit.exeTo enable the debugging mode. After restarting the virtual machine, the system enters the debugging mode (you do not need to press F8 to select the menu during the startup process ).

I load my driver as needed, that is, usingService Control Manager(SC .exe) Issue a command to dynamically load and uninstall the file, and implement the corresponding batch processing file content for this function, for example, note that the file should be executed in the virtual machine,"Start = demand "indicates that SC .exe is enabled as needed;"Binpath is the disk path where the drive file is stored.Assume that my driver is named hideprocess. sys, after the batch processing task is executed, an item is added to the relevant registry location. Later, you only need to execute “SC .exe start/stop hideprocess in cmd.exe to dynamically unmount it.

 

When loading according to the above method, we will automatically trigger the preset software breakpoint to check the kernel space of the virtual machine on the host.
Note that the "build" environment should be selected during driver compilation.Check BuildIn this way, a symbolic file with the same name will be generated together with the suffix". PdbSo that the debugger can display the functions and variable names in our own drive to improve debugging efficiency, such:

 

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

After a software breakpoint is triggered,Kv"Command to view stack tracing information. It discloses that our driver entry point DriverEntry () is called by IopLoadDriver () of the I/O manager:

 


Stack top-level function "ReferenceDeviceAndHookIRPdispatchRoutine +0x56"Is where I added a Soft Interrupt. Execute"RRun the "u hideprocess!" command to view the status of the current x86 general register. The EIP points to the address 0x8f4a3196! ReferenceDeviceAndHookIRPdispatchRoutine + 0x56L2", The address of the first line of the disassembly output is 0x8f4a3196, which is consistent with the EIP value; the second line is a hexadecimal value"704F6F49h"Pressure stack, in fact it is an ASCII character"POoIIn other words, this is the third parameter passing ExAllocatePoolWithTag () through the kernel stack (from right to left, please review the previous IopAllocateOpenPacket () macro defines the image ).

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

Continue to press"T"One-step execution, as shown in, you can see that the second parameter of ExAllocatePoolWithTag () is allocated with a kernel memory of 0x70 bytes, because I hardcoded this value in the macro definition, instead of using the sizeof (OPEN_PACKET) expression for the compiler to calculate; on the other hand, the"DtThe command also confirms that its size is 0x70 bytes.

The first input parameter"NonPagedPool"Is a non-convertible page pool, and the data in it cannot be swapped out of the physical memory. The corresponding value of this constant is" 0 ":

 

I don't want to waste time checking the kernel memory allocation details, so I press"P", Step through the ExAllocatePoolWithTag () function call, the followingCmp/jne Assembly SequenceCheck whether the memory is successfully allocated and used for the openPacket pointer in the source code. The actual execution result is to jump to the address 0x8f4a31c6, which corresponds to the logic of the first two fields in the source code initialization OPEN_PACKET structure:


NextKeep running in one stepOn the eve of calling ObReferenceObjectByName (),Step"It conducts troubleshooting internally, so press"T"Follow up, here is a small trick. We have analyzed the source code of ObReferenceObjectByName (), knowing that it will call many functions, and it is roughly clear that the problem occurs in ObpLookupObjectName (), so the command"TcYou can trace the stop of each function call and decide whether to follow up the function.

This is my beautiful dream, but the reality is always cruel. When I track atomic operation functions

Nt! ExInterlockedPopEntrySList ()When calling, kd.exe gets stuck and cannot continue tracking the subsequent call chain. From the stack tracing information earlier, it is roughly consistent with the call sequence we predicted in the source code, but I don't know why it is in nt! In ObpAllocateObjectNameBuffer (), to allocate kernel memory to the input driver object name \ Device \ QQProtect, call nt! ExInterlockedPopEntrySList (), which cannot be traced .... Is it because of the virtual machine environment or the inseparable nature of atomic operation functions?

 

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

This is a bit of nonsense. Generally, we see the top-layer instruction line in stack backtracking. There is an "Args to Child" project, which indicatesParameters passed by the caller, But you can only display the first three.
For example, pass it to nt! The three parameters of ExAllocatePoolWithTag () (from left to right) are 00000000 (NonPagedPool), 00000070 (My hardcoded value ),704f6f49 (ASCII string "pOoI ")Similarly, it is passed to hideprocess! The first parameter 867c3550 of DriverEntry () is the address of the _ DRIVER_OBJECT structure, which is allocated when it is loaded by the I/O manager () the _ DRIVER_OBJECT pointer defined is different.Args to Child"

The data listed is equivalent to the result after the operator * is executed.), The second parameter is the address of the UNICODE_STRING structure, which corresponds to a _ UNICODE_STRING pointer in the source code definition. The structure stores the complete path of our driver in the registry:

 


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


All in all, for the above reasons, I cannot continue to follow up on ObpLookupObjectName () to check whether it has executed the IopParseDevice () callback, and thus cannot determine why the latter returns C0000024.

I think it may be because of changes in the kernel source code version that lead to different judgment logics of Related Routines. I cannot compile the driver that is expected to run on the kernel of the previous version based on the logic of the source code of the previous version.
In fact, there are still some solutions. It takes a long time to use"U"Command disassembly Assembly ObpLookupObjectName () at the beginning of the corresponding machine commands, and then decompile into a similar C pseudo code, compared with the NT 5.2 kernel source code, find out where the change, however, this is a time-and labor-consuming task, with little benefit. It is better to search for the source code of the NT 6.1 kernel, or a similar version, on the Internet, and then think about the bypass method.

Next, obtain the pointer to the object of device A based on the name of device A, and attach the malicious device created by the rootkit/self-drive to the device stack of device, to intercept and check the IRP data passing through device .... This method is outdated, because the kernel mode components of anti-virus software will also check these device stacks to find any malicious devices that match the signature. In addition, the kernel debugger's "!" The devstack command can easily traverse and reveal the device stack content of a given device. It is widely used in computer investigation and evidence collection. From the perspective of achieving stealth, the primary goal of rootkit is, attach to the device stack is not a good example.

On the contrary, ObReferenceObjectByName () can always get the pointer of the drive object, and then hook the driver's IRP distribution routine. This method is extremely concealed and is not easy to detect.

Later posts will discuss how to use this technology in rootkit while adapting to the popular symmetric multi-processor (SMP) environment.

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

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.