You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows内核驱动中UCHAR转WCHAR字符串的问题排查

解决Windows内核驱动中ANSI转Unicode字符串的写入只读内存错误

问题背景

在Windows内核驱动开发中,需将_SYSTEM_MODULE_INFORMATION结构体的UCHAR类型FullPathName字符串,分别复制到_DRIVER_INFO结构体的WCHAR类型ModuleName(取FullPathName + OffsetToFileName部分)和FullPathName字段。使用RtlAnsiStringToUnicodeString函数时,单独执行第一个转换正常,添加第二个转换后触发“attempted write to read only location”错误。


相关结构体定义

_DRIVER_INFO 结构体

typedef struct _DRIVER_INFO {  
    PVOID MappedBase;     
    PVOID ImageBase;     
    ULONG ImageSize;     
    WCHAR ModuleName[MAXIMUM_FILENAME_LENGTH];     
    WCHAR FullPathName[MAXIMUM_FILENAME_LENGTH]; 
} DRIVER_INFO;

_SYSTEM_MODULE_INFORMATION 结构体

typedef struct _SYSTEM_MODULE_INFORMATION {
    HANDLE Section;     
    PVOID MappedBase;     
    PVOID ImageBase;     
    ULONG ImageSize;     
    ULONG Flags;     
    USHORT LoadOrderIndex;     
    USHORT InitOrderIndex;     
    USHORT LoadCount;     
    USHORT OffsetToFileName;     
    UCHAR FullPathName[MAXIMUM_FILENAME_LENGTH]; 
} SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION;

错误代码问题分析

你提供的错误代码存在两个核心问题:

  1. 错误的类型转换:直接将PCSTR指针强制转为PCANSI_STRING传入函数,而ANSI_STRING是包含Length、MaximumLength、Buffer的结构体,并非单纯字符指针,导致函数读取非法内存。
  2. 内存分配参数错误:第三个参数传TRUE时,函数会尝试从堆分配内存存储转换后的Unicode字符串,但driverInfo的ModuleName和FullPathName是栈上固定大小数组,函数试图向不匹配的内存区域写入,触发只读内存错误。

正确解决方案

  1. 手动构造合法的ANSI_STRING结构体,正确设置字符串长度和缓冲区指针。
  2. 调用RtlAnsiStringToUnicodeString时,第三个参数传FALSE,指定使用我们提供的栈缓冲区,而非让函数分配堆内存。
  3. 处理转换失败的边界情况(如字符串过长),避免垃圾数据残留。

修正后的完整代码

for (ULONG i = 0; i < moduleList->NumberOfModules; i++)
{
    PSYSTEM_MODULE_INFORMATION module = &moduleList->Modules[i];
    DRIVER_INFO driverInfo = {0}; // 初始化结构体,清除垃圾数据
    
    // 复制驱动基础信息
    driverInfo.MappedBase = module->MappedBase;
    driverInfo.ImageBase = module->ImageBase;
    driverInfo.ImageSize = module->ImageSize;

    // 构造FullPathName对应的ANSI_STRING
    ANSI_STRING fullPathAnsi = {0};
    fullPathAnsi.Buffer = (PCHAR)module->FullPathName;
    fullPathAnsi.Length = (USHORT)strlen((PCSTR)module->FullPathName);
    fullPathAnsi.MaximumLength = fullPathAnsi.Length + 1; // 包含字符串终止符

    // 构造ModuleName对应的ANSI_STRING
    ANSI_STRING moduleNameAnsi = {0};
    moduleNameAnsi.Buffer = (PCHAR)(module->FullPathName + module->OffsetToFileName);
    moduleNameAnsi.Length = (USHORT)strlen((PCSTR)moduleNameAnsi.Buffer);
    moduleNameAnsi.MaximumLength = moduleNameAnsi.Length + 1;

    // 转换FullPathName到Unicode,使用栈缓冲区
    UNICODE_STRING fullPathUnicode = {0};
    fullPathUnicode.Buffer = driverInfo.FullPathName;
    fullPathUnicode.MaximumLength = sizeof(driverInfo.FullPathName);
    NTSTATUS status = RtlAnsiStringToUnicodeString(&fullPathUnicode, &fullPathAnsi, FALSE);
    if (!NT_SUCCESS(status))
    {
        DebugMessage("FullPath转换失败,状态码: 0x%X\n", status);
        // 手动添加终止符,避免垃圾数据
        driverInfo.FullPathName[sizeof(driverInfo.FullPathName)/sizeof(WCHAR)-1] = L'\0';
    }

    // 转换ModuleName到Unicode
    UNICODE_STRING moduleNameUnicode = {0};
    moduleNameUnicode.Buffer = driverInfo.ModuleName;
    moduleNameUnicode.MaximumLength = sizeof(driverInfo.ModuleName);
    status = RtlAnsiStringToUnicodeString(&moduleNameUnicode, &moduleNameAnsi, FALSE);
    if (!NT_SUCCESS(status))
    {
        DebugMessage("ModuleName转换失败,状态码: 0x%X\n", status);
        driverInfo.ModuleName[sizeof(driverInfo.ModuleName)/sizeof(WCHAR)-1] = L'\0';
    }

    systemDrivers[i] = driverInfo;
    DebugMessage("Driver Path: %ws\n", driverInfo.FullPathName);
    DebugMessage("Driver Name: %ws\n", driverInfo.ModuleName);
}

额外注意事项

  • 必须初始化DRIVER_INFO结构体,避免未初始化内存引发的异常。
  • 依赖strlen计算长度时,需确保内核返回的FullPathName以\0结尾(通常内核返回的结构体满足该条件)。
  • 转换前需明确UNICODE_STRING的缓冲区和最大长度,确保函数写入到我们指定的栈数组中。

内容的提问来源于stack exchange,提问作者mmorg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 20:23:10