Tag: Memory Monitoring

  • Windows Memory Monitoring

    For memory monitoring on Windows, especially targeting virtual memory allocations, page faults, and memory protection changes, there are several safer alternatives that don’t rely on hooking or unsupported mechanisms like ALPC. Below are some methods to achieve memory monitoring at the kernel level and provide user-mode notifications without performance bottlenecks.

    Key Approaches for Memory Monitoring

    1. Process and Thread Notifications: Use PsSetCreateProcessNotifyRoutineEx and PsSetCreateThreadNotifyRoutine to monitor process and thread creation events.
    2. Page Fault Monitoring: While page faults aren’t directly exposed via a kernel API, monitoring changes in memory protections (like NtProtectVirtualMemory) could serve as a way to track suspicious memory activities.
    3. Memory Region Monitoring: You can periodically check for virtual memory allocations by inspecting the memory regions of processes using functions like ZwQueryVirtualMemory.
    4. Filter Drivers: File system filter drivers can also be used to monitor specific file-related memory operations.

    Method 1: Virtual Memory Monitoring with Virtual Address Descriptors (VADs)

    While VADs (Virtual Address Descriptors) aren’t directly accessible through public APIs, you can inspect them indirectly in the kernel. This method requires deep knowledge of the Windows Memory Manager, but is the most efficient way to monitor memory changes.

    Method 2: Using PsSetLoadImageNotifyRoutine for Monitoring Memory-Mapped Files

    If you want to monitor memory allocations related to executable images or DLLs being loaded into memory, you can use the PsSetLoadImageNotifyRoutine function. This doesn’t give complete coverage of all memory operations but can be useful for monitoring memory-mapped files (like DLLs).

    VOID NTAPI LoadImageNotifyRoutine(
        PUNICODE_STRING FullImageName,
        HANDLE ProcessId,
        PIMAGE_INFO ImageInfo
    )
    {
        if (ImageInfo->SystemModeImage) {
            DbgPrint("System Mode Image Loaded: %wZ\n", FullImageName);
        } else {
            DbgPrint("User Mode Image Loaded: %wZ in Process %d\n", FullImageName, ProcessId);
        }
    }
    
    NTSTATUS DriverEntry(
        PDRIVER_OBJECT DriverObject,
        PUNICODE_STRING RegistryPath
    )
    {
        NTSTATUS status;
    
        // Register the image load notification routine
        status = PsSetLoadImageNotifyRoutine(LoadImageNotifyRoutine);
        if (!NT_SUCCESS(status)) {
            DbgPrint("Failed to register load image notify routine\n");
            return status;
        }
    
        DriverObject->DriverUnload = DriverUnload;
        return STATUS_SUCCESS;
    }
    
    VOID DriverUnload(PDRIVER_OBJECT DriverObject)
    {
        PsRemoveLoadImageNotifyRoutine(LoadImageNotifyRoutine);
        DbgPrint("Driver Unloaded\n");
    }

    Method 3: Periodic Virtual Memory Inspections

    You can use ZwQueryVirtualMemory to inspect the memory regions of a process periodically and gather information on virtual memory allocations, protection levels, and committed pages.

    Example of Using ZwQueryVirtualMemory:

    NTSTATUS QueryMemoryRegions(HANDLE ProcessHandle)
    {
        MEMORY_BASIC_INFORMATION memInfo;
        PVOID baseAddress = NULL;
        NTSTATUS status;
    
        while (NT_SUCCESS(status = ZwQueryVirtualMemory(
                ProcessHandle, baseAddress, MemoryBasicInformation, &memInfo, sizeof(memInfo), NULL)))
        {
            DbgPrint("BaseAddress: %p, RegionSize: %llu, State: %x, Protect: %x\n",
                     memInfo.BaseAddress, memInfo.RegionSize, memInfo.State, memInfo.Protect);
    
            // Move to the next memory region
            baseAddress = (PBYTE)baseAddress + memInfo.RegionSize;
        }
    
        return status;
    }

    Method 4: ETW (Event Tracing for Windows) for Page Faults and Memory Monitoring

    ETW is a powerful mechanism in Windows that can be used for tracing low-level system events, including memory-related operations such as page faults.

    1. Enable page fault and virtual memory event tracing using ETW.
    2. Collect and analyze ETW events for memory operations.

    Example of Setting Up ETW for Memory Operations:

    Using ETW, you can set up listeners for specific system events related to memory, such as:

    • Page faults
    • Memory allocations
    • Memory protection changes

    This requires using the Windows Performance Toolkit or programmatically setting up ETW sessions via the EventTrace APIs.

    Efficient Communication to User Mode

    To handle the high volume of kernel events without performance bottlenecks, you can use one of the following mechanisms for notifying user-mode applications:

    1. I/O Completion Ports: If you have a user-mode application that interacts with your driver, I/O completion ports are an efficient way to handle asynchronous notifications for memory changes.
    2. APCs (Asynchronous Procedure Calls): APCs allow you to execute code in the context of a user-mode thread. This is useful for delivering memory change notifications in a non-blocking manner.
    3. Shared Memory: If the volume of data is extremely high, you can create a shared memory region between your kernel-mode driver and user-mode application to pass information efficiently.

    Using APC for Memory Change Notifications

    In place of ALPC, you can use APC to notify user-mode applications about significant memory changes asynchronously. Here’s an outline of how to use APCs:

    1. Queue an APC to a user-mode thread when a memory protection change occurs.
    2. Execute APC in the user-mode thread context, passing memory-related information to user-mode.

    APC Example for Memory Change Notification

    VOID NTAPI MemoryChangeApcRoutine(
        PKAPC Apc,
        PKNORMAL_ROUTINE *NormalRoutine,
        PVOID *NormalContext,
        PVOID *SystemArgument1,
        PVOID *SystemArgument2
    )
    {
        // Log or notify about the memory change
        DbgPrint("Memory change APC triggered\n");
    
        // Cleanup APC
        ExFreePool(Apc);
    }
    
    VOID QueueMemoryChangeApc(PEPROCESS Process)
    {
        PKAPC Apc = (PKAPC)ExAllocatePool(NonPagedPool, sizeof(KAPC));
        if (!Apc) {
            return;
        }
    
        // Initialize and queue APC
        KeInitializeApc(Apc,
                        PsGetCurrentThread(), // Thread to queue the APC to
                        OriginalApcEnvironment,
                        (PKKERNEL_ROUTINE)MemoryChangeApcRoutine,
                        NULL, // Rundown routine
                        NULL, // Normal routine
                        KernelMode,
                        NULL);
    
        // Insert APC into the queue
        if (!KeInsertQueueApc(Apc, NULL, NULL, 0)) {
            ExFreePool(Apc);
        }
    }

    Conclusion

    For monitoring memory allocations, protection changes, and page faults in Windows 11, without using methods like SSDT hooking, you can:

    • Use PsSetCreateProcessNotifyRoutineEx and PsSetLoadImageNotifyRoutine for high-level monitoring of process and image loads.
    • Use ZwQueryVirtualMemory to inspect memory regions for allocation and protection changes.
    • Use APCs to asynchronously notify user-mode applications of memory changes without impacting performance.
    • Consider ETW for detailed tracing of memory events like page faults.

    These techniques help monitor memory efficiently without causing performance bottlenecks or violating Windows’ kernel integrity protections.For memory monitoring on Windows, especially targeting virtual memory allocations, page faults, and memory protection changes, there are several safer alternatives that don’t rely on hooking or unsupported mechanisms like ALPC. Below are some methods to achieve memory monitoring at the kernel level and provide user-mode notifications without performance bottlenecks.

    Key Approaches for Memory Monitoring

    1. Process and Thread Notifications: Use PsSetCreateProcessNotifyRoutineEx and PsSetCreateThreadNotifyRoutine to monitor process and thread creation events.
    2. Page Fault Monitoring: While page faults aren’t directly exposed via a kernel API, monitoring changes in memory protections (like NtProtectVirtualMemory) could serve as a way to track suspicious memory activities.
    3. Memory Region Monitoring: You can periodically check for virtual memory allocations by inspecting the memory regions of processes using functions like ZwQueryVirtualMemory.
    4. Filter Drivers: File system filter drivers can also be used to monitor specific file-related memory operations.

    Method 1: Virtual Memory Monitoring with Virtual Address Descriptors (VADs)

    While VADs (Virtual Address Descriptors) aren’t directly accessible through public APIs, you can inspect them indirectly in the kernel. This method requires deep knowledge of the Windows Memory Manager, but is the most efficient way to monitor memory changes.

    Method 2: Using PsSetLoadImageNotifyRoutine for Monitoring Memory-Mapped Files

    If you want to monitor memory allocations related to executable images or DLLs being loaded into memory, you can use the PsSetLoadImageNotifyRoutine function. This doesn’t give complete coverage of all memory operations but can be useful for monitoring memory-mapped files (like DLLs).

    VOID NTAPI LoadImageNotifyRoutine(
        PUNICODE_STRING FullImageName,
        HANDLE ProcessId,
        PIMAGE_INFO ImageInfo
    )
    {
        if (ImageInfo->SystemModeImage) {
            DbgPrint("System Mode Image Loaded: %wZ\n", FullImageName);
        } else {
            DbgPrint("User Mode Image Loaded: %wZ in Process %d\n", FullImageName, ProcessId);
        }
    }
    
    NTSTATUS DriverEntry(
        PDRIVER_OBJECT DriverObject,
        PUNICODE_STRING RegistryPath
    )
    {
        NTSTATUS status;
    
        // Register the image load notification routine
        status = PsSetLoadImageNotifyRoutine(LoadImageNotifyRoutine);
        if (!NT_SUCCESS(status)) {
            DbgPrint("Failed to register load image notify routine\n");
            return status;
        }
    
        DriverObject->DriverUnload = DriverUnload;
        return STATUS_SUCCESS;
    }
    
    VOID DriverUnload(PDRIVER_OBJECT DriverObject)
    {
        PsRemoveLoadImageNotifyRoutine(LoadImageNotifyRoutine);
        DbgPrint("Driver Unloaded\n");
    }

    Method 3: Periodic Virtual Memory Inspections

    You can use ZwQueryVirtualMemory to inspect the memory regions of a process periodically and gather information on virtual memory allocations, protection levels, and committed pages.

    Example of Using ZwQueryVirtualMemory:

    NTSTATUS QueryMemoryRegions(HANDLE ProcessHandle)
    {
        MEMORY_BASIC_INFORMATION memInfo;
        PVOID baseAddress = NULL;
        NTSTATUS status;
    
        while (NT_SUCCESS(status = ZwQueryVirtualMemory(
                ProcessHandle, baseAddress, MemoryBasicInformation, &memInfo, sizeof(memInfo), NULL)))
        {
            DbgPrint("BaseAddress: %p, RegionSize: %llu, State: %x, Protect: %x\n",
                     memInfo.BaseAddress, memInfo.RegionSize, memInfo.State, memInfo.Protect);
    
            // Move to the next memory region
            baseAddress = (PBYTE)baseAddress + memInfo.RegionSize;
        }
    
        return status;
    }

    Method 4: ETW (Event Tracing for Windows) for Page Faults and Memory Monitoring

    ETW is a powerful mechanism in Windows that can be used for tracing low-level system events, including memory-related operations such as page faults.

    1. Enable page fault and virtual memory event tracing using ETW.
    2. Collect and analyze ETW events for memory operations.

    Example of Setting Up ETW for Memory Operations:

    Using ETW, you can set up listeners for specific system events related to memory, such as:

    • Page faults
    • Memory allocations
    • Memory protection changes

    This requires using the Windows Performance Toolkit or programmatically setting up ETW sessions via the EventTrace APIs.

    Efficient Communication to User Mode

    To handle the high volume of kernel events without performance bottlenecks, you can use one of the following mechanisms for notifying user-mode applications:

    1. I/O Completion Ports: If you have a user-mode application that interacts with your driver, I/O completion ports are an efficient way to handle asynchronous notifications for memory changes.
    2. APCs (Asynchronous Procedure Calls): APCs allow you to execute code in the context of a user-mode thread. This is useful for delivering memory change notifications in a non-blocking manner.
    3. Shared Memory: If the volume of data is extremely high, you can create a shared memory region between your kernel-mode driver and user-mode application to pass information efficiently.

    Using APC for Memory Change Notifications

    In place of ALPC, you can use APC to notify user-mode applications about significant memory changes asynchronously. Here’s an outline of how to use APCs:

    1. Queue an APC to a user-mode thread when a memory protection change occurs.
    2. Execute APC in the user-mode thread context, passing memory-related information to user-mode.

    APC Example for Memory Change Notification

    VOID NTAPI MemoryChangeApcRoutine(
        PKAPC Apc,
        PKNORMAL_ROUTINE *NormalRoutine,
        PVOID *NormalContext,
        PVOID *SystemArgument1,
        PVOID *SystemArgument2
    )
    {
        // Log or notify about the memory change
        DbgPrint("Memory change APC triggered\n");
    
        // Cleanup APC
        ExFreePool(Apc);
    }
    
    VOID QueueMemoryChangeApc(PEPROCESS Process)
    {
        PKAPC Apc = (PKAPC)ExAllocatePool(NonPagedPool, sizeof(KAPC));
        if (!Apc) {
            return;
        }
    
        // Initialize and queue APC
        KeInitializeApc(Apc,
                        PsGetCurrentThread(), // Thread to queue the APC to
                        OriginalApcEnvironment,
                        (PKKERNEL_ROUTINE)MemoryChangeApcRoutine,
                        NULL, // Rundown routine
                        NULL, // Normal routine
                        KernelMode,
                        NULL);
    
        // Insert APC into the queue
        if (!KeInsertQueueApc(Apc, NULL, NULL, 0)) {
            ExFreePool(Apc);
        }
    }

    Conclusion

    For monitoring memory allocations, protection changes, and page faults in Windows 11, without using methods like SSDT hooking, you can:

    • Use PsSetCreateProcessNotifyRoutineEx and PsSetLoadImageNotifyRoutine for high-level monitoring of process and image loads.
    • Use ZwQueryVirtualMemory to inspect memory regions for allocation and protection changes.
    • Use APCs to asynchronously notify user-mode applications of memory changes without impacting performance.
    • Consider ETW for detailed tracing of memory events like page faults.

    These techniques help monitor memory efficiently without causing performance bottlenecks or violating Windows’ kernel integrity protections.