内存中字符串编程
一丶UNICODE_STRING讲解
1.1简介
在内核中.我们的字符有 char类型的.也有wchar_t类型的.分别是宽字符
跟窄字符.但是这种都不建议使用了.而内核提供了两个新的结构体让我们使用
分别别:
UNICODE_STRING
ANSI_STRING
随便哪个结构体进行简介.
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
内核中的UNICODE_STRING其实是个结构体.
简介一下:
参数一: 字符串的字节数.不带 结尾的字节数. 注意是字节数
参数二: Buffer的最大字节数.如果Buffer存储字符串.那么字节数就是
wcslen(Buffer)+sizeof(wchar_t)也就是说说带 结尾.
参数三: Buffer缓冲区.存放字符串的指针.
1.2字符串的操作函数
既然有了这些结构体.那么就会有相应的提供操作字符串的函数.
如我们在WDK文档查询这个结构体的时候.
下面就会有个 See Also告诉我们字符串操作相关的一系列函数.
函数名 | 作用 |
---|---|
RtlUnicodeStringInit | 安全的初始化UNICODE_STRING.不安全的有RtlinitUnicodeString.安全的动词放后面 |
RtlStringCbCopyUnicodeString | 将UNICODE_STRING按照字节大小,拷贝到一个wchar_t的缓冲区中 |
RtlUnicodeStringCat | 拼接一个UNICODE_STRIGN |
RtlUnicodeStringCatString | 将UNICODE_STRING 拼接一个PCTSTR的字符串. |
RtlUnicodeStringCbCatN | 都是传入两个UNICODE_STRING结构体.多了一个参数.这个参数指定你要拼接的字节数进行拼接.不用完整拼接了. |
具体函数查询WDK帮助文档即可.
1.3UNICODE_STRING的使用
说的UNICODE_STRING的使用.这里需要注意一下.
1.3.1 在栈上初始化一个Buffer
UNICODE_STRING TestUCString = [0];
WCHAR wzData[0x100] = L"Hello World""
RtlInitUnicodeString(&TetUCString,wzData);
这样初始化的方式.是将UNICODE_STRING的 Buffer指针指向栈内存.
伪代码:
Buffer = wzData;
最重要的一点就是此时你可以使用Rtl拷贝函数对这个UNICODE_STRING进行操作了.
因为内存是有的.
RtlUnicodeStringCopyString是可以对这个内存进行拷贝的.
1.3.2 全局初始化
UNICODE_STRING ustr;
RtlUnicodeStringInit(&ustr,L"HelloWorld");
此时的UNICODE_STRING里面的Buffer指针是指向全局常量区的 HelloWorld的.所以此时你使用拷贝函数就会出错.很可能就会蓝屏
1.3.3堆上初始化一个缓冲区.
UNICODE_STRING ustr = {0};
ULONG length = (wcslen(L"HelloWorld") + ) * sizeof(WCHAR);
ustr.Buffer = ExAllocatePoolWithTag(PagedPool,MAX_PATH 8 sizeof(WCHAR),"niBI");
if (ustr.Buffer == NULL)
return ;
清空缓存
RtlZeroMemory(ustr.Buffer,MAX_PATH *sizeof(WCHAR));
wcscpy(ustr.Buffer,L"HelloWorld");
ustr.length = length;
ustr.Maximumlength = MAX_PATH * sizeof(WCHAR);
DbgPrint("%wZ",&ustr);
ExFreePool(ustr.Buffer);
上面的UNICODE_STRING 的Buffer指向一个堆内存.这个内存是我们分配的.
二丶内核中其它常用字符串函数的使用
2.1初始化UNICODE_STRING
初始化一个 UNICODE_STRING 字符串
UNICODE_STRING str;
RtlInitUnicodeString(&str,L"111");
RtlUnicodeStringInit(&str,L"111");
两种方法都可以,第二种是安全函数. 需要包含头文件 "ntstrsafe.h"
2.2拷贝字符串UNICODE_STRING
拷贝字符串分为你给不给UNICODE_STRING 分配内存跟不分配内存的拷贝.
不分配内存使用栈内存的分配
UNICODE_STRING str;
UNICODE_STRING dst;
UNICODE_STRING src = RTL_CONSTANT_STRING(L"HelloWorld");
wchar_t wzBuffer[512] = { 0 };
RtlInitEmptyUnicodeString(&dst, wzBuffer,512 * sizeof(WCHAR));
RtlCopyUnicodeString(&dst, &src);//将src UNICODE_STRING拷贝到dst
//RtlUnicodeStringcbCopyN();//这是将缓冲区拷贝给UNICODE_STRING的. 而不是U拷贝给U
上边两个遇到的定义分别都有源码,如下
char _RTL_CONSTANT_STRING_type_check(const void *s);
#define _RTL_CONSTANT_STRING_remove_const_macro(s) (s)
#endif
#define RTL_CONSTANT_STRING(s)
{
sizeof( s ) - sizeof( (s)[0] ),
sizeof( s ) / sizeof(_RTL_CONSTANT_STRING_type_check(s)),
_RTL_CONSTANT_STRING_remove_const_macro(s)
}
这个其实就像相当于将一个WCHAR *给UNICODE_STRING
wrk的定义
#define RtlInitEmptyUnicodeString(_ucStr,_buf,_bufSize)
((_ucStr)->Buffer = (_buf),
(_ucStr)->Length = 0,
(_ucStr)->MaximumLength = (USHORT)(_bufSize))
这个其实就是将缓冲区的地址,给UNICODE_STRING中的buff.并且长度置为0.
这里会有坑.虽然你的 u.buffer = wszBuffer.但是length=0;所以你拷贝的时候很可能会蓝屏.或者拷贝失败.原因就是长度为0.你拷贝一个东西也会去判断它的长度把.
2.3 字符串的拼接
字符串的拼接也是很常用的.
windows内核中提供了几个 Rtl函数供我们使用.
RtlAppenUnicodeToString();
RtlAppendUnicodeStringToString();
两个用法都是一样唯一不同就是第二个参数.
函数1,是直接拼接一个 PWSTR字符串.也就是宽字符
函数2:是拼接一个UNICODE_STRING
UNICODE_STRING dst;
UNICODE_STRING src = RTL_CONSTANT_STRING(L"HelloWorld");
wchar_t wzBuffer[512] = { 0 };
RtlInitEmptyUnicodeString(&dst, wzBuffer,512 * sizeof(WCHAR));
RtlCopyUnicodeString(&dst, &src);//将src UNICODE_STRING拷贝到dst
RtlAppendUnicodeToString(&dst, L"123");
RtlAppendUnicodeStringToString(&dst, &src);
2.4 字符串的格式化输出
在C/C++下.我们常用的格式化字符串是 sprintf swprintf wsprintf...
在内核中依然可以使用.但是windows内核为我们提供了Rtl函数所以可以不用它了.
如下:
RtlStringCbPrintW()
用法:
UNICODE_STRING dst;
UNICODE_STRING src = RTL_CONSTANT_STRING(L"HelloWorld");
char szbuffer[] = "111";
wchar_t wzBuffer[512] = { 0 };
RtlInitEmptyUnicodeString(&dst, wzBuffer,512 * sizeof(WCHAR));
RtlCopyUnicodeString(&dst, &src);//将src UNICODE_STRING拷贝到dst
RtlStringCbPrintfW(dst.Buffer,512*sizeof(WCHAR), L"%wZ %ws %s", &src, src.Buffer, szbuffer);
使用这个函数要注意返回值,如果超过缓冲区则会输出 STATUS_BUFFER_OVERFLOW
%wZ 输出UNICODE_STRING 字符
%ws 输出0结尾的宽字符
%s 输出0结尾的Ansi字符.