Win64 驱动内核编程-6.内核里操作注册表
内核里操作注册表
RING0 操作注册表和 RING3 的区别也不大,同样是“获得句柄->执行操作->关闭句柄”的模式,同样也只能使用内核 API 不能使用 WIN32API。不过内核里有一套 RTL 函数,把 Zw系列的注册表函数进行了封装,也就是说,只剩下“执行操作”这一步了。
接下来说说注册表的本质。注册表其实是文件,它存储在 c:\windows\system32\config
这个目录下(打开目录,看到那几个带锁图标的文件就是。为什么带锁?因为被 SYSTEM 进
程独占访问了)。注册表文件被称为 HIVE 文件,此格式是微软专用的,不公开,每个系统都不一定相同,但总体来说变化不大。当系统关机之前,或者调用 ZwFlushKey 时,把内存中的修改存入磁盘。另外,我们用 WINDOWS 自带的注册表编辑器看到的注册表有 5 个根项,其实 这 是 “ 幻 象 ”, 真 正 的 根 项 只 有 两 个 : HKEY_LOCAL_MACHINE 和 HKEY_USERS 。HKEY_CLASSES_ROOT 和 HKEY_CURRENT_CONFIG 其实都是 HKEY_LOCAL_MACHINE 的“下属”。
HKEY_CURRENT_USER 则是 HKEY_USERS 的“下属”,独立列出来只为了方便操作。
关于注册表的操作不多,无非就是:新建 KEY、重命名 KEY、删除 KEY、新建/设置 VALUE、读取 VALUE、删除 VALUE、枚举子 KEY 和 VALUE。这里之所以用英文,是因为中文的表达较为混乱。比如 KEY,有的翻译为项,有的翻译为键;VALUE 有的翻译为值,有的翻译为键值。所以为了统一说法,就用英文 KEY 和 VALUE 了。
1.新建 KEY
//RegCreateKey(L"\\Registry\\Machine\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\A");
void RegCreateKey(LPWSTR KeyName)
{
OBJECT_ATTRIBUTES objectAttributes;
UNICODE_STRING usKeyName;
NTSTATUS ntStatus;
HANDLE hRegister;
RtlInitUnicodeString(&usKeyName, KeyName);
InitializeObjectAttributes(&objectAttributes,
&usKeyName,
OBJ_CASE_INSENSITIVE,//对大小写敏感
NULL,
NULL);
ZwCreateKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes, 0, NULL,
REG_OPTION_NON_VOLATILE, NULL);
ZwClose(hRegister);
}
2.重命名 KEY(不建议用,如果可以的话最好是 删除然后创建)
(这里用到的关键 API『ZwRenameKey』没有导出,需要自己定位,大家可以先
用硬编码测试。在 WINDNG 里输入 x nt!ZwRenameKey 即可获得函数地址)
typedef NTSTATUS(__fastcall *ZWRENAMEKEY)(IN HANDLE KeyHandle,IN PUNICODE_STRING ReplacementName);
//修改这个地址!!
ZWRENAMEKEY ZwRenameKey = 0xFFFFF80012345678;
void RegRenameKey(UNICODE_STRING usOldKeyName, UNICODE_STRING usNewKeyName)
{
OBJECT_ATTRIBUTES objectAttributes;
HANDLE hRegister;
NTSTATUS ntStatus;
InitializeObjectAttributes(&objectAttributes,
&usOldKeyName,
OBJ_CASE_INSENSITIVE,//对大小写敏感
NULL,
NULL);
ntStatus = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes);
if (NT_SUCCESS(ntStatus))
{
ntStatus = ZwRenameKey(hRegister, &usNewKeyName);
ZwFlushKey(hRegister);
ZwClose(hRegister);
}
}
3.删除 KEY
void RegDeleteKey(LPWSTR KeyName)
{
OBJECT_ATTRIBUTES objectAttributes;
UNICODE_STRING usKeyName;
NTSTATUS ntStatus;
HANDLE hRegister;
RtlInitUnicodeString(&usKeyName, KeyName);
InitializeObjectAttributes(&objectAttributes,
&usKeyName,
OBJ_CASE_INSENSITIVE,//对大小写敏感
NULL,
NULL);
ntStatus = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes);
if (NT_SUCCESS(ntStatus))
{
ntStatus = ZwDeleteKey(hRegister);
ZwClose(hRegister);
}}
4.新建/设置 VALUE
void RegSetValueKey(LPWSTR REG_KEY_NAME, LPWSTR REG_VALUE_NAME, DWORD
DataType, PVOID DataBuffer, DWORD DataLength)
{
OBJECT_ATTRIBUTES objectAttributes;
UNICODE_STRING usKeyName, usValueName;
NTSTATUS ntStatus;
HANDLE hRegister;
ULONG Type;
RtlInitUnicodeString(&usKeyName, REG_KEY_NAME);
RtlInitUnicodeString(&usValueName, REG_VALUE_NAME);
InitializeObjectAttributes(&objectAttributes,
&usKeyName,
OBJ_CASE_INSENSITIVE,//对大小写敏感
NULL,
NULL);
ntStatus = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes);
if (NT_SUCCESS(ntStatus))
{
ntStatus = ZwSetValueKey(hRegister, &usValueName, 0, DataType,
DataBuffer, DataLength);
ZwFlushKey(hRegister);
ZwClose(hRegister);
}
}
5.读取 VALUE
typedef struct _KEY_VALUE_PARTIAL_INFORMATION {
ULONG TitleIndex;
ULONG Type;
ULONG DataLength;
UCHAR Data[1];
} KEY_VALUE_PARTIAL_INFORMATION, *PKEY_VALUE_PARTIAL_INFORMATION;
NTSTATUS RegQueryValue(UNICODE_STRING usKeyName, PUNICODE_STRING pValueName,
PKEY_VALUE_PARTIAL_INFORMATION *pkvpi)
{
ULONG ulSize;
NTSTATUS ntStatus;
PKEY_VALUE_PARTIAL_INFORMATION pvpi;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE hRegister;
InitializeObjectAttributes(&objectAttributes,
&usKeyName,
OBJ_CASE_INSENSITIVE,//对大小写敏感
NULL,
NULL);
ntStatus = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes);
if (!ntStatus)
{
return ntStatus;
}
ntStatus = ZwQueryValueKey(hRegister,
pValueName,
KeyValuePartialInformation,
NULL,
0,
&ulSize);
if (ntStatus == STATUS_OBJECT_NAME_NOT_FOUND || ulSize == 0)
{
return STATUS_UNSUCCESSFUL;
}
pvpi = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool(PagedPool, ulSize);
ntStatus = ZwQueryValueKey(hRegister,
pValueName,
KeyValuePartialInformation,
pvpi,
ulSize,
&ulSize);
if (!NT_SUCCESS(ntStatus))
{
return STATUS_UNSUCCESSFUL;
}
*pkvpi = pvpi; //这里的 pvpi 是没有释放的,用完要释放 ExFreePool(pvpi);
return STATUS_SUCCESS;
}
6.
删除 VALUE
void RegDeleteValueKey(LPWSTR REG_KEY_NAME, LPWSTR REG_VALUE_NAME)
{
OBJECT_ATTRIBUTES objectAttributes;
UNICODE_STRING usKeyName, usValueName;
NTSTATUS ntStatus;
HANDLE hRegister;
RtlInitUnicodeString(&usKeyName, REG_KEY_NAME);
RtlInitUnicodeString(&usValueName, REG_VALUE_NAME);
InitializeObjectAttributes(&objectAttributes,
&usKeyName,
OBJ_CASE_INSENSITIVE,//对大小写敏感
NULL,
NULL);
ntStatus = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes);
if (NT_SUCCESS(ntStatus))
{
ntStatus = ZwDeleteValueKey(hRegister, &usValueName);
ZwFlushKey(hRegister);
ZwClose(hRegister);
}
}
7.枚举子 KEY
VOID EnumerateSubItemRegTest()
{
#define MY_REG_SOFTWARE_KEY_NAME L"\\Registry\\Machine\\Software\\xxxxxxxx"
UNICODE_STRING RegUnicodeString;
HANDLE hRegister;
//初始化 UNICODE_STRING 字符串
RtlInitUnicodeString(&RegUnicodeString, MY_REG_SOFTWARE_KEY_NAME);
OBJECT_ATTRIBUTES objectAttributes;
//初始化 objectAttributes
InitializeObjectAttributes(&objectAttributes,
&RegUnicodeString,
OBJ_CASE_INSENSITIVE,//对大小写敏感
NULL,
NULL);
//打开注册表
NTSTATUS ntStatus = ZwOpenKey(&hRegister,
KEY_ALL_ACCESS,
&objectAttributes);
if (NT_SUCCESS(ntStatus))
{
KdPrint(("Open register successfully\n"));
}
ULONG ulSize;
//第一次调用 ZwQueryKey 为了获取 KEY_FULL_INFORMATION 数据的长度
ZwQueryKey(hRegister,
KeyFullInformation,
NULL,
0,
&ulSize);
PKEY_FULL_INFORMATION pfi =
(PKEY_FULL_INFORMATION)
ExAllocatePool(PagedPool, ulSize);
//第二次调用 ZwQueryKey 为了获取 KEY_FULL_INFORMATION 数据的数据
ZwQueryKey(hRegister,
KeyFullInformation,
pfi,
ulSize,
&ulSize);
for (ULONG i = 0; i<pfi->SubKeys; i++)
{
//第一次调用 ZwEnumerateKey 为了获取 KEY_BASIC_INFORMATION 数据的长度
ZwEnumerateKey(hRegister,
i,
KeyBasicInformation,
NULL,
0,
&ulSize);
PKEY_BASIC_INFORMATION pbi =
(PKEY_BASIC_INFORMATION)
ExAllocatePool(PagedPool, ulSize);
//第二次调用 ZwEnumerateKey 为了获取 KEY_BASIC_INFORMATION 数据的数据
ZwEnumerateKey(hRegister,
i,
KeyBasicInformation,
pbi,
ulSize,
&ulSize);
UNICODE_STRING uniKeyName;
uniKeyName.Length =
uniKeyName.MaximumLength =
(USHORT)pbi->NameLength;
uniKeyName.Buffer = pbi->Name;
KdPrint(("The %d sub item name:%wZ\n", i, &uniKeyName));
ExFreePool(pbi);
}
ExFreePool(pfi);
ZwClose(hRegister);
}
8.枚举子 VALUE
VOID EnumerateSubValueRegTest()
{
#define MY_REG_SOFTWARE_KEY_NAME L"\\Registry\\Machine\\Software\\xxxxxxxx"
UNICODE_STRING RegUnicodeString;
HANDLE hRegister;
//初始化 UNICODE_STRING 字符串
RtlInitUnicodeString(&RegUnicodeString, MY_REG_SOFTWARE_KEY_NAME);
OBJECT_ATTRIBUTES objectAttributes;
//初始化 objectAttributes
InitializeObjectAttributes(&objectAttributes,
&RegUnicodeString,
OBJ_CASE_INSENSITIVE,//对大小写敏感
NULL,
NULL);
//打开注册表
NTSTATUS ntStatus = ZwOpenKey(&hRegister,
KEY_ALL_ACCESS,
&objectAttributes);
if (NT_SUCCESS(ntStatus))
{
KdPrint(("Open register successfully\n"));
}
ULONG ulSize;
//查询 VALUE 的大小
ZwQueryKey(hRegister,
KeyFullInformation,
NULL,
0,
&ulSize);
PKEY_FULL_INFORMATION pfi =
(PKEY_FULL_INFORMATION)
ExAllocatePool(PagedPool, ulSize);
ZwQueryKey(hRegister,
KeyFullInformation,
pfi,
ulSize,
&ulSize);
for (ULONG i = 0; i<pfi->Values; i++)
{
//查询单个 VALUE 的大小
ZwEnumerateValueKey(hRegister,
i,
KeyValueBasicInformation,
NULL,
0,
&ulSize);
PKEY_VALUE_BASIC_INFORMATION pvbi =
(PKEY_VALUE_BASIC_INFORMATION)
ExAllocatePool(PagedPool, ulSize);
//查询单个 VALUE 的详情
ZwEnumerateValueKey(hRegister,
i,
KeyValueBasicInformation,
pvbi,
ulSize,
&ulSize);
UNICODE_STRING uniKeyName;
uniKeyName.Length =
uniKeyName.MaximumLength =
(USHORT)pvbi->NameLength;
uniKeyName.Buffer = pvbi->Name;
KdPrint(("The %d sub value name:%wZ\n", i, &uniKeyName));
if (pvbi->Type == REG_SZ)
{
KdPrint(("The sub value type:REG_SZ\n"));
}
else if (pvbi->Type == REG_MULTI_SZ)
{
KdPrint(("The sub value type:REG_MULTI_SZ\n"));
}
else if (pvbi->Type == REG_DWORD)
{
KdPrint(("The sub value type:REG_DWORD\n"));
}
else if (pvbi->Type == REG_BINARY)
{
KdPrint(("The sub value type:REG_BINARY\n"));
}
ExFreePool(pvbi);
}
ExFreePool(pfi);
ZwClose(hRegister);
}
最后总结一下几个常见的、和注册表相关的Zw函数的功能:
Win64 驱动内核编程-6.内核里操作注册表的更多相关文章
- Win64 驱动内核编程-32.枚举与删除注册表回调
枚举与删除注册表回调 注册表回调是一个监控注册表读写的回调,它的效果非常明显,一个回调能实现在SSDT 上 HOOK 十几个 API 的效果.部分游戏保护还会在注册表回调上做功夫,监控 service ...
- c++ 操作注册表
1. 注册表简介 注册表是为Windows NT和Windows95中所有32位硬件/驱动和32位应用程序设计的数据文件,用于存储系统和应用程序的设置信息.16位驱动在Winnt (Win ...
- C#操作注册表全攻略
相信每个人对注册表并不陌生,在运行里面输入“regedit”就可以打开注册表编辑器了.这东西对Windows系统来说可是比较重要的,也是病 毒常常会光顾的地方,比如病毒和恶意软件常常会在注册表的启动项 ...
- Process Monitor监控进程操作注册表如何实现?
http://zhidao.baidu.com/link?url=Kqav4qkQSprC5FnpHPOGJvhqvY9fJ9-Vdx9g_SWh4w5VOusdRJo4Vl7qIdrG4LwRJvr ...
- [转]C#操作注册表
原文链接:http://www.cnblogs.com/txw1958/archive/2012/08/01/csharp-regidit.html 下面我们就来用.NET下托管语言C#注册表操作,主 ...
- [荐]使用Js操作注册表
使用Js操作注册表 要操作注册表需要通过ActiveX控件调用WScript.shell对象,通过该对象的一些方法来操作. WshShell对象:可以在本地运行程序.操纵注册表内容.创建快捷方式或访问 ...
- .Net操作注册表--un
C#操作注册表 导入命名空间 Using MicroSoft.Win32;//64位系统装的64位版本
- C#获取cpu序列号 硬盘ID 网卡硬地址以及操作注册表 .
转:http://blog.csdn.net/smartsmile2012/article/details/8682295 #region 获取cpu序列号 硬盘ID 网卡硬地址 /**/ /// & ...
- VBS 操作注册表 十六进制
使用VBS操作注册表,通常使用RegRead/RegWrite/RegDelete方法,如: RegRead: 'read.vbs(将以下代码存为read.vbs文件) Dim OperationRe ...
随机推荐
- 一文搞懂 this、apply、call、bind
码文不易,转载请带上本文链接,感谢~ https://www.cnblogs.com/echoyya/p/14506269.html 目录 码文不易,转载请带上本文链接,感谢~ https://www ...
- [SNOI2019] 通信
一.题目 点此看题 二.解法 一看就是傻逼补流模型,不会真的有人这个图都建不出来吧 别走啊,我不阴阳怪气了,如果你不知道怎么建这里有图嘛(思路来源是餐巾计划问题): 其中标红的边数量级很大,因为 \( ...
- 字符串匹配-BF算法和KMP算法
声明:图片及内容基于https://www.bilibili.com/video/av95949609 BF算法 原理分析 Brute Force 暴力算法 用来在主串中查找模式串是否存以及出现位置 ...
- python实现通过URL下载图片到本地服务器
import os import urllib.request image_url = 'http://img.jingtuitui.com/759fa20190115144450401.jpg' f ...
- CVE-2021-21402 Jellyfin任意文件读取
CVE-2021-21402 Jellyfin任意文件读取 漏洞简介 jellyfin 是一个自由的软件媒体系统,用于控制和管理媒体和流媒体.它是 emby 和 plex 的替代品,它通过多个应用程序 ...
- 【面试技巧】老生常谈之 n 种使用 CSS 实现三角形的技巧
在一些面经中,经常能看到有关 CSS 的题目都会有一道如何使用 CSS 绘制三角形,而常见的回答通常也只有使用 border 进行绘制一种方法. 而 CSS 发展到今天,其实有很多有意思的仅仅使用 C ...
- Python基础(八):字符串的使用(下)
find() 功能:检测字符串是否包含指定字符.如果包含指定字符,则返回开始的索引:否则,返回-1. >>> st = "hello world" >> ...
- 201871010109-胡欢欢-实验一-软件工程的准备(初识github及《现代软件工程-构建之法》)
项目 内容 课程班级博客链接 2021年春软件工程课程班(2018级计算机科学与技术) 这个作业要求链接链接 实验一软件工程准备 我的课程学习目标 了解github的基本使用,学习markdown编辑 ...
- (八)Struts2中的参数封装
一.静态参数封装 什么是静态参数? 静态参数就是硬编码的,不可随意改变. 例子: (1)我们首先创建一个Action类,里面有两个参数,用来封装请求参数 public class User exten ...
- 浅谈 Fresco 框架结构
在前面的文章 Fresco 源码分析 -- 图片加载流程 里面详细说明了图片加载的整个流程,但是除了理解源码之外,对于源码的框架层面的设计也是需要去了解的,不能只是简单的读源码,好的源码的框架设计也是 ...