类型
windows 内核有四种字符串类型
- CHAR
- WCHAR
- UNICODE_STRING
- ANSI_STRING
CHAR 和WCHAR是以NULL结尾的字符串类型,UNICODE_STRING和ANSI_STRING是字符串结构体
除此之外,还可以使用一些C语言的字符串函数 ,如: strstr(),strlen(),strcpy(),
UNICODE_STRING
typedef struct _UNICODE_STRING {USHORT Length;USHORT MaximumLength;#ifdef MIDL_PASS[size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer;#else // MIDL_PASS_Field_size_bytes_part_opt_(MaximumLength, Length) PWCH Buffer;#endif // MIDL_PASS} UNICODE_STRING;
UNICODE_STRING 包含3个成员
- Length:Buffer成员指向的buffer 占用了多少给字节,
- 等于字符长度乘以字符数,单位为字节
- Length 不代表字符串有多少个字符
- MaximumLength:字符缓冲区分配的内存字节数。
- Buffer:指向WCHAR(宽字符)的指针
初始化UNICODE_STRING
使用 RTL_CONSANT_STRING 的宏初始化
UNICODE_STRING string = RTL_CONSTANT_STRING(L"hello driver\r\n");DbgPrint("%wZ",&string);
DbgPrint() 函数 使用 “%wZ” 来指明打印内容为UNICODE_SSTRING,函数需要传递一个UNICODE_STRING类型的指针
RtlInitUnicodeString
UNICODE_STRING string2 = { 0 };RtlInitUnicodeString(&string2,L"hello driver2\r\n");DbgPrint("%wZ",&string2);
需要先将 UNICODE_STRING结构体赋值为0,再用函数初始化
手动初始化
UNICODE_STRING string3;WCHAR strbuffer[120] = {0};string3.Buffer = strbuffer;string3.MaximumLength = sizeof(WCHAR) * 120;string3.Length = wcslen(L"hello driver3\r\n") * sizeof(WCHAR);wcscpy(string3.Buffer,L"hello driver3\r\n");DbgPrint("%wZ",&string3);
不过要注意的是strcpy 只能操作以null结尾的字符串

完整代码
#include <ntddk.h>VOID Unload(IN PDRIVER_OBJECT DriverObject) {DbgPrint("driver unload\r\n");}NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegisteryPath) {DriverObject->DriverUnload = Unload;UNICODE_STRING string1 = RTL_CONSTANT_STRING(L"hello driver1\r\n");DbgPrint("%wZ",&string1);UNICODE_STRING string2 = { 0 };RtlInitUnicodeString(&string2,L"hello driver2\r\n");DbgPrint("%wZ",&string2);UNICODE_STRING string3;WCHAR strbuffer[120] = {0};string3.Buffer = strbuffer;string3.MaximumLength = sizeof(WCHAR) * 120;string3.Length = wcslen(L"hello driver3\r\n") * sizeof(WCHAR);wcscpy(string3.Buffer,L"hello driver3\r\n");DbgPrint("%wZ",&string3);return STATUS_SUCCESS;}
ANSI_STRING
typedef struct _STRING {USHORT Length;USHORT MaximumLength;#ifdef MIDL_PASS[size_is(MaximumLength), length_is(Length) ]#endif // MIDL_PASS_Field_size_bytes_part_opt_(MaximumLength, Length) PCHAR Buffer;} STRING;typedef STRING *PSTRING;typedef STRING ANSI_STRING;
和 UNICODE_STRING有相同的结构体,但是buffer指向的是一个asiic 字符串,一个字节一个字符。
这意味着 Length 表示这个字符串有多少个字符
字符串操作
复制
WCHAR strbuf[60] = { 0 };UNICODE_STRING sourecstring = RTL_CONSTANT_STRING(L"hello driver \r\n");UNICODE_STRING deststring = { 0 };deststring.Buffer = strbuf;deststring.Length = deststring.MaximumLength = 60;RtlCopyUnicodeString(&deststring,&sourecstring);DbgPrint("%wZ",&deststring);
字符串转为大写
- 使用RtlUpcaseUnicodeString 函数来转换
- 此函数不会改变原来的字符,而会将转换后的字符串写入新的缓冲区
- 第三个参数为 true ,函数会为我们申请新缓冲区
使用完后需要用 RtlFreeUnicodeString 来释放内存
UNICODE_STRING sourecstring = RTL_CONSTANT_STRING(L"hello driver \r\n");UNICODE_STRING deststring = { 0 };RtlUpcaseUnicodeString(&deststring, &sourecstring, TRUE);DbgPrint("%wZ",&deststring);RtlFreeUnicodeString(&deststring);

将unicode 转为ansi 字符
与上一个函数一样,不过要注意
- 函数会返回一个NT_STATUS 的状态码,返回false 代表转换是失败
- 使用后需要释放内存
- DbgPrint 需要改为 “ Z” ```c UNICODE_STRING sourecstring = RTL_CONSTANT_STRING(L”hello driver \r\n”); ANSI_STRING deststring = { 0 }; NTSTATUS status;
status =RtlUnicodeStringToAnsiString(&deststring, &sourecstring, TRUE);if (NT_SUCCESS(status)){DbgPrint("%Z",&deststring);RtlFreeAnsiString(&deststring);}
<a name="U01LC"></a>## 比较字符串```cUNICODE_STRING sourecstring = RTL_CONSTANT_STRING(L"hello driver");UNICODE_STRING deststring = RTL_CONSTANT_STRING(L"hrllo");if (RtlEqualUnicodeString(&sourecstring,&deststring,TRUE)){DbgPrint("two string are equals\r\n");}else{DbgPrint("two string are not equal\r\n");}
RtlEqualUnicodeString 最后一个参数是 是否大小写敏感
相等返回true,不相等返回false
字符串copy
UNICODE_STRING sourecstring = RTL_CONSTANT_STRING(L"hello driver");WCHAR string[50] = { 0 };wcsncpy(string, sourecstring.Buffer, sourecstring.Length);DbgPrint("string is :%ws",string);
因为unicode_string 不是以null 结尾的,所以直接使用wcscpy 会有风险,我们要使用wscncpy 指定copy大小
字符串长度
PWCHAR string = L"hello driver";ULONG length;length = wcsnlen(string, 50);if (length==50){//do something}DbgPrint("length is :%d",length);
wcslen 是以null 为结尾统计的,如果字符串后门没有null ,会一直统计下去
我们需要wcsnlen 来代替wcslen,函数指定最大统计数
