首页 > 系统相关 >2.4 Windows驱动开发:内核字符串拷贝与比较

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

时间:2023-11-14 09:12:50浏览次数:34  
标签:uncode DbgPrint STRING Windows buffer 内核 字符串 2.4

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

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

标签:uncode,DbgPrint,STRING,Windows,buffer,内核,字符串,2.4
From: https://www.cnblogs.com/LyShark/p/17830852.html

相关文章

  • windows 10 安装ubuntu+docker(不安装desktop docker)步骤
    windows10安装ubuntu+docker(不安装desktopdocker)步骤1.安装wsl2,ubuntu22.04参考网络文档2.powershell里运行wsl3.sudo-i4.curl-fsSLhttps://get.docker.com-oget-docker.sh5.shget-docker.sh6.servicedockerstart7.在2.1通过脚本安装docker时,会出现下面这个问......
  • Azure DevOps 发布.Net项目到Windows IIS站点之通过公网IP(账号、密码)
    在AzureDevOps中通过指定公网IP发布代码到指定目录#ASP.NETCore(.NETCore7.0)#BuildandtestASP.NETCoreprojectstargeting.NETCore7.0.#Addstepsthatpublishsymbols,savebuildartifacts,andmore:#https://docs.microsoft.com/azure/devops/pipeli......
  • Windows下安装Jenkins
    从官网下的最新的Jenkins,在安装时出现了:Service'Jenkins'(Jenkins)failedtostart.verifythatyouhavesufficientprivilegestostartsystemservices试过网上的各种方法都无效。最后找到了Jenkins的安装目录下有一个jenkins.err.log,打开后发现里面有一段话如下:Ru......
  • Azure DevOps 发布.Net项目到Windows IIS站点之Azure项目发布内网VM
    当你有一个需求,需要通过AzureDevOps发布到一个没有公网的VM的时候,你将需要使用以下脚本trigger:-masterpool:vmImage:'windows-2022'variables:-name:Build.ArtifactStagingDirectoryvalue:'$(Build.Repository.LocalPath)\artifacts'-name:buildConf......
  • 【Windows 开发环境配置】NVIDIA 篇
    CUDA从CUDAToolkitArchive下载相应版本的离线安装包,这里以11.7为例。打开安装包,在安装选项选择自定义模式,点击下一步。在自定义安装选项中,仅选择CUDA组件(其中Nsight相关组件用于代码调试与性能分析),若未安装显卡驱动,选择NVIDIAGeForceExperiencecomponents并点击下一步。......
  • coreybutler/nvm-windows 简单使用
    目录nvm是什么安装简单命令nvm是什么Windows电脑node.js管理器。可以方便node.js的安装与切换。最新版本1.1.11coreybutler/nvm-windows有一个更高star的nvm是nvm-sh/nvm,没仔细研究。安装非常简单,下载Releases下的安装包,一步步安装即可,选好安装的位置即可。最先下卸载本......
  • Windows系统CMD命令行添加或删除路由
    Windows系统CMD命令行添加或删除路由 原文地址:https://www.cnblogs.com/dianchaozhang/p/16985395.html1,按Win键输入“CMD”,右键“以管理员身份运行” 2,在CMD窗口输入“ipconfig”并按Enter键  3,找到自己的网卡对应的“默认网关”,执行如下命令添加路由: routeadd{......
  • 使用.NET Core创建Windows服务
    使用.NETCore创建Windows服务1.创建一个新的WorkerService项目打开命令行工具(例如:PowerShell、CMD、或者终端),然后输入以下命令创建一个新的WorkerService项目:dotnet new worker -n MyWorkerService这个命令将会创建一个名为"MyWorkerService"的新项目。2.添加必要的NuGe......
  • Windows Privileges一览
    权限升级是指对IT系统访问权限有限的用户可以增加其访问权限的范围和规模。对于受信任的用户,权限升级允许在有限的时间内扩大访问权限以完成特定任务。例如,用户可能需要访问权限来解决技术问题、运行季度财务报告或安装程序。WindowsPrivileges一览有一天我发现了这个有......
  • VMware Workstation 17.5 Pro Unlocker & OEM BIOS for Windows
    VMwareWorkstation17.5ProUnlocker&OEMBIOSforWindows在Windows上运行macOSSonoma请访问原文链接:https://sysin.org/blog/vmware-workstation-17-unlocker/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.org桌面HypervisorVMwareWorkstationProVMwar......