• Aucun résultat trouvé

Technique: IRP hooking

Dans le document Michael A. Davis (Page 190-193)

Even though the He4Hook project was abandoned by its authors in 2002, there are many versions of He4Hook available in the wild (http://he4dev.e1.bmstu.ru/HookSysCall/), each with varying levels of capabilities. Under the hood, all versions either use SSDT modification or IRP hooking to hide files, drivers, and registry entries. The version used for this book, 2.11, utilizes IRP hooking on the file-system drivers for Windows, ntfs.sys, and fastfat.sys. It overwrites the entries in the major function table of all drivers and devices attached to the file-system drivers for the following functions:

• IRP_MJ_CREATE

• IRP_MJ_CREATE_NAMED_PIPE

• IRP_MJ_CREATE_MAILSLOT

• IRP_MJ_DIRECTORY_CONTROL

He4Hook replaces the pointers to the real OS functions with pointers to the rootkit’s functions. It also replaces the driver’s unload routine. The rootkit achieves this by directly modifying the DRIVER_OBJECT structures in memory and replacing the pointers as necessary.

After the He4Hook rootkit driver is loaded, it queues a system thread to scan the directory objects, \\Drivers and \\FileSystem, using the Windows API function ZwOpenDirectoryObject(). For each driver file it can read from these lists (using an undocumented Windows API function, ZwQueryDirectoryObject(), it gets a pointer to the driver’s DRIVER_OBJECT using an undocumented exported Windows kernel function, ObReferenceObjectByName(). After it retrieves this pointer, the rootkit inspects the device chain for that driver, looping through each device and ensuring its DEVICE_OBJECT is an appropriate type of device to hook (i.e., file-system-related devices). The code from DriverObjectHook.c is shown here:.

pDeviceObject = pDriverObject->DeviceObject;

while (pDeviceObject) {

if (IsRightDeviceTypeForFunc(pDeviceObject->DeviceType, IRP_MJ_CREATE) == TRUE) { TopDeviceObject = pDeviceObject;

do {

if (IsRightDeviceTypeForFunc(TopDeviceObject->DeviceType, IRP_MJ_CREATE) == TRUE) { pTargetDriverObject = TopDeviceObject->DriverObject;

for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; ++i) { if (pTargetDriverObject->MajorFunction[i] != NULL) {

if (pTargetDriverObject->MajorFunction[i] != DriverObjectDispatch){

AddHookedDriverIntoTree(pTargetDriverObject);

if (TopDeviceObject->AttachedDevice == NULL) break;

TopDeviceObject = TopDeviceObject->AttachedDevice;

}

while (1);

}

pDeviceObject = pDeviceObject->NextDevice;

}

The most important part of this code is highlighted in bold. This FOR loop iterates through all possible IRP major function codes for the given driver and device, and if there is a corresponding dispatch function in the driver, the rootkit replaces that function pointer with its own dispatch routine, DriverObjectDispatch(). This is the definition of IRP hooking. Note how the rootkit also makes sure the functions aren’t already hooked.

Thus, the rootkit has succeeded in redirecting IRPs destined for the dispatch functions of these various drivers to its own dispatch function. Great, now it’s going to get literally hundreds of IRPs per microsecond for all sorts of devices, from network named pipes to symbolic links. To find the melody from the noise, the rootkit filters these IRPs inside its dispatch function.

So let’s take a closer look at the dispatch function’s source code to explore how He4Hook utilizes IRP hooking to hide files by hooking IRP_MJ_DIRECTORY_CONTROL.

Keep in mind that this function is used by the rootkit as an IRP dispatch function, so every time a file read request, for example, is issued, this function will get a chance to inspect the resulting IRP. The rootkit has already hookedthe necessary IRPs; the dispatch function is where it does something with those IRPs (like hide a file!).

The first 70 lines of the function set up data structures and ensure the IRP is the one that it wants to filter. It does this by validating that the IRP’s major function code matches one of the four codes that it cares about and that its device type is appropriate (CD-ROM, disk, and so on):

if ( (dwMajorFunction == IRP_MJ_SHUTDOWN) || (bIrpAlreadyTreat == FALSE) &&

(bIsRightDeviceType == TRUE) && ((dwMajorFunction >= IRP_MJ_CREATE &&

dwMajorFunction <= IRP_MJ_CREATE_NAMED_PIPE) ||

(dwMajorFunction == IRP_MJ_CREATE_MAILSLOT) #ifdef HOOK_QUERY_DIRECTORY_IRP || (

dwMajorFunction == IRP_MJ_DIRECTORY_CONTROL ) #endif))

If all of these conditions match, the rootkit then copies some data from the IRP that it cares about (such as the filename being requested for creating, reading, deleting, and so on) and calls two functions, TreatmentIrpThread() and TreatmentQueryDirectoryIRP (or TreatmentCreateObjectIRP() for all other major functions). These two functions handle modifying the IRP before the rootkit passes it on to the next driver in the driver stack. To hide a file, the dispatch routine simply removes the directory information from the resulting IRP, so when other drivers receive the IRP, the information is missing. Thus, whenever a program calls into

NtQueryDirectoryFile(), or any other API that relies on a file-system driver, whichever files were configured to be hidden will not be returned by these functions.

The technique used by He4Hook relies on some undocumented functions that may not exist in recent versions of Windows. Since these functions are undocumented, they are not guaranteed to exist between patches and major releases. Furthermore, most versions of He4Hook that implement kernel-function hooking in addition to IRP hooking can be trivially detected by tools that scan the SSDT.

He4Hook is a fairly sophisticated rootkit that pays attention to detail. This makes it a very stealthy rootkit. Its meticulous nature is evident in the use of undocumented functions (in an impressively small, 38KB header file called NtoskrnlUndoc.h) and crafty pointer reassignments throughout the source code. It also makes extensive use of preprocessor directives to exclude certain code sections from compilation. That way, if the rootkit user doesn’t want that functionality, it won’t appear in the resulting driver, minimizing the suspicious code inside the resulting driver.

Perhaps the stealthiest aspect of He4Hook is how it loads the driver initially. All Windows drivers must implement a function called DriverEntry() that represents the entry point when the driver is loaded. All well-behaved drivers initialize required housekeeping structures and fill in the driver’s major function table. However, He4Hook instead calls a function InstallDriver() inside its DriverEntry() routine. This function extracts the driver binary at a predefined offset from its image base address in memory. It allocates some non-paged kernel pool memory and copies the driver into that buffer. It then calls a custom function to get the address of an unexported internal function, which it then calls as the “real” DriverEntry() routine.

dwFunctionAddr = (DRIVER_ENTRY) NativeGetProcAddress((DWORD)pNewDriver-Place, "__InvisibleDriverEntry@8");

if (!dwFunctionAddr) {

ExFreePool(pNewDriverPlace);

return FALSE;

}

NtStatus = dwFunctionAddr(DriverObject, RegistryPath);

The unexported function __InvisibleDriverEntry is the actual DriverEntry() routine that is immediately called inside InstallDriver() once it is assigned to the pointer variable dwFunctionAddr. This technique provides two primary benefits:

(1) The driver is not loaded using the Service Control Manager (SCM), thus no disk or registry footprints exist, and (2) the function redirection helps disguise its real functionality, rather than advertising it inside well-known driver routines such as DriverEntry().

Dans le document Michael A. Davis (Page 190-193)

Documents relatifs