NTSTATUS
ReadKernelMemory(IN PVOID BaseAddress,
OUT PVOID Buffer,
IN ULONG Length)
{
NTSTATUS Status;
SYSDBG_VIRTUAL DbgMemory;
//
// Setup the request
//
DbgMemory.Address = BaseAddress;
DbgMemory.Buffer = Buffer;
DbgMemory.Request = Length;
//
// Do the read
//
Status = NtSystemDebugControl(SysDbgReadVirtual,
&DbgMemory,
sizeof(DbgMemory),
NULL,
0,
NULL);
return Status;
}
PCHAR
FindDriverForAddress(IN PVOID Pointer)
{
NTSTATUS Status;
PRTL_PROCESS_MODULES ModuleInfo;
PRTL_PROCESS_MODULE_INFORMATION ModuleEntry;
ULONG ReturnedLength;
ULONG i;
//
// Figure out how much size we need
//
Status = NtQuerySystemInformation(SystemModuleInformation,
NULL,
0,
&ReturnedLength);
if (Status != STATUS_INFO_LENGTH_MISMATCH) return NULL;
//
// Allocate a buffer large enough
//
ModuleInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, ReturnedLength);
if (!ModuleInfo) return NULL;
//
// Now query the data again
//
Status = NtQuerySystemInformation(SystemModuleInformation,
ModuleInfo,
ReturnedLength,
&ReturnedLength);
if (!NT_SUCCESS(Status)) return NULL;
//
// Loop all the drivers
//
for (i = 0; i < ModuleInfo->NumberOfModules; i++)
{
//
// Get the current entry and check if the pointer is within it
//
ModuleEntry = &ModuleInfo->Modules[i];
if ((Pointer > ModuleEntry->ImageBase) &&
(Pointer < ((PVOID)((ULONG_PTR)ModuleEntry->ImageBase +
ModuleEntry->ImageSize))))
{
//
// Found a match, return it
//
return ModuleEntry->FullPathName;
}
}
}
PCHAR
DetectDriver(VOID)
{
BOOLEAN Old;
NTSTATUS Status;
ULONG_PTR MappedAddress;
PVOID KernelBase, TableBase;
UNICODE_STRING KernelName;
ANSI_STRING TableName = RTL_CONSTANT_STRING("KeServiceDescriptorTable");
RTL_PROCESS_MODULES ModuleInfo;
ULONG Flags;
KSERVICE_TABLE_DESCRIPTOR ServiceTable;
//
// Give our thread the debug privilege
//
Status = RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &Old);
if (!NT_SUCCESS(Status)) return NULL;
//
// Query the kernel's module entry
//
Status = NtQuerySystemInformation(SystemModuleInformation,
&ModuleInfo,
sizeof(ModuleInfo),
NULL);
if (Status != STATUS_INFO_LENGTH_MISMATCH) return NULL;
//
// Initialize the kernel's full path name
//
Status = RtlCreateUnicodeStringFromAsciiz(&KernelName,
ModuleInfo.Modules[0].FullPathName);
if (!Status) return NULL;
//
// Keep only the short name
//
KernelName.Buffer = KernelName.Buffer +
(KernelName.Length / sizeof(WCHAR)) -
12;
//
// Map the kernel
//
Flags = IMAGE_FILE_EXECUTABLE_IMAGE;
Status = LdrLoadDll(NULL, &Flags, &KernelName, &KernelBase);
if (!NT_SUCCESS(Status)) return NULL;
//
// Find the address of KeServiceDescriptorTable
//
Status = LdrGetProcedureAddress(KernelBase, &TableName, 0, &TableBase);
if (!NT_SUCCESS(Status)) return NULL;
//
// Unload the kernel image, we're done with it
//
Status = LdrUnloadDll(KernelBase);
if (!NT_SUCCESS(Status)) return NULL;
//
// Get the virtual address we need
//
MappedAddress = (ULONG_PTR)ModuleInfo.Modules[0].ImageBase;
MappedAddress -= (ULONG_PTR)KernelBase;
MappedAddress += (ULONG_PTR)TableBase;
//
// Now read the SSDT
//
Status = ReadKernelMemory((PVOID)MappedAddress,
&ServiceTable,
sizeof(ServiceTable));
if (!NT_SUCCESS(Status)) return NULL;
//
// Setup the argument table
//
ArgumentTable = RtlAllocateHeap(RtlGetProcessHeap(),
0,
ServiceTable.Limit * sizeof(ULONG_PTR));
if (!ArgumentTable) return NULL;
//
// Now fill it up
//
Status = ReadKernelMemory(ServiceTable.Base,
ArgumentTable,
ServiceTable.Limit * sizeof(ULONG_PTR));
if (!NT_SUCCESS(Status)) return NULL;
//
// Now scan it
//
for (i = 0; i < ServiceTable.Limit; i++)
{
//
// Make sure no pointer is outside the kernel area
//
if (ArgumentTable[i] > 0x8FFFFFFF)
{
//
// Find the driver file that this belongs to
//
return FindDriverForAddress(UlongToPtr(ArgumentTable[i]));
}
}
//
// If we got here, then you don't have any rootkit
//
return NULL;
}