打印本文 打印本文 关闭窗口 关闭窗口
Win2K/XP SDT Restore 0.2 (Proof-Of-Concept)
作者:武汉SEO闵涛  文章来源:敏韬网  点击数1904  更新时间:2009/4/25 0:44:48  文章录入:mintao  责任编辑:mintao
by Tan Chew Keong

Released : 06 Jul 2004
Updated : 09 Oct 2004 (paper available for download)

Download Version 0.2
Download Version 0.1

Download Paper Presented at HITB 2004

Introduction

Win32 Kernel Rootkits modify the behaviour of the system by Kernel Native API hooking. This technique is typically implemented by modifying the ServiceTable entries in the Service Descriptor Table (SDT). Such modification ensures that a replacement (hook) function installed by a rootkit is called prior to the original native API. The replacement function usually calls the original native API and modifies the output before returning the results to the user-space program. This technique allows kernel rootkits to hide files, processes, and to prevent process termination.

This proof-of-concept tool demonstrates the possibility of defeating such rootkits by removing Kernel Native APIs hooks and restoring the ServiceTable entries back to their original state.
 

Kernel Native API Hooking by System Service Dispatch Table Modification

In Windows, user-space applications request for system services by calling the APIs exported by the various DLLs. For example, to write data to an open file, pipe or device, the WriteFile API that is exported by kernel32.dll is usually used. Within kernel32.dll, the implementation of WriteFile API in turn calls the ZwWriteFile native API that is exported by ntdll.dll. The work done by ZwWriteFile is actually performed in kernel-space. Hence, the implementation of ZwWriteFile in ntdll.dll contains only minimal code to transit into kernel-space using interrupt 0x2E. The disassembly of ZwWriteFile is shown below.

 

1- MOV EAX, 0ED
2- LEA EDX, DWORD PTR SS:[ESP+4]
3- INT 2E
4- RETN 24
The magic number 0ED in line 1 is the Service Number for ZwWriteFile. It will be used to offset into the ServiceTable (System Service Dispatch Table) in kernel-space to locate the address of the function that implements the writefile service. The address of the ServiceTable can be found within the Service Descriptor Table (SDT). The Service Descriptor Table can be referenced using the exported KeServiceDescriptorTable symbol. This is a structure with the following definition.
typedef struct ServiceDescriptorTable {
	SDE ServiceDescriptor[4];
} SDT;

typedef struct ServiceDescriptorEntry {
        PDWORD ServiceTable;
        PDWORD CounterTableBase;
        DWORD  ServiceLimit;
        PBYTE  ArgumentTable;
} SDE;

The first member of the structure, SDT.ServiceDescriptor[0].ServiceTable, is an array of function pointers to the service functions. The DWORD value at ServiceTable[0xED] is a function pointer to NtWriteFile, which contains the actual code to write to files, pipes or devices. Hence, to modify the behaviour of the user-space WriteFile API, one simply needs to write a replacement function, load it into kernel space as a driver, and modify ServiceTable[0xED] to point to the replacement function. The replacement function needs to keep the original function pointer (original value of ServiceTable[0xED]) so that it can be called to perform the original defined function.

Example One - Process Hiding by Hooking ZwQuerySystemInformation

User-space programs can use the ToolHelp APIs to obtain a list of all running processes. The ToolHelp APIs in turn calls the ZwQuerySystemInformation native API exported by ntdll.dll to obtain the list. To hide processes, a kernel-space rootkit, which is loaded as a driver, can modify the function pointer at ServiceTable[0x97] (ZwQuerySystemInformation) to redirect the call to a replacement function. The replacement function first calls the original ZwQuerySystemInformation API to obtain an array containing information of all running process. The returned array is then modified to remove the entry containing the process to be hidden. Finally, the modified result is returned to the user-space program. This effectively prevents the user-space program from "seeing" the hidden process.

Example Two - Driver/Module Hiding by Hooking ZwQuerySystemInformation

User-space programs can obtain a list of all loaded drivers using the ZwQuerySystemInformation native API, specifying SystemModuleInformation as its first parameter. As mentioned earlier, ZwQuerySystemInformation is exported by ntdll.dll and can be called directly by user-space programs. In kernel-space, the ZwQuerySystemInformation native API obtains the list of loaded drivers by traversing the PsLoadedModuleList. A kernel-space rootkit can manipulate the results returned by ZwQuerySystemInformation by modifying ServiceTable[0x97] (ZwQuerySystemInformation) to point to a replacement fnuction. The replacement function will first call the original ZwQuerySystemInformation to get an array of all loaded drivers. The driver to be hidden (i.e. the rootkit) is then removed from the array. This manipulated array is returned to the user-space program.
 

SDT Restoring Technique Used by POC Code

This POC code restores the values of the ServiceTable entries by writing directly to \device\physicalmemory. Hence, it works entirely in user-space and do not need to load a driver. The following steps describe how the code works.

  1. Use NtOpenSection to get a handle to \device\physicalmemory with SECTION_MAP_READ | SECTION_MAP_WRITE access. If this fails, modify the DACL of \device\physicalmemory by adding SECTION_MAP_WRITE access permission to the current user. Try to open \device\physicalmemory again.

     

  2. Load ntoskrnl.exe into memory with proper alignment and locate the address of KeServiceDescriptorTable from the export table of ntoskrnl.exe

     

  3. Use NtMapViewOfSection to map in the physical memory page at the address of KeServiceDescriptorTable.

     

  4. Get the address of KeServiceDescriptorTable.ServiceDescriptor[0].ServiceTable from the page.

     

  5. Use NtMapViewOfSection to map in the physical memory page containing the running kernel''''s SerivceTable. This address is available at KeServiceDescriptorTable.ServiceDescriptor[0].ServiceTable.

     

  6. Use the address of KeServiceDescriptorTable.ServiceDescriptor[0].ServiceTable to offset into the loaded ntoskrnl.exe

     

  7. Loop through all entries in KeServiceDescriptorTable.ServiceDescriptor[0].ServiceTable, comparing the copy in the kernel memory with the copy in the loaded ntoskrnl.exe. Restore to kernel memory (i.e. into the mapped page) any discrepancies that are detected. This code works based on the fact that a complete original copy of the ServiceTable exists in ntoskrnl.exe.

     


 
Screen Dump

 

C:\>sdtrestore
SDTrestore Version 0.1 Proof-of-Concept by SIG^2 G-TEC (www.security.org.sg)

KeServiceDescriptorTable                8046DFA0
KeServiceDecriptorTable.ServiceTable    804742B8
KeServiceDescriptorTable.ServiceLimit   248

ZwAllocateVirtualMemory    10 --[hooked by unknown at F754CE74]--
ZwCreateFile               20 --[hooked by unknown at F754CA85]--
ZwCreateKey                23 --[hooked by unknown at F754CC5E]--
ZwCreateProcess            29 --[hooked 

[1] [2]  下一页

打印本文 打印本文 关闭窗口 关闭窗口