minifilter驱动调用RtlCompareString比较用户数据致系统崩溃
问题诱因
核心崩溃原因是RtlCompareString API 误用,同时代码存在多处内核编程的内存安全隐患:
RtlCompareString不是C标准库strcmp类的裸字符串比较函数,它的函数原型要求传入的两个参数均为STRING(即ANSI_STRING)结构体指针,而非PCHAR类型的C风格以'\0'结尾的字符串指针:
你直接传入裸LONG RtlCompareString( _In_ const STRING *String1, _In_ const STRING *String2, _In_ BOOLEAN CaseInSensitive );PCHAR时,函数会把指针指向的内存前2字节解析为字符串长度、后续2字节解析为缓冲区最大长度、64位系统下再往后8字节解析为实际字符串缓冲区地址,最终会访问完全随机的非法内存地址,触发0xC0000005内存访问违例,对应你看到的0x0000003b蓝屏。你提到的比较OutputBuffer时“正常运行”只是巧合:OutputBuffer前几个字节的随机值刚好没有让函数访问到缺页的非法地址,本质依然是错误写法,随时会触发崩溃。- 输入缓冲区处理完全缺失安全校验:
- 你直接对用户态传入的
InputBuffer调用strlen、DbgPrintEx %s,但微过滤端口回调给出的用户输入缓冲区不保证以'\0'结尾,strlen会持续越界向后读取内存直到碰到0值,极易触发读违例蓝屏。 - 完全没有校验
InputBuffer是否为空、InputBufferLength是否符合最小长度要求,用户态传入空缓冲区时直接访问会立刻崩溃。
- 你直接对用户态传入的
- 输出缓冲区处理存在严重越界风险:
- 你仅判断了
strlen(msg)和OutputBufferLength的大小,后续strncpy硬编码拷贝500字节,若OutputBufferLength小于500会直接越界写内核内存,触发写违例蓝屏。 - 未对回调输出参数
RetLength赋值,该参数需要返回实际写入OutputBuffer的字节数,未赋值会使用栈上随机值作为返回长度,后续会引发随机内存破坏。
- 你仅判断了
修复方案
- 替换错误的字符串比较逻辑:如果要使用
RtlCompareString,必须先将待比较的内容初始化为合法的ANSI_STRING结构体,禁止直接传入裸PCHAR;也可以直接基于缓冲区长度用RtlCompareMemory做内容匹配,更适配无null结尾的原始输入缓冲区场景。 - 所有用户态传入的缓冲区访问前必须做合法性校验:检查指针是否为空、长度是否满足要求,禁止在用户原始缓冲区上调用依赖'\0'结尾的C字符串函数。
- 写入输出缓冲区时严格控制拷贝长度不超过
OutputBufferLength,必须正确填充RetLength参数。
修复后的参考代码如下:
NTSTATUS MiniSendRecv(PVOID portcookie, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength, PULONG RetLength) { UNREFERENCED_PARAMETER(portcookie); const char* msg = "Hi User"; const ULONG msgLen = (ULONG)strlen(msg); const char* stopCmd = "status=stop"; const ULONG stopCmdLen = (ULONG)strlen(stopCmd); // 先校验返回长度参数地址合法性 if (!RetLength) { return STATUS_INVALID_PARAMETER; } *RetLength = 0; // 校验输入缓冲区合法性 if (InputBuffer && InputBufferLength >= stopCmdLen) { // 匹配长度足够才做比较,不依赖null结尾,用RtlCompareMemory直接比较指定长度内容 if (RtlCompareMemory(InputBuffer, stopCmd, stopCmdLen) == stopCmdLen) { DbgPrintEx(0, 0, "stop requested\n"); } DbgPrintEx(0, 0, "User input length: %lu\n", InputBufferLength); } // 校验输出缓冲区长度是否足够 if (OutputBufferLength < msgLen) { DbgPrintEx(0, 0, "output buffer too small\n"); return STATUS_BUFFER_TOO_SMALL; } // 拷贝实际长度的内容,不要硬编码拷贝长度 RtlCopyMemory(OutputBuffer, msg, msgLen); // 正确赋值返回长度 *RetLength = msgLen; return STATUS_SUCCESS; }
注:如果需要做大小写不敏感的比较,可以先将输入缓冲区和目标字符串都封装为
ANSI_STRING结构(基于传入的InputBufferLength设置Length成员,不要自行计算字符串长度),再调用RtlCompareString即可,示例写法:ANSI_STRING inputStr, stopStr; RtlInitAnsiString(&stopStr, "status=stop"); inputStr.Length = (USHORT)min(InputBufferLength, USHRT_MAX); inputStr.MaximumLength = inputStr.Length; inputStr.Buffer = (PCHAR)InputBuffer; if (RtlCompareString(&inputStr, &stopStr, TRUE) == 0) { // 匹配成功逻辑 }
内容的提问来源于stack exchange,提问作者hashy
相关产品推荐
相关产品推荐

