1,使用字符串结构
常常使用传统C语言的程序员比较喜欢用如下的方法定义和使用字符串:
char *str ={"my first string"}; //ANSI字符串 wchar_t *wstr = {L"my first string"}; //UNICODE字符串 size_t len = strlen(str); //求出ANSC字符长度 size_t wLen = wcslen(wstr); // 求出unicode字符串长度 printf("%s %ws %d %d", str, wstr, len, wLen); //---输出两种那个字符串但是,实际上这种字符串相当不安全,很容易导致缓冲溢出漏洞。因为没有任何地方确切的表明一个字符串的长度。仅仅用一个"\0"字符来表明字符串的结束。一旦碰到根本就没有空结束的字符串(坑是攻击者的恶意输入,或者是编程错误导致的意外),程序就可能崩溃。
使用高级C++特性的编码者则容易忽略这个问题,以为常常是使用 std::string 和 CString 这样的高级类,不用去担忧字符串的安全性。
在驱动开发中,一般不再用空来表示一个字符串的结束,而是定义了如下的一个结构体:
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; //---字符串缓冲区的长度(字节数) PWSTR Buffer; //---字符串缓冲区。 }ANSI_STRING, *PANSI_STRING;
在驱动开发者中,到处可见的是UNicode字符串,因此可以说,Windows的内核是使用unicode编码的。ANSI_STRING 仅仅在某些碰到窄字符的场合使用,而且这种场合非常罕见。
UNICODE_STRING 并不保证Buffer中的字符串是以空结束的。因此,类下面的做法都是错误的。可能导致内核崩溃:
UNICODE_STRING str; ........ len = wclen(str.Buffer); ///---求长度, DbgPrin("%ws", str.Buffer); ///---输出字符串如果要使用以上的方法,必须在编码中保证buffer始终是以空结束,但这又是一个麻烦的问题。所以,使用微软提供的Rtl系列函数来操作字符串,才是正确的方法。
--------------------摘自《[天书夜读-从汇编语言到Windows内核编程]》