注册表操作
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 ,KeyValueFullInformation,KeyValuePartialInformation 。 一般情况下,选择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. 枚举子键 枚举子键是通过ZwQueryKey和ZwEnumerateValueKey两个函数配合完成的。 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删除注册表的子键