Windows内核函数(3) - 内核模式下的注册表操作

转载自:http://mzf2008.blog.163.com/blog/static/355997862010111313716234/



注册表里的几个概念:

Windows内核函数(2) - 内核模式下的注册表操作 - Fly - 从C开始

 

 

1.       创建关闭注册表项

NTSTATUS 
  ZwCreateKey(
    OUT PHANDLE
  
KeyHandle,
    IN ACCESS_MASK  
DesiredAccess//访问权限,一般为KEY_ALL_ACCLESS
    IN POBJECT_ATTRIBUTES  
ObjectAttributes,
    IN ULONG  
TitleIndex//一般为NULL
    IN PUNICODE_STRING  
Class  OPTIONAL, //一般为NULL
    IN ULONG  
CreateOptions//一般为REG_OPTION_NON_VOLATILE
    OUT PULONG  
Disposition  OPTIONAL //返回是打开成功还是创建成功
    );

如果ZwCreateKey指定的项目不存在,则直接创建这个项目,并利用Disposition 参数返回REG_CREATED_NEW_KEY。如果该项目已经存在,Disposition 参数返回REG_OPENED_EXISTING_KEY

 

示例代码:

//新建注册表项为HKEY_LOCAL_MACHINE\SOFTWARE\Mzf

#define  MY_REG_SOFTWARE_KEY_NAME  L"\\Registry\\Machine\\Software\\Mzf"

#pragma INITCODE

VOID TetsKey()

{

         UNICODE_STRING string1;

         RtlInitUnicodeString(&string1, MY_REG_SOFTWARE_KEY_NAME);

        

         OBJECT_ATTRIBUTES  objAttribute;

         InitializeObjectAttributes(&objAttribute, &string1, OBJ_CASE_INSENSITIVE, NULL, NULL);

 

         HANDLE hKey;

         ULONG Des;

         NTSTATUS status = ZwCreateKey(&hKey, KEY_ALL_ACCESS, &objAttribute,

NULL, NULL, REG_OPTION_NON_VOLATILE, &Des);

 

         if (NT_SUCCESS(status))

         {

                   if (Des == REG_CREATED_NEW_KEY)

                   {

                            KdPrint(("新建注册表项!\n"));

                   }

                   else

                   {

                            KdPrint(("要创建的注册表项已经存在!\n"));

                   }

         }

        

//打开或创建注册表子项

         UNICODE_STRING string2;

         RtlInitUnicodeString(&string2, L"SubKey");

 

         OBJECT_ATTRIBUTES  subObjAttribute;      

         //注意最后第二个参数,为父键的句柄

         InitializeObjectAttributes( &subObjAttribute, &string2, OBJ_CASE_INSENSITIVE, hKey, NULL);

 

         HANDLE hSubKey;

         ULONG subDes;

         status = ZwCreateKey(&hSubKey, KEY_ALL_ACCESS, &subObjAttribute,

NULL, NULL, REG_OPTION_NON_VOLATILE, &subDes);

 

         if (NT_SUCCESS(status))

         {

                   if (subDes == REG_CREATED_NEW_KEY)

                   {

                            KdPrint(("新建注册表子项!\n"));

                   }

                   else

                   {

                            KdPrint(("要创建的注册表子项已经存在!\n"));

                   }

         }

         //关闭注册表句柄

         ZwClose(hKey);

         ZwClose(hSubKey);

}

 

 

2.       打开注册表项

NTSTATUS 
  ZwOpenKey(
    OUT PHANDLE
  
KeyHandle,
    IN ACCESS_MASK  
DesiredAccess,
    IN POBJECT_ATTRIBUTES  
ObjectAttributes
    );

 

 

3.  添加,修改注册表键值

注册表键值是以二元形式存储的,既“键名”和“键值”。通过键名设置键值,而键值可以分为以下几类:

分类

描述

REG_BINARY

键使用二进制存储

REG_SZ

键使用宽字符串,字符串以\0结尾

REG_EXPAND_SZ

该字符是扩展的字符,其它同上

REG_MULTI_SZ

键值存储多个字符串,每个字符串以\0隔开

REG_DWORD

键值用4字节整型存储

REG_QWORD

键值使用8字节存储

在添加和修改注册表键值的时候,要分类进行添加和修改。DDK提供了ZwSetValueKey函数来完成这个任务。

NTSTATUS 
  ZwSetValueKey(
    IN HANDLE
  
KeyHandle,
    IN PUNICODE_STRING  
ValueName//要新建或者修改的键名
    IN ULONG  
TitleIndex  OPTIONAL, //一般设为0
    IN ULONG  
Type,  //键值类型,上表中的一个
    IN PVOID  
Data//数据
    IN ULONG  
DataSize //记录键值数据大小
    );

在使用ZwSetValueKey函数的时候,如果指定的键名不存在,则直接创建。否则,对已有的键值进行修改。

 

示例代码:

#define  MY_REG_SOFTWARE_KEY_NAME  L"\\Registry\\Machine\\Software\\Mzf"

#pragma INITCODE

NTSTATUS TetsKey()

{

         //初始化注册表项

         UNICODE_STRING stringKey;

         RtlInitUnicodeString(&stringKey, MY_REG_SOFTWARE_KEY_NAME);

        

         //初始化OBJECT_ATTRIBUTES结构

         OBJECT_ATTRIBUTES  ObjectAttributes;

         InitializeObjectAttributes(&ObjectAttributes, &stringKey,

OBJ_CASE_INSENSITIVE, NULL, NULL);

 

         //打开注册表项

         HANDLE hKey;

         NTSTATUS status = ZwOpenKey(&hKey, GENERIC_ALL, &ObjectAttributes);

         if (!NT_SUCCESS(status))

         {

                   KdPrint(("打开注册表项失败!\n"));

                   return status;

         }

        

         //初始化valueName

         UNICODE_STRING valueName;

         RtlInitUnicodeString(&valueName, L"valueName REG_DWORD");

        

         //设置REG_DWORD键值

         ULONG ulValue = 100;

         status = ZwSetValueKey(hKey, &valueName, 0, REG_DWORD, &ulValue, sizeof(ulValue));

         if (!NT_SUCCESS(status))

         {

                   KdPrint(("设置1键值失败!\n"));

                   return status;

         }

 

         //设置REG_SZ键值

         RtlInitUnicodeString(&valueName, L"valueName REG_SZ");

 

         WCHAR* str = L"Hello World";

         status = ZwSetValueKey(hKey, &valueName, 0, REG_SZ, str, wcslen(str)*2 + 2);

         if (!NT_SUCCESS(status))

         {

                   KdPrint(("设置2键值失败!\n"));

                   return status;

         }

 

         //设置REG_BINARY键值

         RtlInitUnicodeString(&valueName, L"valueName REG_BINARY");

 

         UCHAR buffer[10];

         RtlFillMemory(buffer, sizeof(buffer), 0xFF);

         status = ZwSetValueKey(hKey, &valueName, 0, REG_BINARY, buffer, sizeof(buffer));

         if (!NT_SUCCESS(status))

         {

                   KdPrint(("设置3键值失败!\n"));

                   return status;

         }

 

         //关闭注册表句柄

         ZwClose(hKey);

         return status;

}

 

 

4.       查询注册表

NTSTATUS 
  ZwQueryValueKey(
    IN HANDLE
  
KeyHandle,
    IN PUNICODE_STRING  
ValueName//要查询的键名
    IN KEY_VALUE_INFORMATION_CLASS  
KeyValueInformationClass//查询的类别
    OUT PVOID  
KeyValueInformation//返回查询的信息
    IN ULONG  
Length//要查数据的长度
    OUT PULONG  
ResultLength //实际查询数据的长度
    );

在使用上述函数对注册表键值进行查询时,需要指定KeyValueInformationClass 的值,这个值可以是KeyValueBasicInformation KeyValueFullInformationKeyValuePartialInformation 

一般情况下,选择KeyValuePartialInformation就可以查询键值的数据了。它对应查询所返回的结果存放在一个KEY_VALUE_PARTIAL_INFORMATION结构体中。

typedef struct _KEY_VALUE_PARTIAL_INFORMATION {
  ULONG  
TitleIndex;
  ULONG  
Type;           //数据的类型
  ULONG  
DataLength;     //数据的长度
  UCHAR  
Data[1];        //数据指针,这里是变长的数据
} KEY_VALUE_PARTIAL_INFORMATION, *PKEY_VALUE_PARTIAL_INFORMATION;

 

KEY_VALUE_PARTIAL_INFORMATION结构体的长度不固定,所以首先需要确定这个长度。一般使用ZwQueryValueKey分为以下四个步骤:

(1)     ZwQueryValueKey获取这个数据结构的长度。

(2)分配如此长度的内存,用来查询

(3)再次调用ZwQueryValueKey,获取键值

(4)回收内存。

 

示例代码:

#define MY_REG_SOFTWARE_KEY_NAME  L"\\Registry\\Machine\\Software\\Mzf"

#pragma INITCODE

NTSTATUS TetsKey()

{

       //初始化注册表项

       UNICODE_STRING stringKey;

       RtlInitUnicodeString(&stringKey, MY_REG_SOFTWARE_KEY_NAME);

      

       //初始化OBJECT_ATTRIBUTES结构

       OBJECT_ATTRIBUTES  ObjectAttributes;

       InitializeObjectAttributes(&ObjectAttributes, &stringKey,

              OBJ_CASE_INSENSITIVE, NULL, NULL);

 

       //打开注册表项

       HANDLE hKey;

       NTSTATUS status = ZwOpenKey(&hKey, GENERIC_ALL, &ObjectAttributes);

       if (!NT_SUCCESS(status))

       {

              KdPrint(("打开注册表项失败!\n"));

              return status;

       }

      

       //初始化valueName

       UNICODE_STRING valueName;

       RtlInitUnicodeString(&valueName, L"valueName REG_DWORD");

      

       //获取实际查询的数据的大小

       ULONG ulSize = 0;

       status = ZwQueryValueKey(hKey, &valueName,

              KeyValuePartialInformation, NULL, 0, &ulSize);

 

       if (status == STATUS_OBJECT_NAME_NOT_FOUND || ulSize == 0)

       {

              ZwClose(hKey);

              KdPrint(("注册表键值不存在!\n"));

              return status;

       }

      

       //分配实际查询所需的内存空间

       PKEY_VALUE_PARTIAL_INFORMATION pkvpi =

              (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool(PagedPool, ulSize);

 

       //查询键值

       status = ZwQueryValueKey(hKey, &valueName, KeyValuePartialInformation,

              pkvpi, ulSize, &ulSize);

      

       if(!NT_SUCCESS(status))

       {

              ZwClose(hKey);

              KdPrint(("查询注册表键值失败!\n"));

              return status;

       }

       //判断是否为REG_DWORD类型

       if (pkvpi->Type == REG_DWORD && pkvpi->DataLength == sizeof(ULONG))

       {

              PULONG a = (PULONG)pkvpi->Data;

              KdPrint(("%d\n", *a));

       }

 

       //关闭注册表句柄

       ZwClose(hKey);

       return status;

}

 

 

5.       枚举子项

DDK提供了两个函数,他们组合使用可以起到枚举子项的作用。

NTSTATUS 
  ZwQueryKey(
    IN HANDLE
  
KeyHandle,
    IN KEY_INFORMATION_CLASS  
KeyInformationClass,
    OUT PVOID  
KeyInformation,
    IN ULONG  
Length,
    OUT PULONG  
ResultLength
    );

 

NTSTATUS 
  ZwEnumerateKey(
    IN HANDLE
  
KeyHandle,
    IN ULONG  
Index,
    IN KEY_INFORMATION_CLASS  
KeyInformationClass,
    OUT PVOID  
KeyInformation,
    IN ULONG  
Length,
    OUT PULONG  
ResultLength
    );

 

ZwQueryKey的作用主要是获得注册表项究竟有多少个子项,而ZwEnumerateKey的作用主要是针对第几个子项,获取该子项的具体信息。

在使用ZwQueryKey时,可以将参数KeyInformationClass指定为KeyFullInformation。这样参数KeyInformation就对应一个KEY_FULL_INFORMATION的数据结构。该结构中的SubKeys指明了项中有多少子项。

         在使用ZwEnumerateKey时,需要将参数KeyInformationClass指定为KeyBasicInformation,已返回子项的基本信息。

         注意:上面提到的这两个结构都是变长的,在使用时需要调用两次函数。第一次获得长度,第二次真正得到数据。

 

示例代码:

#define  MY_REG_SOFTWARE_KEY_NAME  L"\\Registry\\Machine\\Software\\Mzf"

#pragma INITCODE

NTSTATUS TetsKey()

{

         //初始化注册表项

         UNICODE_STRING stringKey;

         RtlInitUnicodeString(&stringKey, MY_REG_SOFTWARE_KEY_NAME);

        

         //初始化OBJECT_ATTRIBUTES结构

         OBJECT_ATTRIBUTES  ObjectAttributes;

         InitializeObjectAttributes(&ObjectAttributes, &stringKey,

                   OBJ_CASE_INSENSITIVE, NULL, NULL);

 

         //打开注册表项

         HANDLE hKey;

         NTSTATUS status = ZwOpenKey(&hKey, GENERIC_ALL, &ObjectAttributes);

         if (!NT_SUCCESS(status))

         {

                   KdPrint(("打开注册表项失败!\n"));

                   return status;

         }

 

         //第一次调用ZwQueryKey,为了获取KEY_FULL_INFORMATION数据的长度

         ULONG ulSize = 0;

         status = ZwQueryKey(hKey, KeyFullInformation, NULL, 0, &ulSize);

         //申请内存

         PKEY_FULL_INFORMATION pkfi =

(PKEY_FULL_INFORMATION)ExAllocatePool(PagedPool, ulSize);

        

         //第二次调用ZwQueryKey,为了获取KEY_FULL_INFORMATION数据

         status = ZwQueryKey(hKey, KeyFullInformation, pkfi, ulSize, &ulSize);

        

         for (ULONG i=0; i<pkfi->SubKeys; i++)

         {

                   //第一次调用ZwEnumerateKey,为了获取KEY_BASIC_INFORMATION数据的长度

                   ZwEnumerateKey(hKey, i, KeyBasicInformation, NULL, 0, &ulSize);

                   //非配内存

                   PKEY_BASIC_INFORMATION pkbi =

                                               (PKEY_BASIC_INFORMATION)ExAllocatePool(PagedPool, ulSize);

                   //第二次调用ZwEnumerateKey,为了获取KEY_BASIC_INFORMATION数据                              ZwEnumerateKey(hKey, i, KeyBasicInformation, pkbi, ulSize, &ulSize);

                  

                   UNICODE_STRING string;

                   string.Length = string.MaximumLength = (USHORT)pkbi->NameLength;

                   string.Buffer = (PWSTR)ExAllocatePool(PagedPool, string.Length);

                   RtlCopyMemory(string.Buffer, pkbi->Name, string.Length);

                   KdPrint((" %d 个子项: %wZ\n", i, &string));

                   //回收内存

                   ExFreePool(pkbi);

                   RtlFreeUnicodeString(&string);

         }

//回收内存

         ExFreePool(pkfi);

         //关闭注册表句柄

         ZwClose(hKey);

         return status;

}

 

 

6.       枚举子键

枚举子键是通过ZwQueryKeyZwEnumerateValueKey两个函数配合完成的。

NTSTATUS
  ZwEnumerateValueKey(
    IN HANDLE
  
KeyHandle,
    IN ULONG  
Index,
    IN KEY_VALUE_INFORMATION_CLASS  
KeyValueInformationClass,
    OUT PVOID  
KeyValueInformation,
    IN ULONG  
Length,
    OUT PULONG  
ResultLength
    );

 

示例代码:

#define  MY_REG_SOFTWARE_KEY_NAME  L"\\Registry\\Machine\\Software\\Mzf"

#pragma INITCODE

NTSTATUS TetsKey()

{

         //初始化注册表项

         UNICODE_STRING stringKey;

         RtlInitUnicodeString(&stringKey, MY_REG_SOFTWARE_KEY_NAME);

        

         //初始化OBJECT_ATTRIBUTES结构

         OBJECT_ATTRIBUTES  ObjectAttributes;

         InitializeObjectAttributes(&ObjectAttributes, &stringKey,

                   OBJ_CASE_INSENSITIVE, NULL, NULL);

 

         //打开注册表项

         HANDLE hKey;

         NTSTATUS status = ZwOpenKey(&hKey, GENERIC_ALL, &ObjectAttributes);

         if (!NT_SUCCESS(status))

         {

                   KdPrint(("打开注册表项失败!\n"));

                   return status;

         }

 

         ULONG ulSize = 0;

         //查询注册表

         status = ZwQueryKey(hKey, KeyFullInformation, NULL, 0, &ulSize);

 

         PKEY_FULL_INFORMATION pkfi =

(PKEY_FULL_INFORMATION)ExAllocatePool(PagedPool, ulSize);

         //查询注册表

         status = ZwQueryKey(hKey, KeyFullInformation, pkfi, ulSize, &ulSize);

        

         for (ULONG i=0; i<pkfi->Values; i++)

         {

                   //枚举注册表

                   ZwEnumerateValueKey(hKey, i, KeyValueBasicInformation, NULL, 0, &ulSize);

                   PKEY_VALUE_BASIC_INFORMATION pkbi =

                                     (PKEY_VALUE_BASIC_INFORMATION)ExAllocatePool(PagedPool, ulSize);

                   ZwEnumerateValueKey(hKey, i, KeyValueBasicInformation, pkbi, ulSize, &ulSize);

                  

                   UNICODE_STRING string;

                   string.Length = string.MaximumLength = (USHORT)pkbi->NameLength;

                   string.Buffer = (PWSTR)ExAllocatePool(PagedPool, string.Length);

                   RtlCopyMemory(string.Buffer, pkbi->Name, string.Length);

                   KdPrint((" %d 个子项: %wZ\n", i, &string));

                   //判断键值类型

                   if (pkbi->Type == REG_SZ)

                   {

                            KdPrint(("REG_SZ\n"));

                   }

                   ExFreePool(pkbi);

                   RtlFreeUnicodeString(&string);

         }

         ExFreePool(pkfi);

         //关闭注册表句柄

         ZwClose(hKey);

         return status;

}

 

7.       删除子项

NTSTATUS 
  ZwDeleteKey(
    IN HANDLE
  
KeyHandle
    );

需要指出,该函数只能删除没有子项的项目。如果项中有子项,则不能删除。这时候需要先将该项中的所有子项全部删除,再删除该项。

 

8.  其他

为了简化注册表的操作,DDK还提供了一系列以Rtl开头的运行时函数,这些函数把前面介绍的函数进行了封装。往往一条函数就能实现前面介绍的若干条函数的功能。

分类

描述

RtlCreateRegistryKey

创建注册表

RtlCheckRegistryKey

查看某注册表项是否存在

RtlWriteRegistryValue

写注册表

RtlDeleteRegistryValue

删除注册表的子键



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值