Reputation: 1210
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);
}
Upvotes: 3
Views: 7294
Reputation: 25278
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.
Upvotes: 7
Reputation: 11
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.
Upvotes: 1
Reputation: 114
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
.
Upvotes: 3