一、前言
大量的系统安全问题是由于薄弱的缓冲处理以及由此产生的缓冲区溢出造成的,而薄弱的缓冲区处理常常与字符串操作相关。c/c++语言运行库提供的标准字符串操作函数(strcpy, strcat, sprintf等)不能阻止在超出字符串尾端的写入。
基于Windows XP SP1以及随后的操作系统的Windows DDK版本提供了安全字符串函数(safe string functions)。这类函数被设计的目的是用来取代相同功能的c/c++标准函数和其它微软提供的库函数。这类函数具有以下特征:
- 每个函数以目标缓冲区所占的字节大小作为其一个输入参数,因此可以保证在写入时不会超出缓冲区末端。
- 每个函数的输出字符串均以NULL结尾(null-terminate),即使该函数可能会对正确的结果进行截断。
- 所有函数均有返回值,类型为NTSTATUS,只有返回STATUS_SUCCESS时,操作结果才正确。
- 每个函数均有两种类型的版本,按字节或者按字符数。例如,RtlStringCbCatW和RtlStringCchCatW。
- 每个函数均有支持双字节的unicode字符(以W作为后缀)和单字节的ANSI字符(以A作为后缀)的版本。例如:RtlStringCbCatW和RtlStringCbCatA。
- 大部分函数有提供扩展版本的函数(以Ex作为后缀),例如,RtlStringCbCatW和RtlStringCbCatExW。
二、如何在内核驱动代码中引入安全字符串函数
有两种方式可以引入安全字符串函数:
l 以内联的方式引入,包含在ntstrsafe.h中
l 在链接时以库的方式引入
其中,如果代码需要在系统为Windows XP及以后版本运行时,可以使用内联的方式;如果代码需要运行在早于Windows XP时,则必须使用链接库的方式。
以内联方式引入
只需包含头文件即可
#include <ntstrsafe.h>
以链接库的方式
- 在包含头文件之前先定义宏
#define NTSTRSAFE_LIB
#include <ntstrsafe.h>
- 在项目的sources文件中,添加一TARGETLIBS条目如下: $(DDK_LIB_PATH) tstrsafe.lib.
在默认情况下,当引入了安全字符串函数后,那些被取代的c/c++运行库函数将变得无效,编译是会报错,提示需要使用安全字符串函数。
如果还希望继续使用c/c++运行库函数,即在使用安全字符串函数的时候,c/c++运行库函数还可以继续使用,则需要在包含ntstrsafe.h之前先定义宏NTSTRSAFE_NO_DEPRECATE
#define NTSTRSAFE_NO_DEPRECATE
The maximum number of characters that any ANSI or Unicode string can contain is STRSAFE_MAX_CCH. This constant is defined in ntstrsafe.h.
字符串最长长度为STRSAFE_MAX_CCH,该宏在ntstrsafe.h中定义。另外,如果一个字符串需要被转换成UNICODE_STRING结构,则该字符串长度不能超过65535.
三、内核模式安全字符串函数概述
下表概述了可以在内核驱动中使用的安全字符串函数,并指明了它们用来何种类型的c/c++运行库函数。
说明:
函数名含有Cb的是以字节数为单位,含有Cch的是以字符数为单位。
函数名 |
作用 |
取代 |
RtlStringCbCat |
将源字符串连接到目的字符串的末尾 |
strcat |
RtlStringCbCatN |
将源字符串指定数目的字符连接到目的字符串的末尾 |
strncat |
RtlStringCbCopy |
将源字符串拷贝到目的字符串 |
strcpy |
RtlStringCbCopyN |
将源字符串指定数目的字符拷贝到目的字符串 |
strncpy |
RtlStringCbLength |
确定字符串的长度 |
strlen |
RtlStringCbPrintf |
格式化输出 |
sprintf |
RtlStringCbVPrintf |
可变格式化输出 |
vsprintf |
各个函数的作用可以通过它所取代的c/c++函数可以大概看出,具体用法请查阅DDK帮助文档。
驱动中使用的字符串操作函数 ,这里给出ANSI和UNICODE的对比
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ansi转换Unicode
RtlAnsiStringToUnicodeString
Unicode转换Ansi
RtlUnicodeStringToAnsiString
以上两个函数第三个参数都为True时需要使用RtlFreeUnicodeString/RtlFreeAnsiString来释放空间。我设置过False 但是测试时候 第一次可以 第二次就蓝屏了。如果读者知道为什么 可以在下方评论留言给我。谢谢
KdPrint输出unicode_string类型字符串使用 %wZ
// convert the name to UNICODE_STRING
wchar_t name[100];
auto status =
RtlStringCchPrintfW(name, RTL_NUMBER_OF(name), L"%S", export_name);
if (!NT_SUCCESS(status)) {
return true;
}
UNICODE_STRING name_u = {};
RtlInitUnicodeString(&name_u, name);