UNICODE_STRING总结
UNICODE_STRING:
typedef struct _UNICODE_STRING {
USHORT Length; //UNICODE占⽤的内存字节数,个数*2;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING ,*PUNICODE_STRING;
参数定义:
Length-----buffer的字节长度,不包括终⽌符“NULL”
MaximumLength---buffer的的总的字节⼤⼩。Up to MaximumLength bytes may be written into the buffer without trampling memory.
Buffer---Pointer to a wide-character string指向宽字符串的指针
ANSI_STRING:
typedef struct _STRING {
USHORT Length;
USHORT MaximumLength;
PCHAR Buffer;
} STRING, *PANSI_STRING;
实例:
WCAHR temp[] = L"Hello World!"; //定义宽字符串
UNICODE_STRING str ;
str.Buffer = temp;
str.Length = wcslen(temp)*sizeof(WCHAR) //这个长度为UNICODE字符串所占⽤的字节数,即字符的个数乘以每个字符所占的字节数。通常为: 字符个数*2 ;
当使⽤UNICODE_STRING 时,⼀定要⼿动设置UNICODE_STRING 的Length和MaximumLength 成员,不要想当然的认为设置了Buffer后,Length和MaximumLength 成员就会根据Buffer被⾃动设置。由其是当⾃⼰写的函数⽤UNICODE_STRING作为参数返回时,⼀定要设置Length和MaximumLength 成员,不然很可能得到⾮预期结果。
当应⽤程序与驱动通信时,⼀般应⽤程序传⼊的字符串为ANSI,所以在驱动中应先定义ANSI_STRING,然后再使⽤RtlAnsiStringToUnicodeString 将其转换成UNICODE_STRING,作为后⽤。例:
ANSI_STRING str_a;
UNICODE_STRING str_u;
WCHAR buf_u[1024] = L"";
str_a.Buffer = InputBuffer ; //InputBuffer 为输⼊缓冲区地址
str_a.Length = str_a.MaximumLength = strlen(InputBuffer);
//开辟UNICODE内存空间
str_u.Buffer = buf_u;
str_u.Length = str_u.MaximumLength = strlen(InputBuffer) * sizeof(WCHAR);
RtlAnsiStringToUnicodeString(&str_u, &str_a , TRUE);
........
........
//当RtlAnsiStringToUnicodeString第三个参数为TRUE时,要⽤RtlFreeUnicodeString释放临时的UNICODE_STRING RtlFreeUnicodeString(&str_u);
拼接UNICODE_STRING
当拼接UNICODE_STRING 时,注意⽬标UNICODE的Length为当前UNICODE中存储字符的字节数。如:
WCHAR str1[] = L"12345";
UNICODE_STRING str2;
str2.Buffer = ExAllocatePool(NonPagedPool, wcslen(str1)*sizeof(WCHAR))
str2.Length = 0;
str2.MaximumLength = wcslen(str1)*sizeof(WCHAR);
RtlAppendUnicodeToString(&str2, str1);
UNICODE 和 ExAllocatePool
内核在UNICODE拼接或其他临时操作时,经常使⽤ExAllocatePool动态分配UNICODE的Buffer,简单情况:unicode汉字
UNICODE_STRING str;
str.Buffer = ExAllocatePool(NonPagedPool, 50*sizeof(WCHAR));
str.Length = 0;
str.MaximumLength = 50*sizeof(WCHAR);
但若是定义⼀个UNICODE的指针,则如何初始化UNICODE ?
PUNICODE_STRING pStr;
因为定义了⼀个指针,但指针⽬前并没有指向可⽤的内存地址,故先分配⼀块内存(NonPagedPool),让pStr指向这块内存。
pStr = ExAllocatePool(NonPagedPool, 50*sizeof(WCHAR));
接着初始化成员:
pStr.Length = 0;
pStr.MaximumLength = ;
如何初始化Buffer ?
因为UNICODE_STRING 是⼀个数据结构,我们申请⼀块内存来存储这个数据结构,所以这块内存不仅存储了Buffer这个我们最关⼼的字符串,⽽且还储存这个数据结构,即Length 、 MaximumLength 和 Buffer (指针)成员。因为pStr是这块内存的起始地址,所以:
&pStr.Length = (USHORT*)pStr
&pStr.MaximumLength = (USHORT*)pStr + 1
&pStr.Buffer = (USHORT*)pStr + 2
所以得:
pStr.Buffer = (WCHAR*)((USHORT*)pStr + 2 + 2 ) 因为⼀个USHORT占2个字节,⼀个指针占4个字节。这时得
出,pStr.MaximumLength = 50*sizeof(WCHAR) - (2+2+4);
(注意,以上是为了⽅便观察特意写成赋值形式,但在编译器中并不适⽤,因为=左边得为左值。)
整体:
PUNICODE_STRING pStr;
pStr = ExAllocatePool(NonPagedPool, 50*sizeof(WCHAR));
pStr.Length = 0;
pStr.Buffer = (WCHAR*)( (USHORT*)pStr + 2 + 2 ) ;
总结:
定义UNICODE_STRING 时,编译器在栈上⾃动分配了存储UNICODE_STRING这个数据结构的空间,我们唯⼀要做的就是给Buffer这个指针成员(指向)分配内存。
⽽定义PUNICODE_STRING时,在堆上分配了⼀块内存,这块内存不仅存储了Buffer,⽽且还存储了UNICODE_STRING这个数据结构。所以定义为PUNICODE_STRING时,要⽐预期的字符串⾄少多8个字节,就因为此。
pStr.MaximumLength = 50*sizeof(WCHAR) - (2+2+4);
UNICODE_STRING在驱动应⽤⽐较多,其操作⼤致有如下⼏个:
(1)初始化,常见的初始化有两种⽅式:
1.调⽤RtlInitUnicodeString,该函数原型如下:
程序代码
VOID RtlInitUnicodeString(
IN OUT PUNICODE_STRING DestinationString,
IN PCWSTR SourceString
);
该⽅法的实际原理是:将SourceString的宽字符串指针赋值给UNICODE_STRING的Buffer值,同时SourceString的长度值填⼊结构的Length域,默认的MaximumLength = Length + 2;
如:RtlInitUnicodeString(&ustrRegString,L"Hello");
2.采⽤动态分配内存的⽅式初始化,其调⽤⽅式如下:
程序代码
UNICODE_STRING ustr;
ustr.Length= 0;
ustr.MaximumLength = 256;
ustr.Buffer = (PWCHAR)ExAllocatePoolWithTag(NonPagedPool,256,MEM_TAG); //MEM_TAG为⾃定义
这样就动态分配了⼀个存储空间给ustr。
(2)字符串连接
连接常见的就是将两个UNICODE_STRING或者将WCHAR_T的字符串连接起来
⾸先,将两个UNICODE_STRING连接起来,调⽤RtlAppendUnicodeStringToString函数实现,其原型如下:
程序代码
NTSTATUS RtlAppendUnicodeStringToString(
IN OUT PUNICODE_STRING Destination,
IN PUNICODE_STRING Source
);
调⽤该函数的原理是将两者的字符串Buffer拼接起来,同时,更新对应的Length和MaxLength域。在调⽤的
过程中要注意Destination的MaxLength域,若MaxLength⼩于Destination和Source的Length域的和的时候,该函数调⽤不成功,返回0xC0000023,即缓冲区溢出错误。所以在调⽤该函数的时候,⼀定要确定Destination的MaxLength 域。
若将WCHAR_T字符串串接到UNICODE_STRING之后,则需要调⽤RtlAppendUnicodeToString,函数原型如下:
程序代码
NTSTATUS RtlAppendUnicodeToString(
IN OUT PUNICODE_STRING Destination,
IN PCWSTR Source
);
该函数和上⾯函数并没有特别多的不⼀致,但该函数较上⼀个函数不同的地⽅就是不会出现缓冲区溢出的错误,也就是即使Destination的MaxLength域为0,也可以执⾏RtlAppendUnicodeToString的操作。
还有另外⼀个需要注明的地⽅,如果UNICODE_STRING是通过RtlInitUnicodeString初始化,那么不管调⽤什么函数,修改
UNICODE_STRING值的时候,初始化使⽤的PCWSTR数组的值也会发⽣改变,因为他们指向的是同⼀个Buffer。
注上⼀例,备后⽤:
程序代码
UNICODE_STRING ustrStr,ustrName;
ustrStr.Length = wcslen(strKey) * 2;
RtlAppendUnicodeToString(&ustrStr,strKey);
ustrStr.MaximumLength = 256;
RtlAppendUnicodeToString(&ustrStr,L"\\");
RtlAppendUnicodeStringToString(&ustrStr,&ustrName);
(3)字符串转换
转换⽬的可能涉及到中⽂显⽰的问题,如果简单的UNICODE_STRING转换为wchar_t或者char的形式,采⽤RtlCopyMemory的⽅式,因为UNICODE_STRING字符串并不⼀定以\0作为结束符,所以需要使⽤RtlCopyMemory,拷贝定长的字符串。
对于中⽂显⽰的问题,采⽤ANSI_STRING的⽅式进⾏输出。从UNICODE_STRING到ANSI_STRING转换,可以通过RtlUnicodeStringToAnsiString实现,其原型如下:
程序代码
NTSTATUS RtlUnicodeStringToAnsiString(
IN OUT PANSI_STRING DestinationString,
IN PUNICODE_STRING SourceString,
IN BOOLEAN AllocateDestinationString
);
典型例⼦如下:
程序代码
UNICODE_STRING src;
ANSI_STRING dst;
RtlInitUnicodeString(&src,L”打印汉字”);
RtlUnicodeStringToAnsiString(&dst,&src,TRUE);
DbgPrint(“%Z”,&dst);
RtlFreeAnsiString(&dst);
RtlInitUnicodeString与UNICODE_STRING的区别
UNICODE_STRING是⼀个结构.当你声明⼀个UNICODE_STRING时它的成员未初始化.⽽RtlInitUnicodeString是⼀个函数⽤来初始化⼀个UNICODE_STRING.
UNICODE_STRING
The UNICODE_STRING structure is used to define Unicode strings.
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING *PUNICODE_STRING;
Members
Length
The length in bytes of the string stored in Buffer.
MaximumLength
The maximum length in bytes of Buffer.
Buffer
Pointer to a buffer used to contain a string of wide characters.
Headers
Defined in ntdef.h. Include wdm.h or ntddk.h.
Comments
The UNICODE_STRING structure is used to pass Unicode strings. Use RtlInitUnicodeString to initialize a UNICODE_STRING. If the string is NULL-terminated, Length does not include the trailing NULL.
The MaximumLength is used to indicate the length of Buffer so that if the string is passed to a conversion routine such as RtlAnsiStringToUnicodeString the returned string does not exceed the buffer size.
我经常在⽹上遇到⼼如⽕燎的提问者。他们碰到很多⼯作中的技术问题,是关于驱动开发的。其实绝⼤部分他们碰到的“巨⼤困难”是被⽼⽜们看成初级得不能再初级的问题。⽐如经常有⼈定义⼀个空的UNICODE_STRING,然后往⾥⾯拷贝字符串。结果⽆论如何都是蓝屏。也有⼈在堆栈中定义⼀个局部SPIN_LOCK,作为下⾯的同步⽤——这样⽤显然没有任何意义。我⽆法⼀⼀回答这些问题:因为往往要耐⼼的看他们的代码,才能很不容易的发现这些错误。⽽且我⼜不是总是空闲的,可以⽆休⽌的去帮⽹友阅读代码和查初级错误。但是归根结底,这些问题的出现,是因为现在写驱动的同⾏越来越多,但是做驱动开发⼜没有⽐较基础的,容易读懂的资料。为此我决定从今天开始连载⼀篇超级⼊门级的教程,来解决那些最基本的开发问题。⽼⽜们就请⽆视这篇教程,⼀笑⽽过了。
Windows驱动编程基础教程(1.1-1.3)
1.1 使⽤字符串结构
常常使⽤传统C语⾔的程序员⽐较喜欢⽤如下的⽅法定义和使⽤字符串:
char *str = { “my first string” }; // ansi字符串
wchar_t *wstr = { L”my first string” }; // unicode字符串
size_t len = strlen(str); // ansi字符串求长度
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; // 字符串的长度(字节数)
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论