类型

windows 内核有四种字符串类型

  • CHAR
  • WCHAR
  • UNICODE_STRING
  • ANSI_STRING

CHAR 和WCHAR是以NULL结尾的字符串类型,UNICODE_STRING和ANSI_STRING是字符串结构体
除此之外,还可以使用一些C语言的字符串函数 ,如: strstr(),strlen(),strcpy(),

UNICODE_STRING

  1. typedef struct _UNICODE_STRING {
  2. USHORT Length;
  3. USHORT MaximumLength;
  4. #ifdef MIDL_PASS
  5. [size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer;
  6. #else // MIDL_PASS
  7. _Field_size_bytes_part_opt_(MaximumLength, Length) PWCH Buffer;
  8. #endif // MIDL_PASS
  9. } UNICODE_STRING;

UNICODE_STRING 包含3个成员

  • Length:Buffer成员指向的buffer 占用了多少给字节,
    • 等于字符长度乘以字符数,单位为字节
    • Length 不代表字符串有多少个字符
  • MaximumLength:字符缓冲区分配的内存字节数。
  • Buffer:指向WCHAR(宽字符)的指针

    初始化UNICODE_STRING

    使用 RTL_CONSANT_STRING 的宏初始化

    1. UNICODE_STRING string = RTL_CONSTANT_STRING(L"hello driver\r\n");
    2. DbgPrint("%wZ",&string);

DbgPrint() 函数 使用 “%wZ” 来指明打印内容为UNICODE_SSTRING,函数需要传递一个UNICODE_STRING类型的指针

RtlInitUnicodeString

  1. UNICODE_STRING string2 = { 0 };
  2. RtlInitUnicodeString(&string2,L"hello driver2\r\n");
  3. DbgPrint("%wZ",&string2);

需要先将 UNICODE_STRING结构体赋值为0,再用函数初始化

手动初始化

  1. UNICODE_STRING string3;
  2. WCHAR strbuffer[120] = {0};
  3. string3.Buffer = strbuffer;
  4. string3.MaximumLength = sizeof(WCHAR) * 120;
  5. string3.Length = wcslen(L"hello driver3\r\n") * sizeof(WCHAR);
  6. wcscpy(string3.Buffer,L"hello driver3\r\n");
  7. DbgPrint("%wZ",&string3);

不过要注意的是strcpy 只能操作以null结尾的字符串
image.png
image.png

完整代码

  1. #include <ntddk.h>
  2. VOID Unload(IN PDRIVER_OBJECT DriverObject) {
  3. DbgPrint("driver unload\r\n");
  4. }
  5. NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegisteryPath) {
  6. DriverObject->DriverUnload = Unload;
  7. UNICODE_STRING string1 = RTL_CONSTANT_STRING(L"hello driver1\r\n");
  8. DbgPrint("%wZ",&string1);
  9. UNICODE_STRING string2 = { 0 };
  10. RtlInitUnicodeString(&string2,L"hello driver2\r\n");
  11. DbgPrint("%wZ",&string2);
  12. UNICODE_STRING string3;
  13. WCHAR strbuffer[120] = {0};
  14. string3.Buffer = strbuffer;
  15. string3.MaximumLength = sizeof(WCHAR) * 120;
  16. string3.Length = wcslen(L"hello driver3\r\n") * sizeof(WCHAR);
  17. wcscpy(string3.Buffer,L"hello driver3\r\n");
  18. DbgPrint("%wZ",&string3);
  19. return STATUS_SUCCESS;
  20. }

ANSI_STRING

  1. typedef struct _STRING {
  2. USHORT Length;
  3. USHORT MaximumLength;
  4. #ifdef MIDL_PASS
  5. [size_is(MaximumLength), length_is(Length) ]
  6. #endif // MIDL_PASS
  7. _Field_size_bytes_part_opt_(MaximumLength, Length) PCHAR Buffer;
  8. } STRING;
  9. typedef STRING *PSTRING;
  10. typedef STRING ANSI_STRING;

和 UNICODE_STRING有相同的结构体,但是buffer指向的是一个asiic 字符串,一个字节一个字符。
这意味着 Length 表示这个字符串有多少个字符

字符串操作

复制

  1. WCHAR strbuf[60] = { 0 };
  2. UNICODE_STRING sourecstring = RTL_CONSTANT_STRING(L"hello driver \r\n");
  3. UNICODE_STRING deststring = { 0 };
  4. deststring.Buffer = strbuf;
  5. deststring.Length = deststring.MaximumLength = 60;
  6. RtlCopyUnicodeString(&deststring,&sourecstring);
  7. DbgPrint("%wZ",&deststring);

字符串转为大写

  • 使用RtlUpcaseUnicodeString 函数来转换
  • 此函数不会改变原来的字符,而会将转换后的字符串写入新的缓冲区
  • 第三个参数为 true ,函数会为我们申请新缓冲区
  • 使用完后需要用 RtlFreeUnicodeString 来释放内存

    1. UNICODE_STRING sourecstring = RTL_CONSTANT_STRING(L"hello driver \r\n");
    2. UNICODE_STRING deststring = { 0 };
    3. RtlUpcaseUnicodeString(&deststring, &sourecstring, TRUE);
    4. DbgPrint("%wZ",&deststring);
    5. RtlFreeUnicodeString(&deststring);

    image.png

将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;
  1. status =RtlUnicodeStringToAnsiString(&deststring, &sourecstring, TRUE);
  2. if (NT_SUCCESS(status))
  3. {
  4. DbgPrint("%Z",&deststring);
  5. RtlFreeAnsiString(&deststring);
  6. }
  1. <a name="U01LC"></a>
  2. ## 比较字符串
  3. ```c
  4. UNICODE_STRING sourecstring = RTL_CONSTANT_STRING(L"hello driver");
  5. UNICODE_STRING deststring = RTL_CONSTANT_STRING(L"hrllo");
  6. if (RtlEqualUnicodeString(&sourecstring,&deststring,TRUE))
  7. {
  8. DbgPrint("two string are equals\r\n");
  9. }
  10. else
  11. {
  12. DbgPrint("two string are not equal\r\n");
  13. }

RtlEqualUnicodeString 最后一个参数是 是否大小写敏感
相等返回true,不相等返回false

字符串copy

  1. UNICODE_STRING sourecstring = RTL_CONSTANT_STRING(L"hello driver");
  2. WCHAR string[50] = { 0 };
  3. wcsncpy(string, sourecstring.Buffer, sourecstring.Length);
  4. DbgPrint("string is :%ws",string);

因为unicode_string 不是以null 结尾的,所以直接使用wcscpy 会有风险,我们要使用wscncpy 指定copy大小

字符串长度

  1. PWCHAR string = L"hello driver";
  2. ULONG length;
  3. length = wcsnlen(string, 50);
  4. if (length==50)
  5. {
  6. //do something
  7. }
  8. DbgPrint("length is :%d",length);

wcslen 是以null 为结尾统计的,如果字符串后门没有null ,会一直统计下去
我们需要wcsnlen 来代替wcslen,函数指定最大统计数
image.png