来简单实战几个字符串API
初始化
ASCII 字符和宽字符的版本分别是
1 2
| RtlInitAnsiString RtlInitUnicodeString
|
第一个参数都是对应的字符串结构体的指针,也就是说,在使用的时候需要先定义一个结构体变量再去使用这个 API 去初始化字符串变量。
1 2 3 4 5 6 7
| LPSTR str2 = "123456789 hello"; ANSI_STRING astr; RtlInitAnsiString(&astr, str2);
LPWSTR str = L"123456789 hello"; UNICODE_STRING ustr; RtlInitUnicodeString(&ustr, str);
|
使用格式化字符串输出的 %Z
和 %wZ
可以直接输出该字符串。
1 2 3 4
| #define kprintf(format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, format, ##__VA_ARGS__)
kprintf(("%Z\n"), astr); kprintf(("%wZ\n"), ustr);
|
运行结果:
相互转换
宽字符可以变 ASCII 字符,ASCII 字符也可以变宽字符。前提是它们都是 ASCII 范围内可以在不改变内容的情况下转换,如果使用 Unicode 字符将 Unicode 字符串转为 ASCII 字符串则可能会出现乱码。
事实证明想多了,即使是 ASCII 字符串,也能存放 Unicode 字符串。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include<fltKernel.h> #define kprintf(format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, format, ##__VA_ARGS__) VOID UnloadDriver(PDRIVER_OBJECT DriverObject) { UNREFERENCED_PARAMETER(DriverObject); kprintf(("Bye!\n")); } extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path) { reg_path; NTSTATUS status = STATUS_SUCCESS; kprintf(("Hello!\n")); driver->DriverUnload = UnloadDriver; ANSI_STRING astr; UNICODE_STRING ustr; LPWSTR str = L"123456789 hello哈哈"; RtlInitUnicodeString(&ustr, str); RtlUnicodeStringToAnsiString(&astr, &ustr, TRUE); kprintf(("%p,%p\n"), ustr.Buffer, astr.Buffer); kprintf(("%Z\n"), astr); return status; }
|
运行结果:
这里的 API RtlUnicodeStringToAnsiString(&astr, &ustr, TRUE);
,前两个参数,一个是目的字符串指针,一个是源字符串指针。第三个参数 TRUE 表示为目的字符串新分配内存,FALSE 表示不分配内存。
如果第三个参数设置为 FALSE 一定要注意,它会在 astr.buffer 中写入,因此一定要确保指向了一个正确的可写的内存,否则就会面临蓝屏。
RtlAnsiStringToUnicodeString
这个 API 同理可得。
注意,使用RtlUnicodeStringToAnsiString/RtlAnsiStringToUnicodeString
函数时,需要在使用完后调用RtlFreeAnsiString/RtlFreeUnicodeString
函数来释放所分配的缓冲区,否则会产生内存泄露。
数字与字符串之间的转换
在传统的 CHAR */WCHAR *
字符串中,我们只有 atoi,sprintf 等传统函数做数字与字符串之间的转换。 而内核就有很方便的 api 做转换,下面来看一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include<fltKernel.h> #define kprintf(format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, format, ##__VA_ARGS__) VOID UnloadDriver(PDRIVER_OBJECT DriverObject) { UNREFERENCED_PARAMETER(DriverObject); kprintf(("Bye!\n")); } extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path) { reg_path; NTSTATUS status = STATUS_SUCCESS; kprintf(("Hello!\n")); driver->DriverUnload = UnloadDriver; UNICODE_STRING ustr,ustr2; ULONG val,val2=0x12345678; LPWSTR str = L"123456789"; RtlInitUnicodeString(&ustr, str); RtlUnicodeStringToInteger(&ustr, 10, &val); kprintf(("val=%d\n"), val);
WCHAR *S=(WCHAR*)ExAllocatePoolWithTag(PagedPool, 0x1000, 'str'); ustr2.Buffer=S; ustr2.Length=0; ustr2.MaximumLength=0x1000; RtlIntegerToUnicodeString(val2, 16, &ustr2); kprintf(("val=%wZ\n"), ustr2); return status; }
|
其中第二个参数 base 指示了字符串是什么进制。
可以发现,从数字转字符串还是需要废一点功夫的,需要手动给 UNICODE_STRING 结构体初始化分配内存。
但是也是显然的,该例程会存在内存泄漏,当驱动被释放的时候,分配的内存不会释放,因此需要养成良好的习惯,当不使用这个字符串的时候,及时释放分配的内存。
字符串拷贝与比较
例程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include<fltKernel.h> #define kprintf(format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, format, ##__VA_ARGS__) VOID UnloadDriver(PDRIVER_OBJECT DriverObject) { UNREFERENCED_PARAMETER(DriverObject); kprintf(("Bye!\n")); } extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path) { reg_path; NTSTATUS status = STATUS_SUCCESS; kprintf(("Hello!\n")); driver->DriverUnload = UnloadDriver; UNICODE_STRING s1; WCHAR* S = (WCHAR*)ExAllocatePoolWithTag(PagedPool, 0x1000, 'str'); s1.Buffer = S; s1.Length = 0; s1.MaximumLength = 0x1000; RtlCopyUnicodeString(&s1, &driver->DriverName); kprintf(("DriverName: %wZ\n"), s1); auto compare=RtlCompareUnicodeString(&s1, &driver->DriverName, TRUE); kprintf(("compare=%d\n"), compare); return status; }
|
RtlCopyUnicodeString 就是简单的字符串拷贝函数,不做过多解释。
RtlCompareUnicodeString 是字符串比较函数,与 strcmp 一样,字符串相等返回 0,不相等返回非 0,第三个参数指示英文单词是否大小写敏感,TRUE 则敏感,S 与 s 视为不同的字符。
其它
还有一系列的字符串操作函数也不一一展示了,它们都是 Rtl 为前缀命名的 API。
参考文献