#include <ntddk.h>
#include "process_monitor_driver.h"

PROCESS_MONITOR_CONFIG g_ProcessMonitorConfig = {
    PROCESS_MONITOR_ENABLED_DEFAULT,
    PROCESS_MONITOR_LOG_LEVEL_DEFAULT
};

static VOID LogStatus(_In_ ULONG Level, _In_z_ PCSTR Message)
{
    if (g_ProcessMonitorConfig.LogLevel >= Level) {
        DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s", Message);
    }
}

NTSTATUS ReadProcessMonitorConfig(_In_ PUNICODE_STRING RegistryPath)
{
    RTL_QUERY_REGISTRY_TABLE queryTable[3] = { 0 };
    NTSTATUS status;

    UNREFERENCED_PARAMETER(RegistryPath);

    g_ProcessMonitorConfig.Enabled = PROCESS_MONITOR_ENABLED_DEFAULT;
    g_ProcessMonitorConfig.LogLevel = PROCESS_MONITOR_LOG_LEVEL_DEFAULT;

    queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
    queryTable[0].Name = PROCESS_MONITOR_ENABLED_VALUE;
    queryTable[0].EntryContext = &g_ProcessMonitorConfig.Enabled;
    queryTable[0].DefaultType = REG_DWORD;
    queryTable[0].DefaultData = &g_ProcessMonitorConfig.Enabled;
    queryTable[0].DefaultLength = sizeof(g_ProcessMonitorConfig.Enabled);

    queryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
    queryTable[1].Name = PROCESS_MONITOR_LOGLEVEL_VALUE;
    queryTable[1].EntryContext = &g_ProcessMonitorConfig.LogLevel;
    queryTable[1].DefaultType = REG_DWORD;
    queryTable[1].DefaultData = &g_ProcessMonitorConfig.LogLevel;
    queryTable[1].DefaultLength = sizeof(g_ProcessMonitorConfig.LogLevel);

    status = RtlQueryRegistryValues(
        RTL_REGISTRY_ABSOLUTE,
        RegistryPath->Buffer,
        queryTable,
        NULL,
        NULL);

    if (!NT_SUCCESS(status)) {
        DbgPrintEx(
            DPFLTR_IHVDRIVER_ID,
            DPFLTR_WARNING_LEVEL,
            "[ProcessMonitor] Registry configuration unavailable (0x%08X); using defaults.\n",
            status);
        return status;
    }

    DbgPrintEx(
        DPFLTR_IHVDRIVER_ID,
        DPFLTR_INFO_LEVEL,
        "[ProcessMonitor] Config: Enabled=%lu LogLevel=%lu\n",
        g_ProcessMonitorConfig.Enabled,
        g_ProcessMonitorConfig.LogLevel);

    return STATUS_SUCCESS;
}

VOID NTAPI ProcessNotifyRoutineEx(
    _Inout_ PEPROCESS Process,
    _In_ HANDLE ProcessId,
    _Inout_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo)
{
    UNREFERENCED_PARAMETER(Process);

    if (!g_ProcessMonitorConfig.Enabled) {
        return;
    }

    if (CreateInfo == NULL) {
        if (g_ProcessMonitorConfig.LogLevel >= 2) {
            DbgPrintEx(
                DPFLTR_IHVDRIVER_ID,
                DPFLTR_INFO_LEVEL,
                "[ProcessMonitor] Process exited: PID=%lu\n",
                HandleToULong(ProcessId));
        }
        return;
    }

    if (g_ProcessMonitorConfig.LogLevel >= 1) {
        UNICODE_STRING unavailable = RTL_CONSTANT_STRING(L"<n/a>");
        PCUNICODE_STRING imageName = CreateInfo->ImageFileName ? CreateInfo->ImageFileName : &unavailable;
        PCUNICODE_STRING commandLine = CreateInfo->CommandLine ? CreateInfo->CommandLine : &unavailable;

        DbgPrintEx(
            DPFLTR_IHVDRIVER_ID,
            DPFLTR_INFO_LEVEL,
            "[ProcessMonitor] Process created: PID=%lu Image=%wZ CommandLine=%wZ\n",
            HandleToULong(ProcessId),
            imageName,
            commandLine);
    }
}

VOID NTAPI ThreadNotifyRoutine(
    _In_ HANDLE ProcessId,
    _In_ HANDLE ThreadId,
    _In_ BOOLEAN Create)
{
    if (!g_ProcessMonitorConfig.Enabled || g_ProcessMonitorConfig.LogLevel < 3) {
        return;
    }

    DbgPrintEx(
        DPFLTR_IHVDRIVER_ID,
        DPFLTR_INFO_LEVEL,
        "[ProcessMonitor] Thread %s: PID=%lu TID=%lu\n",
        Create ? "created" : "exited",
        HandleToULong(ProcessId),
        HandleToULong(ThreadId));
}

NTSTATUS DriverEntry(
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ PUNICODE_STRING RegistryPath)
{
    NTSTATUS status;

    DriverObject->DriverUnload = DriverUnload;

    (VOID)ReadProcessMonitorConfig(RegistryPath);

    status = PsSetCreateProcessNotifyRoutineEx(ProcessNotifyRoutineEx, FALSE);
    if (!NT_SUCCESS(status)) {
        DbgPrintEx(
            DPFLTR_IHVDRIVER_ID,
            DPFLTR_ERROR_LEVEL,
            "[ProcessMonitor] Process callback registration failed: 0x%08X\n",
            status);
        return status;
    }

    status = PsSetCreateThreadNotifyRoutine(ThreadNotifyRoutine);
    if (!NT_SUCCESS(status)) {
        PsSetCreateProcessNotifyRoutineEx(ProcessNotifyRoutineEx, TRUE);
        DbgPrintEx(
            DPFLTR_IHVDRIVER_ID,
            DPFLTR_ERROR_LEVEL,
            "[ProcessMonitor] Thread callback registration failed: 0x%08X\n",
            status);
        return status;
    }

    LogStatus(1, "[ProcessMonitor] Driver loaded successfully.\n");
    return STATUS_SUCCESS;
}

VOID DriverUnload(_In_ PDRIVER_OBJECT DriverObject)
{
    UNREFERENCED_PARAMETER(DriverObject);

    (VOID)PsSetCreateProcessNotifyRoutineEx(ProcessNotifyRoutineEx, TRUE);
    (VOID)PsRemoveCreateThreadNotifyRoutine(ThreadNotifyRoutine);

    LogStatus(1, "[ProcessMonitor] Driver unloaded.\n");
}
