UNICODESTRING 总结Word文档格式.docx

上传人:b****4 文档编号:17610778 上传时间:2022-12-07 格式:DOCX 页数:9 大小:22.64KB
下载 相关 举报
UNICODESTRING 总结Word文档格式.docx_第1页
第1页 / 共9页
UNICODESTRING 总结Word文档格式.docx_第2页
第2页 / 共9页
UNICODESTRING 总结Word文档格式.docx_第3页
第3页 / 共9页
UNICODESTRING 总结Word文档格式.docx_第4页
第4页 / 共9页
UNICODESTRING 总结Word文档格式.docx_第5页
第5页 / 共9页
点击查看更多>>
下载资源
资源描述

UNICODESTRING 总结Word文档格式.docx

《UNICODESTRING 总结Word文档格式.docx》由会员分享,可在线阅读,更多相关《UNICODESTRING 总结Word文档格式.docx(9页珍藏版)》请在冰豆网上搜索。

UNICODESTRING 总结Word文档格式.docx

//这个长度为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;

WCHARbuf_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中存储字符的字节数。

如:

WCHARstr1[]=L"

12345"

UNICODE_STRINGstr2;

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_STRINGstr;

str.Buffer=ExAllocatePool(NonPagedPool,50*sizeof(WCHAR));

str.Length=0;

str.MaximumLength=50*sizeof(WCHAR);

但若是定义一个UNICODE的指针,则如何初始化UNICODE?

PUNICODE_STRINGpStr;

因为定义了一个指针,但指针目前并没有指向可用的内存地址,故先分配一块内存(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);

(注意,以上是为了方便观察特意写成赋值形式,但在编译器中并不适用,因为=左边得为左值。

整体:

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(

INOUTPUNICODE_STRING 

DestinationString,

INPCWSTR 

SourceString

);

该方法的实际原理是:

将SourceString的宽字符串指针赋值给UNICODE_STRING的Buffer值,同时SourceString的长度值填入结构的Length域,默认的MaximumLength=Length+2;

RtlInitUnicodeString(&

ustrRegString,L"

Hello"

2.采用动态分配内存的方式初始化,其调用方式如下:

UNICODE_STRINGustr;

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(

Destination,

INPUNICODE_STRING 

Source

调用该函数的原理是将两者的字符串Buffer拼接起来,同时,更新对应的Length和MaxLength域。

在调用的

过程中要注意Destination的MaxLength域,若MaxLength小于Destination和Source的Length域的和的时候,该函数调用不成功,返回0xC0000023,即缓冲区溢出错误。

所以在调用该函数的时候,一定要确定Destination的MaxLength域。

若将WCHAR_T字符串串接到UNICODE_STRING之后,则需要调用RtlAppendUnicodeToString,函数原型如下:

RtlAppendUnicodeToString(

该函数和上面函数并没有特别多的不一致,但该函数较上一个函数不同的地方就是不会出现缓冲区溢出的错误,也就是即使Destination的MaxLength域为0,也可以执行RtlAppendUnicodeToString的操作。

还有另外一个需要注明的地方,如果UNICODE_STRING是通过RtlInitUnicodeString初始化,那么不管调用什么函数,修改UNICODE_STRING值的时候,初始化使用的PCWSTR数组的值也会发生改变,因为他们指向的是同一个Buffer。

注上一例,备后用:

ustrStr,ustrName;

ustrStr.Length=wcslen(strKey)*2;

ustrStr,strKey);

ustrStr.MaximumLength=256;

ustrStr,L"

\\"

RtlAppendUnicodeStringToString(&

ustrStr,&

ustrName);

(3)字符串转换

转换目的可能涉及到中文显示的问题,如果简单的UNICODE_STRING转换为wchar_t或者char的形式,采用RtlCopyMemory的方式,因为UNICODE_STRING字符串并不一定以\0作为结束符,所以需要使用RtlCopyMemory,拷贝定长的字符串。

对于中文显示的问题,采用ANSI_STRING的方式进行输出。

从UNICODE_STRING到ANSI_STRING转换,可以通过RtlUnicodeStringToAnsiString实现,其原型如下:

RtlUnicodeStringToAnsiString(

INOUTPANSI_STRING 

SourceString,

INBOOLEAN 

AllocateDestinationString

典型例子如下:

UNICODE_STRINGsrc;

ANSI_STRINGdst;

src,L”打印汉字”);

RtlUnicodeStringToAnsiString(&

dst,&

src,TRUE);

DbgPrint(“%Z”,&

dst);

RtlFreeAnsiString(&

RtlInitUnicodeString与UNICODE_STRING的区别

UNICODE_STRING是一个结构.当你声明一个UNICODE_STRING时它的成员未初始化.而RtlInitUnicodeString是一个函数用来初始化一个UNICODE_STRING.

UNICODE_STRING

TheUNICODE_STRINGstructureisusedtodefineUnicodestrings.

USHORTLength;

USHORTMaximumLength;

PWSTRBuffer;

}UNICODE_STRING*PUNICODE_STRING;

Members

Length

ThelengthinbytesofthestringstoredinBuffer.

MaximumLength

ThemaximumlengthinbytesofBuffer.

Buffer

Pointertoabufferusedtocontainastringofwidecharacters.

Headers

Definedinntdef.h.Includewdm.horntddk.h.

Comments

TheUNICODE_STRINGstructureisusedtopassUnicodestrings.UseRtlInitUnicodeStringtoinitializeaUNICODE_STRING.

IfthestringisNULL-terminated,LengthdoesnotincludethetrailingNULL.

TheMaximumLengthisusedtoindicatethelengthofBuffersothatifthestringispassedtoaconversionroutinesuchasRtlAnsiStringToUnicodeStringthereturnedstringdoesnotexceedthebuffersize.

我经常在网上遇到心如火燎的提问者。

他们碰到很多工作中的技术问题,是关于驱动开发的。

其实绝大部分他们碰到的“巨大困难”是被老牛们看成初级得不能再初级的问题。

比如经常有人定义一个空的UNICODE_STRING,然后往里面拷贝字符串。

结果无论如何都是蓝屏。

也有人在堆栈中定义一个局部SPIN_LOCK,作为下面的同步用——这样用显然没有任何意义。

我无法一一回答这些问题:

因为往往要耐心的看他们的代码,才能很不容易的发现这些错误。

而且我又不是总是空闲的,可以无休止的去帮网友阅读代码和查找初级错误。

但是归根结底,这些问题的出现,是因为现在写驱动的同行越来越多,但是做驱动开发又没有比较基础的,容易读懂的资料。

为此我决定从今天开始连载一篇超级入门级的教程,来解决那些最基本的开发问题。

老牛们就请无视这篇教程,一笑而过了。

Windows驱动编程基础教程(1.1-1.3)

1.1使用字符串结构

常常使用传统C语言的程序员比较喜欢用如下的方法定义和使用字符串:

char*str={“myfirststring”};

//ansi字符串

wchar_t*wstr={L”myfirststring”};

//unicode字符串

size_tlen=strlen(str);

//ansi字符串求长度

size_twlen=wcslen(wstr);

//unicode字符串求长度

printf(“%s%ws%d%d”,str,wstr,len,wlen);

//打印两种字符串

但是实际上这种字符串相当的不安全。

很容易导致缓冲溢出漏洞。

这是因为没有任何地方确切的表明一个字符串的长度。

仅仅用一个’\0’字符来标明这个字符串的结束。

一旦碰到根本就没有空结束的字符串(可能是攻击者恶意的输入、或者是编程错误导致的意外),程序就可能陷入崩溃。

使用高级C++特性的编码者则容易忽略这个问题。

因为常常使用std:

:

string和CString这样高级的类。

不用去担忧字符串的安全性了。

在驱动开发中,一般不再用空来表示一个字符串的结束。

而是定义了如下的一个结构:

//字符串的长度(字节数)

//字符串缓冲区的长度(字节数)

//字符串缓冲区

}UNICODE_STRING,*PUNICODE_STRING;

以上是Unicode字符串,一个字符为双字节。

与之对应的还有一个Ansi字符串。

Ansi字符串就是C语言中常用的单字节表示一个字符的窄字符串。

PSTRBuffer;

}ANSI_STRING,*PANSI_STRING;

在驱动开发中四处可见的是Unicode字符串。

因此可以说:

Windows的内核是使用Uincode编码的。

ANSI_STRING仅仅在某些碰到窄字符的场合使用。

而且这种场合非常罕见。

UNICODE_STRING并不保证Buffer中的字符串是以空结束的。

因此,类似下面的做法都是错误的,可能会会导致内核崩溃:

len=wcslen(str.Buffer);

//试图求长度。

DbgPrint(“%ws”,str.Buffer);

//试图打印str.Buffer。

如果要用以上的方法,必须在编码中保证Buffer始终是以空结束。

但这又是一个麻烦的问题。

所以,使用微软提供的Rtl系列函数来操作字符串,才是正确的方法。

下文逐步的讲述这个系列的函数的使用。

1.2字符串的初始化

请回顾之前的UNICODE_STRING结构。

读者应该可以注意到,这个结构中并不含有字符串缓冲的空间。

这是一个初学者常见的出问题的来源。

以下的代码是完全错误的,内核会立刻崩溃:

wcscpy(str.Buffer,L”myfirststring!

”);

str.Length=str.MaximumLength=wcslen(L”myfirststring!

”)*sizeof(WCHAR);

以上的代码定义了一个字符串并试图初始化它的值。

但是非常遗憾这样做是不对的。

因为str.Buffer只是一个未初始化的指针。

它并没有指向有意义的空间。

相反以下的方法是正确的:

//先定义后,再定义空间

str.Buffer=L”myfirststring!

”;

……

上面代码的第二行手写的常数字符串在代码中形成了“常数”内存空间。

这个空间位于代码段。

将被分配于可执行页面上。

一般的情况下不可写。

为此,要注意的是这个字符串空间一旦初始化就不要再更改。

否则可能引发系统的保护异常。

实际上更好的写法如下:

//请分析一下为何这样写是对的:

UNICODE_STRINGstr={

sizeof(L”myfirststring!

”)–sizeof((L”myfirststring!

”)[0]),

”),

L”myfirst_string!

”};

但是这样定义一个字符串实在太繁琐了。

但是在头文件ntdef.h中有一个宏方便这种定义。

使用这个宏之后,我们就可以简单的定义一个常数字符串如下:

#include<

ntdef.h>

UNICODE_STRINGstr=RTL_CONSTANT_STRING(L“myfirststring!

这只能在定义这个字符串的时候使用。

为了随时初始化一个字符串,可以使用RtlInitUnicodeString。

示例如下:

str,L”myfirststring!

用本小节的方法初始化的字符串,不用担心内存释放方面的问题。

因为我们并没有分配任何内存。

1.3字符串的拷贝

因为字符串不再是空结束的,所以使用wcscpy来拷贝字符串是不行的。

UNICODE_STRING可以用RtlCopyUnicodeString来进行拷贝。

在进行这种拷贝的时候,最需要注意的一点是:

拷贝目的字符串的Buffer必须有足够的空间。

如果Buffer的空间不足,字符串会拷贝不完全。

这是一个比较隐蔽的错误。

下面举一个例子。

UNICODE_STRINGdst;

//目标字符串

WCHARdst_buf[256];

//我们现在还不会分配内存,所以先定义缓冲区

UNICODE_STRINGsrc=RTL_CONST_STRING(L”Mysourcestring!

//把目标字符串初始化为拥有缓冲区长度为256的UNICODE_STRING空串。

RtlInitEmptyString(dst,dst_buf,256*sizeof(WCHAR));

RtlCopyUnicodeString(&

src);

//字符串拷贝!

以上这个拷贝之所以可以成功,是因为256比L”Mysourcestring!

”的长度要大。

如果小,则拷贝也不会出现任何明示的错误。

但是拷贝结束之后,与使用者的目标不符,字符串实际上被截短了。

我曾经犯过的一个错误是没有调用RtlInitEmptySt

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 初中教育 > 理化生

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1