Flag: Tornado! Hurricane!

Blogs >> AlexIonescu's Blog

Created: Tuesday, July 4 2006 11:03.14 CDT  
Printer Friendly ...
Tips & Tricks Part 2 - Putting ZwSystemDebugControl to good use
Author: AlexIonescu # Views: 10195

In my REcon talk I talked about this API, but I realized a lot of people were not aware of all the wonderful things you can do with it. Here is a small example of a really simple and naive rootkit detection code that is fully user-mode. It works with 100% of commercial spyware/keygens that I've tried, but of course not with the experimental "scene" rootkits that can use Kernel Object hooking, memory obfuscation to change the contents of this table, or even injecting code within the area that is being flagged. Nevertheless, it's a useful POC code for the API:
(A couple of entries ago I talked about an API I had discovered that no commercial rootkit seems to hook, this is NOT it).

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;
}




Add New Comment
Comment:









There are 31,137 total registered users.


Recently Created Topics
Information on the t...
Feb/08
Information on the m...
Feb/07
Order Finax, Fincar ...
Feb/07
Information on the m...
Feb/07
Order Proscar (Finas...
Feb/07
Order Proscar, Finax...
Feb/07
Order Finasteride, F...
Feb/07
How to view IDA Pro'...
Nov/02
reverse MC9S12DG128
Oct/07
Looking for an advan...
Mar/21


Recent Forum Posts
Looking for an advan...
tthtlc
Looking for an advan...
tthtlc
Looking for an advan...
clightning
Identify RVA data in...
sohlow
let 'IDAPython' impo...
sohlow
How to find specific...
hackgreti
Problem with ollydbg
sh3dow
How can I write olly...
sh3dow
New LoadMAP plugin v...
mefisto...
Intel pin in loaded ...
djnemo


Recent Blog Entries
nieo
Mar/22
Android Application Reversing

halsten
Mar/14
Breaking IonCUBE VM

oleavr
Oct/24
Anatomy of a code tracer

hasherezade
Sep/24
IAT Patcher - new tool for ...

oleavr
Aug/27
CryptoShark: code tracer ba...

More ...


Recent Blog Comments
ComPuer on:
May/14
Android Application Reversing

nieo on:
Mar/22
IAT Patcher - new tool for ...

djnemo on:
Nov/17
Kernel debugger vs user mod...

acel on:
Nov/14
Kernel debugger vs user mod...

pedram on:
Dec/21
frida.github.io: scriptable...

More ...


Imagery
SoySauce Blueprint
Jun 6, 2008

[+] expand

View Gallery (11) / Submit