简体   繁体   English

从 C 中的 windows kernel 驱动程序读取注册表

[英]Reading registry from windows kernel driver in C

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.该驱动程序是一个 kernel 驱动程序,它设置为启动类型设置为SYSTEM ,因此所有服务和 WinAPI 不一定可用。

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 .我正在尝试使用RtlQueryRegistryValues function 从注册表中读取单个字符串值,但无论我做什么,我似乎都得到了相同的0xC0000034错误代码,它转换为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.根据 MSDN 上提供的文档,当路径参数与有效键不匹配,或者设置了特定标志并且不满足特定于该标志的条件时,从RtlQueryRegistryValues返回STATUS_OBJECT_NAME_NOT_FOUND 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.据我所知,注册表项实际上存在于我的测试机器中,并且我没有使用RTL_QUERY_REGISTRY_REQUIRED标志。

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 .我试图读取的注册表值位于HKEY_LOCAL_MACHINE/SOFTWARE/company/ProjectName下,我正在尝试读取默认值和名为parameter的 REG_SZ 值。 The call to RtlQueryRegistryValues is performed during the DriverEntry(...) stage of loading the driver.在加载驱动程序的 DriverEntry(...) 阶段执行对RtlQueryRegistryValues的调用。

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.我不知道我做错了什么,因为我是 kernel 驱动程序的新手,调试过程非常繁琐,我不确定我是否只是错误地引用了注册表值,或者是否在系统启动的这个阶段,注册表是完全可用的。

mydriver.c 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);
}

I'm not 100% sure but I suspect the registry hive for the Software subtree is just not loaded.我不是 100% 确定,但我怀疑软件子树的注册表配置单元没有加载。 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.驱动程序配置参数的正确位置是它自己的注册表项( \\Registry\\Machine\\System\\CurrentControlSet\\Services\\<DriverName>\\ ),它的路径甚至会传递给您的DriverEntry函数,因此您无需对其进行硬编码.

See also: Registry Trees and Keys for Devices and Drivers .另请参阅: 设备和驱动程序的注册表树和键

You could use the RegistryPath passed into DriverEntry like so:您可以像这样使用传递给DriverEntryRegistryPath

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 .在我的示例中,我正在读取ULONG但它可以是任何内容,想法是您尝试读取的地址位于地址pKeyInfo + pKeyInfo->DataOffset

Looks like the problem is with RTL_QUERY_REGISTRY_DIRECT.看起来问题出在 RTL_QUERY_REGISTRY_DIRECT 上。 According to documentation ( https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlqueryregistryvalues ) it requires RTL_QUERY_REGISTRY_TYPECHECK flag set as well.根据文档( https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlqueryregistryvalues ),它还需要设置 RTL_QUERY_REGISTRY_TYPECHECK 标志。 Flag RTL_QUERY_REGISTRY_TYPECHECK, in turn, requires DefaultType and DefaultSize (of PKEY_VALUE_FULL_INFORMATION) set.反过来,标志 RTL_QUERY_REGISTRY_TYPECHECK 需要设置 DefaultType 和 DefaultSize(属于 PKEY_VALUE_FULL_INFORMATION)。 At least that is what I've found which is not according to documentation.至少那是我发现的不符合文档的内容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM