字符串UNICODE_STRING
在Windows下编程,根据字符串的使用分为UNICODE编程和我们平常使用的多字节编程。
我们开发Windows驱动使用的是C语言。在C语言中定义的字符串是以\0
为结尾表示一个字符串的结束。
char* pStr="www.bytekits.com";// ansi字符串
wchar_t pwStr = L"www.bytekits.com";// unicode字符串
size_t size = strlen(pStr);// ansi字符串求长度
size_t wsize = wcslen(pwStr);// unicode字符串求长度
printf("%s size is %d\n",pStr,len); //打印ansi字符串长度
printf("%ws size is %d\n",pwStr,wlen);//打印unicode字符串求长度
从上面的代码可以看到,字符串的长度是使用strlen和wcslen函数获取的。但这两个函数获取长度的方式是从字符串的内存头开始,按字符进行计数,当遇到’\0’或L’\0’时结束。所以使用这种字符串相当的不安全。很容易导致缓冲溢出漏洞。这是因为没有任何地方确切的表明一个字符串的长度。仅仅用一个’\0’字符来标明这个字符串的结束。一旦碰到根本就没有空结束的字符串(可能是攻击者恶意的输入、或者是编程错误导致的意外),程序就可能陷入崩溃。
使用高级C++特性的编码者则容易忽略这个问题。因为常常使用std::string和CString这样高级的类。不用去担忧字符串的安全性了。
windows驱动中的字符串
在Windows驱动开发中,一般不再用空来表示一个字符串的结束。而是定义了如下的一个结构:
typedef struct _UNICODE_STRING {
USHORT Length; // 字符串的长度(字节数)
USHORT MaximumLength; // 字符串缓冲区的长度(字节数)
PWSTR Buffer; // 字符串缓冲区
} UNICODE_STRING, *PUNICODE_STRING;
以上是Unicode字符串,一个字符为双字节。与之对应的还有一个Ansi字符串。Ansi字符串就是C语言中常用的单字节表示一个字符的窄字符串。
typedef struct _STRING {
USHORT Length;
USHORT MaximumLength;
PSTR Buffer;
} ANSI_STRING, *PANSI_STRING;
说明:这里的长度Length和MaximumLength是以字节长度计算的。
在驱动开发中四处可见的是Unicode字符串。因此可以说:Windows的内核是使用Uincode编码的。ANSI_STRING仅仅在某些碰到窄字符的场合使用。而且这种场合非常罕见。
在上面的结构体中:
- Length变量明确指定字符串的长度
- MaximumLength表示实际的内存空间大小.
- Buffer指针指向字符串的内存。
UNICODE_STRING并不保证Buffer中的字符串是以空结束的。因此,类似下面的做法都是错误的,可能会会导致内核崩溃:
UNICODE_STRING str;
…
len = wcslen(str.Buffer); // 试图求长度。
DbgPrint(“%ws”,str.Buffer); // 试图打印str.Buffer。
如果要用以上的方法,必须在编码中保证Buffer始终是以空结束。但这又是一个麻烦的问题。所以,使用微软提供的Rtl系列函数来操作字符串,才是正确的方法。