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

news2025/1/11 19:59:31

在上一篇文章《内核字符串转换方法》中简单介绍了内核是如何使用字符串以及字符串之间的转换方法,本章将继续探索字符串的拷贝与比较,与应用层不同内核字符串拷贝与比较也需要使用内核专用的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.Buffer
    RtlCopyMemory(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.coloradmin.cn/o/1206334.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ffmpeg扩展支持H265视频流的flv封装

ffmpeg扩展支持H265视频流的flv封装 由于Adobe暂停了对RTMP/FLV标准的更新&#xff0c;所以目前标准中没有支持HEVC视频编码格式。为避免各终端和服务器间的兼容性问题&#xff0c;FFmpeg也没有在RTMP/FLV的协议实现中进行HEVC的相关扩展。CDN联盟制定了相关的协议扩展规范&am…

C++引用 引用做函数参数

一.引用的定义和语法 // 给a取别名为b int &b a; // 修改b的值&#xff0c;a的值也会被修改&#xff0c;因为他们都指向同一个内存空间 b 20; 二.引用的注意事项 1.引用必须初始化如 int&b; 是错误的&#xff0c;因为没有初始化。 2.引用在初始化后&#xff0c;不…

二维码智慧门牌管理系统升级,实现综合运营可视化

文章目录 前言一、升级解决方案概述二、重点指标综合展示三、综合运营可视化 前言 随着科技的发展和城市化进程的加速&#xff0c;传统的门牌管理系统已经无法满足现代社会的需求。为了解决这一问题&#xff0c;一款二维码智慧门牌管理系统应运而生&#xff0c;为城市管理和运…

Jenkins Docker Swarm插件 配置的坑

配置 Docker Host URI 注意&#xff0c;这里要用 http://&#xff01;&#xff01;&#xff01;如果按照提示里用了 tcp:// 则会报错&#xff0c;异常信息如下&#xff1a; 2023-11-13 16:28:42.6830000 [id34] WARNING o.e.j.s.h.ContextHandler$Context#log: Error while s…

如何在TS中使用JS库

在 TypeScript 中使用 JavaScript 库&#xff0c;几种常用的方法。 直接使用&#xff1a;如果 JavaScript 库不提供 TypeScript 类型定义文件&#xff08;.d.ts&#xff09;&#xff0c;您可以直接在 TypeScript 代码中使用该库。您可以通过在 TypeScript 代码的开头添加 //ts-…

【C/PTA——8.数组2(课内实践)】

C/PTA——8.数组2&#xff08;课内实践&#xff09; 7-1 求矩阵的局部极大值7-2 求矩阵各行元素之和7-3 判断上三角矩阵7-4 点赞 7-1 求矩阵的局部极大值 #include<stdio.h> int main() {int m, n, i, j;int arr[100][100];scanf("%d %d", &m, &n);for…

PHP在自己框架中引入composer

目录 1、使用composer之前先安装环境 2、 在项目最开始目录添加composer.json文本文件 3、写入配置文件 composer.json 4、使用composer安装whoops扩展 5、引入composer类并且使用安装异常显示类 1、使用composer之前先安装环境 先安装windows安装composer并更换国内镜像…

部署 KVM 虚拟化平台

虚拟化技术的演变过程分为软件模拟、虚拟化层翻译、容器虚拟化三个阶段 1 软件模拟的技术方式 软件模拟是通过软件完全模拟CPU、网卡、芯片组、磁盘等计算机硬件&#xff0c;因为是软件模拟&#xff0c;所以理论上可以模拟任何硬件&#xff0c;甚至不存在的硬件。但是由于是软…

SAP-SD-一个无交货数量的项目是不允许的,项目将被删除

创建外向交货单时报错 初步判断是没有销售订单库存&#xff0c;普通库存1000&#xff0c;从普通库存转移100到销售订单库存&#xff0c;移动类型413 转移完成 再VL01N就可以了

数据结构 顺序表和链表

1.线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列 线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串.. 线性表在逻辑上是线性结构&#xff0c;也就说是连续的一条直线…

数据仓库入门简介

一&#xff0c;数组仓库介绍 数据仓库 &#xff08;英语&#xff1a;Data Warehouse&#xff0c;简称数仓、DW&#xff09;是一个为数据分析而设计的企业级数据管理系统。它旨在 支持企业决策过程中的数据分析和业务智能 。数据仓库的基本原理是将不同来源的数据整合到一个中心…

机器学习6:逻辑回归

假设我们有一个二元分类问题&#xff0c;有两个特征&#xff08;x1, x2&#xff09;和对应的类别标签&#xff08;y&#xff09;。给定 以下训练数据集&#xff1a; 我们定义逻辑回归模型的假设函数和损失函数。假设函数使用 sigmoid 函 数来将线性函数的输出转换为概率值&…

计算机毕业设计项目选题推荐(免费领源码)SSM+Mysql电商微信小程序09228

摘 要 随着微信小程序的使用越来越广泛&#xff0c;在传统的商业模式中&#xff0c;对于各类生活日常商品&#xff0c;人们习惯于到各种商家店铺购买。然而在快节奏的新时代中&#xff0c;人们不一定能为购买商品腾出时间&#xff0c;更不会耐心挑选自己想要的商品。所以设计一…

通信原理板块——语音压缩编码

微信公众号上线&#xff0c;搜索公众号小灰灰的FPGA,关注可获取相关源码&#xff0c;定期更新有关FPGA的项目以及开源项目源码&#xff0c;包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 1、语音压缩编码 语音压缩编码可以…

第91步 深度学习图像分割:FCN建模

基于WIN10的64位系统演示 一、写在前面 本期&#xff0c;我们继续学习深度学习图像分割系列的另一个模型&#xff0c;FCN (Fully Convolutional Network)。 二、FCN FCN是一种用于图像语义分割的神经网络。与传统的分类网络&#xff08;如VGG、AlexNet&#xff09;不同&…

ARPG----C++学习记录04 Section8 角色类,移动

角色类输入 新建一个角色C&#xff0c;继承建立蓝图,和Pawn一样&#xff0c;绑定输入移动和相机. 在构造函数中添加这段代码也能实现。打开UsePawnControlRotation就可以让人物不跟随鼠标旋转 得到旋转后的向前向量 使用旋转矩阵 想要前进方向和旋转的方向对应。获取当前控制…

如何通过把setTimeout异步转为同步

一.封装定时器函数 function delayed(time){return new Promise((resolve,reject)>{setTimeout( () > {resolve(time)}, time);}) }二调用的时候通过async await 修饰 async function demo() {console.log(new Date().getMinutes(): new Date().getSeconds())await del…

你知道王者荣耀是怎么实现技能范围指示器的吗?

引言 一文教会你实现类似王者荣耀的技能范围指示器。 技能范围指示器是许多游戏中常见的一个元素&#xff0c;特别是在MOBA&#xff08;多人在线战斗竞技场&#xff09;游戏中&#xff0c;如《王者荣耀》、《英雄联盟》等。 本文将介绍如何在Cocos Creator中实现一个技能范围…

6.jvm中对象创建流程与内存分配

目录 概述对象的创建流程对象的内存分配方式对象怎样才会进入老年代大对象直接进入老年代内存担保 jvc 相关指令查看jdk默认使用的gc查看当前jdk支持的有哪些gc查看指定进程当前正在使用的gc 结束 概述 相关文章在此总结如下&#xff1a; 文章地址jvm基本知识地址jvm类加载系…

Linux常用命令——bzip2命令

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