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: 12515

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,320 total registered users.


Recently Created Topics
[help] Unpacking VMP...
Mar/12
Reverse Engineering ...
Jul/06
hi!
Jul/01
let 'IDAPython' impo...
Sep/24
set 'IDAPython' as t...
Sep/24
GuessType return une...
Sep/20
About retrieving the...
Sep/07
How to find specific...
Aug/15
How to get data depe...
Jul/07
Identify RVA data in...
May/06


Recent Forum Posts
Finding the procedur...
rolEYder
Question about debbu...
rolEYder
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
OOP_RE tool available?
Bl4ckm4n


Recent Blog Entries
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...

oleavr
Jun/25
Build a debugger in 5 minutes

More ...


Recent Blog Comments
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...

capadleman on:
Jun/19
Using NtCreateThreadEx for ...

More ...


Imagery
SoySauce Blueprint
Jun 6, 2008

[+] expand

View Gallery (11) / Submit