2.4 Windows驱动开发:内核字符串拷贝与比较

在上一篇文章《内核字符串转换方法》中简单介绍了内核是如何使用字符串以及字符串之间的转换方法,本章将继续探索字符串的拷贝与比较,与应用层不同内核字符串拷贝与比较也需要使用内核专用的API函数,字符串的拷贝往往伴随有内核内存分配,我们将首先简单介绍内核如何分配堆空间,然后再以此为契机简介字符串的拷贝与比较。

2.4.1 内核中的空间分配

首先内核中的堆栈分配可以使用ExAllocatePool()这个内核函数实现,此外还可以使用ExAllocatePoolWithTag()函数,两者的区别是,第一个函数可以直接分配内存,第二个函数在分配时需要指定一个标签,此外内核属性常用的有两种NonPagedPool用于分配非分页内存,而PagePool则用于分配分页内存,在开发中推荐使用非分页内存,因为分页内存数量有限。

内存分配使用ExAllocatePool函数,内存拷贝可使用RtlCopyMemory函数,需要注意该函数其实是对Memcpy函数的包装。

ExAllocatePool用于在内核空间分配内存。它的作用是向系统申请一块指定大小的内存,并返回这块内存的起始地址,供内核使用。需要注意的是,使用ExAllocatePool分配的内存是在内核空间中,因此不能被用户空间的代码直接访问。

RtlCopyMemory也是Windows内核开发中的一个函数,用于在内存中拷贝数据。它的作用是将指定长度的数据从源地址拷贝到目标地址,可以用于在内核空间中拷贝数据。需要注意的是,RtlCopyMemory实际上是对memcpy函数的封装,但是它提供了更加严格的参数检查和更好的错误处理机制,因此在内核开发中建议使用RtlCopyMemory而不是直接使用memcpy

在使用这两个函数时需要注意以下几点:

  • ExAllocatePool分配的内存必须在使用完后及时释放,否则会导致内存泄漏。可以使用ExFreePool函数来释放内存。
  • ExAllocatePool分配的内存是非连续的,因此不能使用指针算术运算来访问内存块中的某个元素。如果需要在内存块中访问某个元素,可以使用数组下标的方式来访问。
  • RtlCopyMemory函数需要确保源地址和目标地址所指向的内存块不会重叠,否则会导致数据的不确定性。可以使用RtlMoveMemory函数来处理源地址和目标地址重叠的情况。
#include <ntifs.h>VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint("驱动已卸载 \n");
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{UNICODE_STRING uncode_buffer = { 0 };DbgPrint("hello lyshark \n");wchar_t * wchar_string = L"hello lyshark";// 设置最大长度uncode_buffer.MaximumLength = 1024;// 分配内存空间uncode_buffer.Buffer = (PWSTR)ExAllocatePool(PagedPool, 1024);// 设置字符长度 因为是宽字符,所以是字符长度的 2 倍uncode_buffer.Length = wcslen(wchar_string) * 2;// 保证缓冲区足够大,否则程序终止ASSERT(uncode_buffer.MaximumLength >= uncode_buffer.Length);// 将 wchar_string 中的字符串拷贝到 uncode_buffer.BufferRtlCopyMemory(uncode_buffer.Buffer, wchar_string, uncode_buffer.Length);// 设置字符串长度 并输出uncode_buffer.Length = wcslen(wchar_string) * 2;DbgPrint("输出字符串: %wZ \n", uncode_buffer);// 释放堆空间ExFreePool(uncode_buffer.Buffer);uncode_buffer.Buffer = NULL;uncode_buffer.Length = uncode_buffer.MaximumLength = 0;DbgPrint("驱动已加载 \n");Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

代码输出效果如下图所示:

实现空间分配,字符串结构UNICODE_STRING可以定义数组,空间的分配也可以循环进行,例如我们分配十个字符串结构,并输出结构内的参数。

#include <ntifs.h>VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint("驱动已卸载 \n");
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{UNICODE_STRING uncode_buffer[10] = { 0 };wchar_t * wchar_string = L"hello lyshark";DbgPrint("hello lyshark \n");int size = sizeof(uncode_buffer) / sizeof(uncode_buffer[0]);DbgPrint("数组长度: %d \n", size);for (int x = 0; x < size; x++){// 分配空间uncode_buffer[x].Buffer = (PWSTR)ExAllocatePool(PagedPool, 1024);// 设置长度uncode_buffer[x].MaximumLength = 1024;uncode_buffer[x].Length = wcslen(wchar_string) * sizeof(WCHAR);ASSERT(uncode_buffer[x].MaximumLength >= uncode_buffer[x].Length);// 拷贝字符串并输出RtlCopyMemory(uncode_buffer[x].Buffer, wchar_string, uncode_buffer[x].Length);uncode_buffer[x].Length = wcslen(wchar_string) * sizeof(WCHAR);DbgPrint("循环: %d 输出字符串: %wZ \n", x, uncode_buffer[x]);// 释放内存ExFreePool(uncode_buffer[x].Buffer);uncode_buffer[x].Buffer = NULL;uncode_buffer[x].Length = uncode_buffer[x].MaximumLength = 0;}DbgPrint("驱动加载成功 \n");Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

代码输出效果如下图所示:

2.4.2 内核中的字符串拷贝

实现字符串拷贝,此处可以直接使用RtlCopyMemory函数直接对内存操作,也可以调用内核提供的RtlCopyUnicodeString函数来实现,具体代码如下。

#include <ntifs.h>VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint("驱动已卸载 \n");
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark \n");UNICODE_STRING uncode_buffer_source = { 0 };UNICODE_STRING uncode_buffer_target = { 0 };// 该函数可用于初始化字符串RtlInitUnicodeString(&uncode_buffer_source, L"hello lyshark");// 初始化target字符串,分配空间uncode_buffer_target.Buffer = (PWSTR)ExAllocatePool(PagedPool, 1024);uncode_buffer_target.MaximumLength = 1024;// 将source中的内容拷贝到target中RtlCopyUnicodeString(&uncode_buffer_target, &uncode_buffer_source);// 输出结果DbgPrint("source = %wZ \n", &uncode_buffer_source);DbgPrint("target = %wZ \n", &uncode_buffer_target);// 释放空间 source 无需销毁// 如果强制释放掉source则会导致系统蓝屏,因为source是在栈上的RtlFreeUnicodeString(&uncode_buffer_target);DbgPrint("驱动加载成功 \n");Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

代码输出效果如下图所示:

2.4.3 内核中的字符串比较

实现字符串比较,如果需要比较两个UNICODE_STRING字符串结构体是否相等,那么可以使用RtlEqualUnicodeString这个内核函数实现。

RtlEqualUnicodeString用于比较两个UNICODE_STRING字符串结构体是否相等。该函数的第一个参数是指向要比较的第一个字符串结构体的指针,第二个参数是指向要比较的第二个字符串结构体的指针,第三个参数是指定比较的方式,如果该参数为TRUE,则函数会在相等的情况下返回TRUE,否则会在不相等的情况下返回FALSE。

下面是一个使用RtlEqualUnicodeString函数比较两个字符串结构体是否相等的示例代码:

#include <ntifs.h>VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint("驱动已卸载 \n");
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark \n");UNICODE_STRING uncode_buffer_source = { 0 };UNICODE_STRING uncode_buffer_target = { 0 };// 该函数可用于初始化字符串RtlInitUnicodeString(&uncode_buffer_source, L"hello lyshark");RtlInitUnicodeString(&uncode_buffer_target, L"hello lyshark");// 比较字符串是否相等if (RtlEqualUnicodeString(&uncode_buffer_source, &uncode_buffer_target, TRUE)){DbgPrint("字符串相等 \n");}else{DbgPrint("字符串不相等 \n");}DbgPrint("驱动加载成功 \n");Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

代码输出效果如下图所示:

有时在字符串比较时需要统一字符串格式,例如将所有字符全部转换为大写之后再做比较,此时可以使用RtlUpcaseUnicodeString函数将小写字符串为大写。

RtlUpcaseUnicodeString用于将UNICODE_STRING字符串结构体中的字符转换为大写字符。该函数的第一个参数是指向要转换的字符串结构体的指针,第二个参数是指向要存储结果的字符串结构体的指针,第三个参数指定转换的方式。

下面是一个使用RtlUpcaseUnicodeString函数大小写字符串转换的示例代码:

#include <ntifs.h>VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint("驱动已卸载 \n");
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark \n");UNICODE_STRING uncode_buffer_source = { 0 };UNICODE_STRING uncode_buffer_target = { 0 };// 该函数可用于初始化字符串RtlInitUnicodeString(&uncode_buffer_source, L"hello lyshark");RtlInitUnicodeString(&uncode_buffer_target, L"HELLO LYSHARK");// 字符串小写变大写RtlUpcaseUnicodeString(&uncode_buffer_target, &uncode_buffer_source, TRUE);DbgPrint("小写输出: %wZ \n", &uncode_buffer_source);DbgPrint("变大写输出: %wZ \n", &uncode_buffer_target);// 销毁字符串RtlFreeUnicodeString(&uncode_buffer_target);DbgPrint("驱动加载成功 \n");Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

代码输出效果如下图所示:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/171462.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Orangepi Zero2 全志H616】驱动串口实现Tik Tok—VUI(语音交互)

一、编程实现语音和开发板通信 wiringpi库源码demo.c 二、基于前面串口的代码修改实现 uartTool.huartTool.cuartTest.c 三、ADB adb控制指令 四、手机接入Linux热拔插相关 a. 把手机接入开发板 b. 安装adb工具&#xff0c;在终端输入adb安装指令&#xff1a; sudo apt-g…

k8s的service自动发现服务:实战版

Service服务发现的必要性: 对于kubernetes整个集群来说&#xff0c;Pod的地址也可变的&#xff0c;也就是说如果一个Pod因为某些原因退出了&#xff0c;而由于其设置了副本数replicas大于1&#xff0c;那么该Pod就会在集群的任意节点重新启动&#xff0c;这个重新启动的Pod的I…

基于卷积神经网络和客源注意力机制的OD客流预测模型

文章信息 论文题目为《An origin–destination passenger flow prediction system based on convolutional neural network and passenger source-based attention mechanism》&#xff0c;该文于2023年发表于Expert Systems With Applications期刊上。文章提出一种基于乘客源注…

xss学习笔记

跨站脚本攻击 掌握XSS 的原理 掌握XSS 的场景 掌握XSS 的危害 掌握XSS 漏洞验证 掌握XSS 的分类跨站脚本攻击 漏洞概述 ​ 跨站点脚本&#xff08;Cross Site Scripting&#xff0c; XSS&#xff09;是指客户端代码注入攻击&#xff0c;攻击者可以在合法网站或Web 应用程…

Linux可以投屏到电视吗?用网页浏览器就能投屏到电视!

Linux系统的电脑如果要投屏到安卓电视屏幕上&#xff0c;可以使用投屏工具AirDroid Cast的网页版和TV版一起实现。 首先&#xff0c;在Linux系统的电脑里用chrome浏览器或edge浏览器打开webcast.airdroid.com。这就是AirDroid Cast的网页版。你可以看到中间白色框框的右上角有个…

使用Jmeter进行http接口性能测试

在进行网页或应用程序后台接口开发时&#xff0c;一般要及时测试开发的接口能否正确接收和返回数据&#xff0c;对于单次测试&#xff0c;Postman插件是个不错的Http请求模拟工具。 但是Postman只能模拟单客户端的单次请求&#xff0c;而对于模拟多用户并发等性能测试&#xf…

asp.net core mvc之 RAZOR共享指令和标签助手 TagHelpers

一、RAZOR共享指令 RAZOR共享指令&#xff1a;在视图中导入命名空间&#xff0c;执行依赖注入。 RAZOR共享指令是写在 Views目录下的 _ViewImports.cshtml 文件 支持指令如下&#xff1a; addTagHelper 增加标签助手 removeTagHelper 移除标签助手 tagHelperPrefix 标签助手…

es性能强悍的推演过程

前言 es底层复用的Lucene的能力&#xff0c;Lucene在以前的文章中有所讲解&#xff0c;感兴趣可查看 https://blog.csdn.net/u013978512/article/details/125474873?ops_request_misc%257B%2522request%255Fid%2522%253A%2522169771769916777224433628%2522%252C%2522scm%2522…

模拟接口数据之使用Fetch方法实现

文章目录 前言一、package.json配置mock执行脚本二、封装接口&#xff0c;区分走ajax还是fetch三、创建mock目录&#xff0c;及相关接口文件四、定义接口五、使用mock数据使用模拟数据优化fetch返回数据 六、不使用模拟数据七、对比其他需要使用依赖相关配置如有启发&#xff0…

Linux 性能调优之硬件资源监控

写在前面 考试整理相关笔记博文内容涉及 Linux 硬件资源监控常见的命名介绍&#xff0c;涉及硬件基本信息查看查看硬件错误信息查看虚拟环境和云环境资源理解不足小伙伴帮忙指正 对每个人而言&#xff0c;真正的职责只有一个&#xff1a;找到自我。然后在心中坚守其一生&#x…

Linux常用命令——bzip2命令

在线Linux命令查询工具 bzip2 将文件压缩成bz2格式 补充说明 bzip2命令用于创建和管理&#xff08;包括解压缩&#xff09;“.bz2”格式的压缩包。我们遇见Linux压缩打包方法有很多种&#xff0c;以下讲解了Linux压缩打包方法中的Linux bzip2命令的多种范例供大家查看&…

Mac 本地部署thinkphp8【部署环境以及下载thinkphp】

PHP的安装以及环境变量配置 1 PHP安装&#xff1a;在终端输入brew install php 这里是PHP下载的最新的 如果提示‘brew’找不到&#xff0c;自己搜索安装吧&#xff0c; 不是特别难 2 环境变量配置 终端输入vim ~/.bash_profile 输入export PATH"/usr/local/Cellar/php/8.…