In this Blog I will explain my approach for solving one of the exercises from Practical Reverse Engineering Book, which is enumerating the loaded module list every 10 minutes.

So after searching for the method to get all loaded Drivers on the system, I found there is an easy way you can to do it from user-mode (1) but I to do it using kernel mode driver and I want it to be stable on all windows versions. After searching for a while, I found there is a List in the Kernel space called _RTL_PROCESS_MODULES contain all loaded Drivers on the System. similar to the _PEB_LDR_DATA List present in each process describing Loaded DLL.

the _RTL_PROCESS_MODULES list exist in the undocumented valuable DriverSection in the DriverObject structure, I tried this technique and it worked on 0x64 bit version of windows but crashed on the 0x86 bit version (probably my definition was not very accurate on the 0x86 bit version), then I remembered there is a windows API ZwQuerySystemInformation and that it can get lots of information like running process, loaded Drivers,..etc so I used this technique, in the Driver Entry point I initialized a DPC and created a timer that will be triggered every 10 min and execute the DPC function, then in the DPC I used the ZwQuerySystemInformation to enumerate the loaded Drivers. I tested on different version of windows, and it worked normally (GitHub)

typedef struct _RTL_PROCESS_MODULE_INFORMATION
{
    HANDLE Section;
    PVOID MappedBase;
    PVOID ImageBase;
    ULONG ImageSize;
    ULONG Flags;
    USHORT LoadOrderIndex;
    USHORT InitOrderIndex;
    USHORT LoadCount;
    USHORT OffsetToFileName;
    UCHAR FullPathName[256];
} RTL_PROCESS_MODULE_INFORMATION, * PRTL_PROCESS_MODULE_INFORMATION;

typedef struct _RTL_PROCESS_MODULES
{
    ULONG NumberOfModules;
    RTL_PROCESS_MODULE_INFORMATION Modules[1];
} RTL_PROCESS_MODULES, * PRTL_PROCESS_MODULES;

1: kd> dt nt!_DRIVER_OBJECT
   +0x000 Type             : Int2B
   +0x002 Size             : Int2B
   +0x008 DeviceObject     : Ptr64 _DEVICE_OBJECT
   +0x010 Flags            : Uint4B
   +0x018 DriverStart      : Ptr64 Void
   +0x020 DriverSize       : Uint4B
   +0x028 DriverSection    : Ptr64 Void                     // a pointer to the _RTL_PROCESS_MODULES list structure
   +0x030 DriverExtension  : Ptr64 _DRIVER_EXTENSION
   +0x038 DriverName       : _UNICODE_STRING
   +0x048 HardwareDatabase : Ptr64 _UNICODE_STRING
   +0x050 FastIoDispatch   : Ptr64 _FAST_IO_DISPATCH
   +0x058 DriverInit       : Ptr64     long 
   +0x060 DriverStartIo    : Ptr64     void 
   +0x068 DriverUnload     : Ptr64     void 
   +0x070 MajorFunction    : [28] Ptr64     long 

0:007> dt ntdll!_PEB_LDR_DATA
   +0x000 Length           : Uint4B
   +0x004 Initialized      : UChar
   +0x008 SsHandle         : Ptr64 Void
   +0x010 InLoadOrderModuleList : _LIST_ENTRY
   +0x020 InMemoryOrderModuleList : _LIST_ENTRY
   +0x030 InInitializationOrderModuleList : _LIST_ENTRY
   +0x040 EntryInProgress  : Ptr64 Void
   +0x048 ShutdownInProgress : UChar
   +0x050 ShutdownThreadId : Ptr64 Void
VOID
EnumerateLoadedDriverDPC(
    PKDPC,
    PVOID,
    PVOID,
    PVOID
)
{
    ULONG BufferSize = 0;
    NTSTATUS Status = STATUS_SUCCESS;

    Status = ZwQuerySystemInformation(SystemModuleInformation, NULL, 0, &BufferSize);

    RTL_PROCESS_MODULES* InformationData = (RTL_PROCESS_MODULES*)ExAllocatePool(NonPagedPool, BufferSize);
    if (!InformationData) {
        DbgPrint("Driver Failed to Allocate memory\n ");
        return;
    }
    RtlZeroMemory(InformationData, BufferSize);

    Status = ZwQuerySystemInformation(SystemModuleInformation, InformationData, BufferSize, &BufferSize);

    if (!NT_SUCCESS(Status)) {

        DbgPrint("ZwQuerySystemInformation Failed Error Code %x\n", Status);
        return;
    }

    DbgPrint("Start Enumerating Loaded Drivers\n");

    for (ULONG i = 0; i < InformationData->NumberOfModules; i++) {
        DbgPrint("Driver %s at Base Address %p \n", InformationData->Modules[i].FullPathName, InformationData->Modules[i].ImageBase);
    }

}