Windows 内核读写注册表

看书实现的代码,遇到了好多问题,做了详细的代码注释。另外在排查问题的过程中看到了一个操作注册表的通用的 c 文件,貌似是一个硬件驱动的 ftp 地址。ftp://ftp.lantronix.com/priv/cpr/Lantronix/4.3/4.3.0.0/Debug/DriverSource/registry.c

#include <ntddk.h>

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
    NTSTATUS status;
    HANDLE KeyHandle = NULL;
    OBJECT_ATTRIBUTES objAttr = { 0 };
    UNICODE_STRING KeyPath = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
    UNICODE_STRING ValueName = RTL_CONSTANT_STRING(L"RegisteredOwner");
    PKEY_VALUE_PARTIAL_INFORMATION pKeyValueInformation;
    ULONG ResultLength;

    InitializeObjectAttributes(&objAttr, &KeyPath, OBJ_CASE_INSENSITIVE, NULL, NULL);

    status = ZwOpenKey(&KeyHandle, KEY_READ, &objAttr);
    if (!NT_SUCCESS(status))
    {
        return STATUS_UNSUCCESSFUL;
    }

    /* 写注册表代码
    UNICODE_STRING ValueName = RTL_CONSTANT_STRING(L"DriverWrited");
    PWCHAR Value = L"My Test Value";

    status = ZwSetValueKey(KeyHandle, &ValueName, 0, REG_SZ, Value,
        (wcslen(Value) + 1) * sizeof(WCHAR));
    if (!NT_SUCCESS(status))
    {
        ZwClose(KeyHandle);
        return STATUS_UNSUCCESSFUL;
    }*/

    // 试探性的调用,主要根据返回值得到实际 buffer 的大小
    status = ZwQueryValueKey(
        KeyHandle,                  // key句柄
        &ValueName,                 // 要获取的键名
        KeyValuePartialInformation, // 获取类型和值
        NULL,                       // 首次 buffer 传 NULL 为了其失败返回实际需要空间
        0,                          // buffer 长度传 0,同上目的
        &ResultLength               // 用来返回实际需要空间的变量
        );

    // 如果说实际需要的长度比 Length 要大,那么返回 STATUS_BUFFER_ OVERFLOW
    // 或者 STATUS_BUFFER_TOO_SMALL。如果成功读出了全部数据,那么返回 STATUS_SUCCESS。
    // 其他的情况,返回一个错误码。
    if (!NT_SUCCESS(status) && 
        status != STATUS_BUFFER_OVERFLOW && 
        status != STATUS_BUFFER_TOO_SMALL)
    {
        ZwClose(KeyHandle);
    }

    // 根据实际返回所需的 buffer 大小分配空间
    pKeyValueInformation = (KEY_VALUE_PARTIAL_INFORMATION*)
        ExAllocatePool(NonPagedPool, ResultLength);
    if (NULL == pKeyValueInformation)
    {
        status = STATUS_INSUFFICIENT_RESOURCES;
        ZwClose(KeyHandle);
    }

    // 继续读取
    status = ZwQueryValueKey(
        KeyHandle,
        &ValueName,
        KeyValuePartialInformation,
        pKeyValueInformation,       // 传递已经分配了空间的 buffer
        ResultLength,               // 传递上一次 ZwQueryValueKey 返回的实际大小值
        &ResultLength
        );

    UNICODE_STRING ResultString;
    RtlInitUnicodeString(&ResultString, (PCWSTR)pKeyValueInformation->Data);
    ResultString.Length = (USHORT)pKeyValueInformation->DataLength;
    ResultString.MaximumLength = (USHORT)pKeyValueInformation->DataLength;

    KdPrint(("pKeyValueInformation.Data = %S\r\n", pKeyValueInformation->Data));
    KdPrint(("ResultString = %wZ, Length = %ld\r\n", &ResultString, ResultString.Length));

    ExFreePool((VOID*)pKeyValueInformation);

    ZwClose(KeyHandle);

    return STATUS_UNSUCCESSFUL;
}

上面链接中的代码,保存下来以防无法访问。

// Registry.c
//
// 
// Requires DDK Only
// File created on 2/2/2005
//

#include "pch.h"
#ifdef CPR_WMI_TRACE
#include "Registry.tmh"
#endif

///////////////////////////////////////////////////////////////////////////////////////////////////
//  CprRegQueryValueKey
//      Queries the value of a value key in the registry
//
//  Arguments:
//      IN  RegKeyHandle
//              Handle to the root key
//
//      IN  SubKeyName
//              Optional subkey path string
//
//      IN  ValueName
//              Value name string
//
//      IN OUT RegValuePtr
//              Pointer to buffer that will contain the
//              registry value.
//
//  Return Value:
//      NTSTATUS
//
NTSTATUS CprRegQueryValueKey(
    IN  HANDLE      RegKeyHandle,
    IN  PWSTR       SubKeyName,
    IN  PWSTR       ValueName,
    IN OUT PVOID    RegValuePtr
    )
{
    NTSTATUS                        status;
    PKEY_VALUE_PARTIAL_INFORMATION  buffer;
    ULONG                           length;
    UNICODE_STRING                  regPath;
    UNICODE_STRING                  name;
    OBJECT_ATTRIBUTES               objAttributes;
    HANDLE                          hReg;
    BOOLEAN                         bFreeHandle;

    // Callers of ZwQueryValueKey must be at PASSIVE_LEVEL IRQL
    ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);

    //ASSERT(RegKeyHandle != NULL);
    ASSERT(ValueName != NULL);
    ASSERT(RegValuePtr != NULL);

    buffer = NULL;
    bFreeHandle = FALSE;

    do
    {
        // Check for subkey path
        if (SubKeyName != NULL)
        {
            // Open a new handle
            RtlInitUnicodeString(&regPath, SubKeyName);

            // Initialize a new object attributes
            InitializeObjectAttributes(
                &objAttributes,
                &regPath,
                OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
                RegKeyHandle,
                NULL
                );

            status = ZwOpenKey(&hReg, KEY_ALL_ACCESS, &objAttributes);

            if (!NT_SUCCESS(status))
            {
                CprDebugPrint1(NULL, DBG_PNP, DBG_WARN, __FUNCTION__ ": ZwOpenKey failed %x", status);
                break;
            }

            // Indicate that we need to free a handle here
            bFreeHandle = TRUE;
        }
        else
        {
            hReg = RegKeyHandle;
        }

        RtlInitUnicodeString(&name, ValueName);

        status = ZwQueryValueKey(
                    hReg,
                    &name,
                    KeyValuePartialInformation,
                    NULL,
                    0,
                    &length
                    );

        if ((status != STATUS_BUFFER_TOO_SMALL) && (status != STATUS_BUFFER_OVERFLOW))
        {
            CprDebugPrint1(NULL, DBG_PNP, DBG_WARN, __FUNCTION__ ": ZwQueryValueKey failed %x", status);
            break;
        }

        buffer = 
            (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePoolWithTag(NonPagedPool, length, CPR_POOL_TAG_REG_KEY);

        if (buffer == NULL)
        {
            status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }

        status = ZwQueryValueKey(
                    hReg,
                    &name,
                    KeyValuePartialInformation,
                    buffer,
                    length,
                    &length
                    );

        if (!NT_SUCCESS(status))
        {
            CprDebugPrint1(NULL, DBG_PNP, DBG_WARN, __FUNCTION__ ": ZwQueryValueKey failed %x", status);
            break;
        }
    }
    while (FALSE);

    // Allocate a buffer to return
    if (NT_SUCCESS(status))
    {
        char *tmpPtr;

        // Zero terminate strings just for ease of handling
        if ((buffer->Type == REG_EXPAND_SZ) ||
            (buffer->Type == REG_MULTI_SZ) ||
            (buffer->Type == REG_SZ))
        {
            // Allocate buffer
            tmpPtr = ExAllocatePoolWithTag(
                                        PagedPool, 
                                        buffer->DataLength + sizeof(WCHAR), 
                                        CPR_POOL_TAG_REG_QUERY
                                        );

            if (tmpPtr == NULL)
            {
                status = STATUS_INSUFFICIENT_RESOURCES;
            }
            else
            {
                RtlZeroMemory(tmpPtr, buffer->DataLength + sizeof(WCHAR));
                // Copy the registry data to the pointer in the return buffer
                RtlCopyMemory(tmpPtr, (PVOID)buffer->Data, buffer->DataLength);
                *(char**)RegValuePtr = tmpPtr;
            }
        }
        else
        {
            // Copy the registry data to the return buffer
            RtlCopyMemory(RegValuePtr, (PVOID)buffer->Data, buffer->DataLength);
        }
    }

    if (buffer != NULL)
    {
        // Free our allocated memory
        ExFreePool(buffer);
    }

    if (bFreeHandle)
    {
        // Close our reg key handle
        ZwClose(hReg);
    }

    return status;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
//  CprRegEnumerateKeys
//      Enumerates and print names of subkeys using a given registry key handle.
//
//  Arguments:
//      IN  RegKeyHandle
//              Handle to root key
//
//  Return Value:
//      none
//
VOID CprRegEnumerateKeys(
    IN  HANDLE RegKeyHandle
    )
{
    NTSTATUS                status;
    ULONG                   index;
    PKEY_BASIC_INFORMATION  regBuffer;
    PWCHAR                  nameBuffer;
    ULONG                   length;

    status = STATUS_SUCCESS;
    index = 0;
    regBuffer = NULL;
    nameBuffer = NULL;

    while (status != STATUS_NO_MORE_ENTRIES)
    {
        // Get the buffer size necessary
        status = ZwEnumerateKey(
                    RegKeyHandle,
                    index,
                    KeyBasicInformation,
                    NULL,
                    0,
                    &length
                    );

        if ((status != STATUS_BUFFER_TOO_SMALL) && (status != STATUS_BUFFER_OVERFLOW))
        {
            if (status != STATUS_NO_MORE_ENTRIES)
            {
                CprDebugPrint1(NULL, DBG_PNP, DBG_INFO, __FUNCTION__ ": ZwEnumerateKey failed %x", status);
            }
            else
            {
                CprDebugPrint1(NULL, DBG_PNP, DBG_INFO, __FUNCTION__ ": Enumerated %d keys", index);
            }

            break;
        }

        regBuffer = 
            (PKEY_BASIC_INFORMATION)ExAllocatePoolWithTag(NonPagedPool, length, CPR_POOL_TAG_ENUM_KEYS);

        if (regBuffer == NULL)
        {
            continue;
        }

        // Now actually attempt to get subkey info
        status = ZwEnumerateKey(
                    RegKeyHandle,
                    index,
                    KeyBasicInformation,
                    regBuffer,
                    length,
                    &length
                    );

        if (!NT_SUCCESS(status))
        {
            CprDebugPrint1(NULL, DBG_PNP, DBG_INFO, __FUNCTION__ ": ZwEnumerateKey failed %x", status);

            // Free our temporary storage
            ExFreePool(regBuffer);

            continue;
        }

        // Allocate a buffer for the display name
        nameBuffer = (PWCHAR)ExAllocatePoolWithTag(
                                    PagedPool, 
                                    regBuffer->NameLength + sizeof(WCHAR), 
                                    CPR_POOL_TAG_DISPLAY_NAME
                                    );

        if (nameBuffer == NULL)
        {
            // Free our temporary storage
            ExFreePool(regBuffer);

            continue;
        }

        // NULL terminate the string
        RtlZeroMemory(nameBuffer, regBuffer->NameLength + sizeof(WCHAR));

        // Copy the name over
        RtlCopyMemory(nameBuffer, regBuffer->Name, regBuffer->NameLength);

        CprDebugPrint1(NULL, DBG_PNP, DBG_INFO, __FUNCTION__ ": ZwEnumerateKey returned %S", nameBuffer);

        // Free both buffers
        ExFreePool(regBuffer);
        ExFreePool(nameBuffer);

        // Increment our index
        ++index;
    }

    return;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
//  CprRegEnumerateValueKeys
//      Enumerates and print names of sub value keys using a given registry key handle.
//
//  Arguments:
//      IN  RegKeyHandle
//              Handle to root key
//
//  Return Value:
//      none
//
VOID CprRegEnumerateValueKeys(
    IN  HANDLE RegKeyHandle
    )
{
    NTSTATUS                        status;
    ULONG                           index;
    PKEY_VALUE_BASIC_INFORMATION    regBuffer;
    PWCHAR                          nameBuffer;
    ULONG                           length;

    status = STATUS_SUCCESS;
    index = 0;
    regBuffer = NULL;
    nameBuffer = NULL;

    while (status != STATUS_NO_MORE_ENTRIES)
    {
        // Get the buffer size necessary
        status = ZwEnumerateValueKey(
                    RegKeyHandle,
                    index,
                    KeyValueBasicInformation,
                    NULL,
                    0,
                    &length
                    );

        if ((status != STATUS_BUFFER_TOO_SMALL) && (status != STATUS_BUFFER_OVERFLOW))
        {
            if (status != STATUS_NO_MORE_ENTRIES)
            {
                CprDebugPrint1(NULL, DBG_PNP, DBG_INFO, __FUNCTION__ ": ZwEnumerateValueKey failed %x", status);
            }
            else
            {
                CprDebugPrint1(NULL, DBG_PNP, DBG_INFO, __FUNCTION__ ": Enumerated %d value keys", index);
            }

            break;
        }

        regBuffer = 
            (PKEY_VALUE_BASIC_INFORMATION)ExAllocatePoolWithTag(NonPagedPool, length, CPR_POOL_TAG_REG_BUF);

        if (regBuffer == NULL)
        {
            continue;
        }

        // Now actually attempt to get subkey info
        status = ZwEnumerateValueKey(
                    RegKeyHandle,
                    index,
                    KeyValueBasicInformation,
                    regBuffer,
                    length,
                    &length
                    );

        if (!NT_SUCCESS(status))
        {
            CprDebugPrint1(NULL, DBG_PNP, DBG_INFO, __FUNCTION__ ": ZwEnumerateValueKey failed %x", status);

            // Free our temporary storage
            ExFreePool(regBuffer);

            continue;
        }

        // Allocate a buffer for the display name
        nameBuffer = (PWCHAR)ExAllocatePoolWithTag(
                                    PagedPool, 
                                    regBuffer->NameLength + sizeof(WCHAR), 
                                    CPR_POOL_TAG_NAME_BUF
                                    );

        if (nameBuffer == NULL)
        {
            // Free our temporary storage
            ExFreePool(regBuffer);

            continue;
        }

        // NULL terminate the string
        RtlZeroMemory(nameBuffer, regBuffer->NameLength + sizeof(WCHAR));

        // Copy the name over
        RtlCopyMemory(nameBuffer, regBuffer->Name, regBuffer->NameLength);

        CprDebugPrint1(NULL, DBG_PNP, DBG_INFO, __FUNCTION__ ": ZwEnumerateValueKey returned %S", nameBuffer);

        // Free both buffers
        ExFreePool(regBuffer);
        ExFreePool(nameBuffer);

        // Increment our index
        ++index;
    }

    return;
}

说说你的想法