Question

I need to read settings from the registry (preferably) or from a file. The driver is a kernel driver that is set to start with type of start set to SYSTEM, so all services and WinAPIs are not necessarily available.

I'm attempting to use the RtlQueryRegistryValues function in order to read a single String value from the registry, but whatever I do I seem to get the same 0xC0000034 error code back which translate to STATUS_OBJECT_NAME_NOT_FOUND.

According to the documentation available at MSDN STATUS_OBJECT_NAME_NOT_FOUND is returned from RtlQueryRegistryValues when the path parameter does not match a valid key, or a specific flag is set and conditions specific to that flag is not met. As far as I can tell the registry keys are actually present in my test machine and I'm not using the RTL_QUERY_REGISTRY_REQUIRED flag.

The registry values I'm attempting to read is located under HKEY_LOCAL_MACHINE/SOFTWARE/company/ProjectName, I'm attempting to read both the default value and a REG_SZ value named parameter. The call to RtlQueryRegistryValues is performed during the DriverEntry(...) stage of loading the driver.

I can't figure out what it is that I'm doing wrong, and since I'm new to kernel drivers and the debugging process is quite tedious I'm not sure whether or not I just refer to the registry values incorrectly or if the registry is available at all during this stage of the system boot.

mydriver.c

NTSTATUS DriverEntry(...) {
    NTSTATUS regStatus = 0;
    UNICODE_STRING data;
    RTL_QUERY_REGISTRY_TABLE query[2];
    WCHAR* regPath = L"\\Registry\\Machine\\SOFTWARE\\Company\\ProjectName";

    RtlZeroMemory(query, sizeof(RTL_QUERY_REGISTRY_TABLE) * 2);

    data.Buffer = NULL;
    data.MaximumLength = 0;
    data.Length = 0;

    // query[0].Name = L"Parameter";
    query[0].Name = L""; // L"" refers to the default value
    query[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
    query[0].EntryContext = &data;

    regStatus = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, regPath, query, NULL, NULL);

    DebugPrint("regStatus: %lx\n", regStatus);
    DebugPrint("data: %wZ\n", &data);
}
Was it helpful?

Solution

I'm not 100% sure but I suspect the registry hive for the Software subtree is just not loaded. Why are you trying to access it anyway? The proper place for driver config parameters is its own registry key (\Registry\Machine\System\CurrentControlSet\Services\<DriverName>\) and the path to it is even passed to your DriverEntry function so you don't need to hardcode it.

See also: Registry Trees and Keys for Devices and Drivers.

OTHER TIPS

You could use the RegistryPath passed into DriverEntry like so:

NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
    // ...

    NTSTATUS ntStatus = STATUS_SUCCESS;

    UNICODE_STRING valueName = RTL_CONSTANT_STRING(L"<YOUR_VALUE_NAME>");
    HANDLE hRegistryKey;
    PKEY_VALUE_FULL_INFORMATION pKeyInfo = nullptr;

    // Create object attributes for registry key querying
    OBJECT_ATTRIBUTES ObjectAttributes = { 0 };
    InitializeObjectAttributes(&ObjectAttributes, RegistryPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);

    do {
        ntStatus = ZwOpenKey(&hRegistryKey, KEY_QUERY_VALUE, &ObjectAttributes);
        if (!NT_SUCCESS(ntStatus)) {
            KdPrint((DRIVER_PREFIX "Registry key open failed (0x%08X)\n", ntStatus));
            break;
        }

        ULONG ulKeyInfoSize;
        ULONG ulKeyInfoSizeNeeded = GetKeyInfoSize(hRegistryKey, &valueName);
        if (ulKeyInfoSizeNeeded == 0) {
            KdPrint((DRIVER_PREFIX "Value not found\n"));
            break;
        }
        ulKeyInfoSize = ulKeyInfoSizeNeeded;

        pKeyInfo = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePoolWithTag(NonPagedPool, ulKeyInfoSize, DRIVER_TAG);
        if (pKeyInfo == nullptr) {
            KdPrint((DRIVER_PREFIX "Could not allocate memory for KeyValueInfo\n"));
            break;
        }
        RtlZeroMemory(pKeyInfo, ulKeyInfoSize);

        ntStatus = ZwQueryValueKey(hRegistryKey, &valueName, KeyValueFullInformation, pKeyInfo, ulKeyInfoSize, &ulKeyInfoSizeNeeded);
        if (!NT_SUCCESS(ntStatus) || ulKeyInfoSize != ulKeyInfoSizeNeeded) {
            KdPrint((DRIVER_PREFIX "Registry value querying failed (0x%08X)\n", ntStatus));
            break;
        }
        
        // your data
        ULONG someLong = *(ULONG*)((ULONG_PTR)pKeyInfo + pKeyInfo->DataOffset);
    } while (false);
    
    // cleanup
    if (hRegistryKey) {
        ZwClose(hRegistryKey);
    }

    if (pKeyInfo) {
        ExFreePoolWithTag(pKeyInfo, DRIVER_TAG);
    }

    if (!NT_SUCCESS(ntStatus)) {
        // Here you can set a default data if something failed it the way
    }

    // ...
}

ULONG GetKeyInfoSize(HANDLE hRegistryKey, PUNICODE_STRING pValueName) {
    NTSTATUS ntStatus = STATUS_SUCCESS;

    ULONG ulKeyInfoSizeNeeded;
    ntStatus = ZwQueryValueKey(hRegistryKey, pValueName, KeyValueFullInformation, 0, 0, &ulKeyInfoSizeNeeded);
    if (ntStatus == STATUS_BUFFER_TOO_SMALL || ntStatus == STATUS_BUFFER_OVERFLOW) {
        // Expected don't worry - when ZwQueryValueKey fails with one of the above statuses, it returns the size needed
        return ulKeyInfoSizeNeeded;
    }
    else {
        KdPrint((DRIVER_PREFIX "Could not get key info size (0x%08X)\n", ntStatus));
    }

    return 0;
}

In my example I'm reading an ULONG but it could be anything, the idea is that the address of what you're trying to read is at the address pKeyInfo + pKeyInfo->DataOffset.

It looks like the problem is with RTL_QUERY_REGISTRY_DIRECT.

According to the documentation, it requires the RTL_QUERY_REGISTRY_TYPECHECK flag set as well.

The RTL_QUERY_REGISTRY_TYPECHECK flag, in turn, requires DefaultType and DefaultSize (of PKEY_VALUE_FULL_INFORMATION) set. At least that is what I've found, which is not according to documentation.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top