内核中char*转UNICODE_STRING失败,MMGetSystemRoutineAddress调用异常求助
问题描述
我尝试使用MmGetSystemRoutineAddress函数获取指定导入函数的地址,但该函数的参数为UNICODE_STRING指针,而我的导入名称变量类型为char*。
为了将其转换为UNICODE_STRING,我尝试使用RtlInitUnicodeString函数,但该函数接收PCWSTR类型参数,无法直接传入非const的char*值。以下是我的代码:
DbgPrint("%s", image_import_by_name->Name); // >> outputs "DbgPrintEx" UNICODE_STRING routineNameByInput; RtlInitUnicodeString(&routineNameByInput, L"DbgPrintEx"); UNICODE_STRING routineNameByVar; RtlInitUnicodeString(&routineNameByVar, (PUNICODE_STRING)&image_import_by_name->Name); DbgPrint("%wZ", routineNameByInput); // >> outputs "DbgPrintEx" DbgPrint("%wZ", routineNameByVar); // >> outputs "???" MmGetSystemRoutineAddress(&routineNameByInput); // >> works perfectly MmGetSystemRoutineAddress(&routineNameByVar); // >> bluescreens MmGetSystemRoutineAddress((PUNICODE_STRING)&image_import_by_name->Name); // >> bluescreens
我还尝试通过给导入名称补零终止符来重新构造UNICODE_STRING,但均无效。
请问是否有简便的方法将char*转换为UNICODE_STRING?另外,RtlInitUnicodeString为何要接收PCWSTR类型参数?
解决方法与解释
1. char*转UNICODE_STRING的正确方法
内核模式下,ANSI字符串(char*)转UNICODE_STRING不能用强制类型转换(会因内存布局不匹配触发蓝屏),必须用内核提供的编码转换函数RtlAnsiStringToUnicodeString,具体步骤如下:
// 1. 初始化ANSI_STRING,绑定目标char*变量 ANSI_STRING ansiName; RtlInitAnsiString(&ansiName, image_import_by_name->Name); // 2. 声明并转换为UNICODE_STRING,第三个参数设为TRUE会自动分配缓冲区 UNICODE_STRING unicodeName = {0}; NTSTATUS status = RtlAnsiStringToUnicodeString(&unicodeName, &ansiName, TRUE); if (NT_SUCCESS(status)) { // 3. 调用MmGetSystemRoutineAddress获取函数地址 PVOID routineAddr = MmGetSystemRoutineAddress(&unicodeName); // 4. 用完后必须释放转换分配的缓冲区,避免内存泄漏 RtlFreeUnicodeString(&unicodeName); }
2. RtlInitUnicodeString参数为PCWSTR的原因
RtlInitUnicodeString的核心作用是把一个已存在的宽字符字符串直接绑定到UNICODE_STRING结构,它不会分配内存,也不做任何编码转换:
- 用
PCWSTR(const宽字符指针)是因为该函数仅读取原字符串,不会修改它,const修饰明确了这一设计意图,同时避免意外修改原字符串引发的问题。 - 如果传入非const指针,会误导开发者认为函数会修改原字符串,但实际上它只是将指针和字符串长度写入UNICODE_STRING的结构字段。
3. 你的代码错误分析
- 强制将
char*转PUNICODE_STRING完全错误:UNICODE_STRING是包含Length、MaximumLength、Buffer的结构体,而char*只是单字节字符指针,内存布局完全不匹配,这会导致DbgPrint输出乱码,调用MmGetSystemRoutineAddress时访问非法内存触发蓝屏。 - 手动补零无效:ANSI是单字节编码,宽字符是双字节编码,单纯补零无法完成编码转换,必须通过内核转换函数处理编码差异。
内容的提问来源于stack exchange,提问作者suljic
相关产品推荐
相关产品推荐

