PDA

View Full Version : Hidden Kernel Module (Driver) detection techniques


Ramsey
06-20-2009, 01:53 AM
I am trying to develop a tool that can detect the presence of a specific kernel driver rootkit. I know for sure it is unlinked from the PsLoadedModuleList and does not hook any API calls (ZwQuerySystemInformation). Obviously, because it has modified the linked list it does not need to mess w/ any API calls.

I was curious as to how I can proceed in detecting the presence of this rookit given the information presented above.

Kayaker
06-20-2009, 03:35 AM
Hi

As a start, how does the rootkit fare against these two strategies?

http://www.security.org.sg/code/kproccheck.html
http://www.security.org.sg/code/sdtrestore.html

GamingMasteR
06-20-2009, 06:09 AM
Hi,

If the rootkit is unlinked from PsLoadedModuleList only then you can detect it's device/driver object by scanning the device/driver directory .

you can start from this example i made :
Code:
#define NUMBER_HASH_BUCKETS 37
#define OBJECT_TO_OBJECT_HEADER(o) ((POBJECT_HEADER)CONTAINING_RECORD((o), OBJECT_HEADER, Body))

typedef struct _OBJECT_DIRECTORY_ENTRY {
struct _OBJECT_DIRECTORY_ENTRY *ChainLink;
PVOID Object;
} OBJECT_DIRECTORY_ENTRY, *POBJECT_DIRECTORY_ENTRY;

typedef struct _OBJECT_DIRECTORY {
struct _OBJECT_DIRECTORY_ENTRY *HashBuckets[ NUMBER_HASH_BUCKETS ];
struct _OBJECT_DIRECTORY_ENTRY **LookupBucket;
BOOLEAN LookupFound;
USHORT SymbolicLinkUsageCount;
struct _DEVICE_MAP *DeviceMap;
} OBJECT_DIRECTORY, *POBJECT_DIRECTORY;

static UNICODE_STRING DirectoryUnicode = RTL_CONSTANT_STRING(L"Directory";

VOID WalkDirectory(POBJECT_DIRECTORY Directory, POBJECT_TYPE Type)
{
ULONG Bucket;
POBJECT_DIRECTORY_ENTRY DirectoryEntry;
POBJECT_DIRECTORY_ENTRY DirectoryEntryNext;
PVOID Object;
POBJECT_HEADER ObjectHeader;
POBJECT_TYPE ObjectType;
POBJECT_NAME_INFORMATION ObjectName;
ULONG dwRetLength;

for(Bucket = 0; Bucket < NUMBER_HASH_BUCKETS; Bucket++)
{
DirectoryEntry = DirectoryEntryNext = Directory->HashBuckets[Bucket];
while (MmIsAddressValid(DirectoryEntryNext))
{
Object = DirectoryEntryNext->Object;
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
ObjectType = ObjectHeader->ObjectType;
ObQueryNameString(Object, NULL, 0, &dwRetLength);
ObjectName = (POBJECT_NAME_INFORMATION)ExAllocatePool(NonPagedPool, dwRetLength);
ObQueryNameString(Object, ObjectName, dwRetLength, &dwRetLength);
if (ObjectType == Type)
{
DbgPrint("%wZ: %wZ -> %p", &Type->ObjectTypeName, ObjectName, Object);
}
else
{
if (RtlCompareUnicodeString(&ObjectType->ObjectTypeName, &DirectoryUnicode, FALSE) == 0)
{
WalkDirectory((POBJECT_DIRECTORY)Object, Type);
}
}
ExFreePool(ObjectName);
DirectoryEntryNext = DirectoryEntryNext->ChainLink;
}
}
}


VOID ScanDirectory(PWCHAR DirName, POBJECT_TYPE DirType)
{
UNICODE_STRING DirectoryName = RTL_CONSTANT_STRING(DirName);
OBJECT_ATTRIBUTES ObjectAttributes = RTL_INIT_OBJECT_ATTRIBUTES(&DirectoryName, OBJ_CASE_INSENSITIVE);
NTSTATUS Status;
HANDLE Handle = NULL;
POBJECT_DIRECTORY Directory = NULL;
PDRIVER_OBJECT *Objects = NULL;


Status = ZwOpenDirectoryObject(&Handle, DIRECTORY_QUERY, &ObjectAttributes);
if (NT_SUCCESS (Status))
{
Status = ObReferenceObjectByHandle(Handle, FILE_READ_ACCESS, NULL, KernelMode, (PVOID *)&Directory, NULL);
if (NT_SUCCESS (Status))
{
WalkDirectory(Directory, DirType);
ObDereferenceObject(Directory);
}
ZwClose(Handle);
}
}


NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
ScanDirectory(L"\\Driver", *IoDriverObjectType);
ScanDirectory(L"\\Device", *IoDeviceObjectType);
return STATUS_UNSUCCESSFUL;
}

darawk
06-22-2009, 08:14 PM
You can also search for a signature of the DRIVER_OBJECT structure, and the DRIVER_ENTRY structure in the PsLoadedModuleList (it's almost identical to the structure used to link the dll list in a process). Another way to search for drivers (though it won't detect all of them) is to enumerate device objects and then look at their attached drivers. Basically, just look for any and every structure that points to either of the two driver structures previously mentioned and enumerate them and follow the pointers.

You can also scan for MZ headers in kernel space and then grab the name of the module out of the export directory and compare it to lists obtained through higher level methods. This can be tricky though because you may run into fragments of modules that have been unloaded, you will need to come up with a way to validate the 'activeness' of the image.

Ramsey
07-02-2009, 05:47 AM
Thanks for the replies & the sample (GamingMasteR)

I am able to get a pointers to the DEVICE_OBJECT & DRIVER_OBJECT....How would I go about copying the driver from memory (so I can later dump it to a file) ?

GamingMasteR
07-02-2009, 10:12 AM
Quote:
nt!_DRIVER_OBJECT
+0x000 Type : Int2B
+0x002 Size : Int2B
+0x004 DeviceObject : Ptr32 _DEVICE_OBJECT
+0x008 Flags : Uint4B
+0x00c DriverStart : Ptr32 Void
+0x010 DriverSize : Uint4B

+0x014 DriverSection : Ptr32 Void
+0x018 DriverExtension : Ptr32 _DRIVER_EXTENSION
+0x01c DriverName : _UNICODE_STRING
+0x024 HardwareDatabase : Ptr32 _UNICODE_STRING
+0x028 FastIoDispatch : Ptr32 _FAST_IO_DISPATCH
+0x02c DriverInit : Ptr32 long
+0x030 DriverStartIo : Ptr32 void
+0x034 DriverUnload : Ptr32 void
+0x038 MajorFunction : [28] Ptr32 long


Use DriverStart and DriverSize for base address and size, make sure every memory page in the driver region is resident in memory before reading it .