Windows内核中的字符串操作

概述

  在内核编程的过程中,有大量的代码涉及到对字符串的操作,比如需要暂存一部分的字符串、需要拷贝一个字符串到另一个字符串中,需要字符串追加,比较,判断是否含有子字符串等操作,在这些操作的过程中,需要时刻注意操作的字符串指针,避免溢出、为空等造成系统运行异常的错误。实际开发过程中微软提供了一套完整的字符串操作API,我们只需要借助这些API函数就可以满足大部分的字符串操作需求了。
  由于在实际编程的过程中主要使用的是宽字符串,所以本文主要介绍的API是操作宽字符集的,窄字符集也有对应的操作。

定义和初始化

头文件

#include <ntifs.h>
#include <windef.h>
#include <ntstrsafe.h>

格式化输出表

符号说明类型
%c, %lcANSI字符char
%C, %wc宽字符wchar_t
%d, %i十进制有符号整数int
%D十进制_int64_int64
%L十六进制的LARGE_INTEGERLARGE_INTEGER
%s, %lsNULL终止的ANSI字符串char*
%S, %wsNULL终止的宽字符串wchar_t*
%ZANSI_STRING字符串STRING*
%wZUNICODE_STRING字符串UNICODE_STRING*
%u十进制的ULONGULONG
%x小写字符十六进制的ULONGULONG
%X大写字符十六进制的ULONGULONG
%p指针Pointer 32/64位PVOID *

基本数据结构

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 结构体包含了三个成员变量,
Length 表示字符串所用的字节长度(不包含结束符),表示的是实际使用的字节数
MaximumLength 表示buffer的长度,表示申请的存放字符串的空间的大小(包含结束符)
Buffer 指向分配的字符串的指针,如果用常量字符串来初始化的话,这个指针指向常量字符串所在的内存,是不能修改的

初始化一个unicode string变量

	UNICODE_STRING contemp = {0};
	RtlInitUnicodeString(&contemp, L"HELLO WORLD!"); 

以上使用了一个常量字符串初始化了一个Unicode string类型的变量contemp,注意这个时候contemp指向的内容是常量字符串“HELLO WORLD!”的地址,是不能修改的,如果强行修改会导致宕机,并有如下提示:
在这里插入图片描述

将ansi转化为unicode

	STRING strtemp = { 0 };
	UNICODE_STRING temp = { 0 }; // 默认定义的变量是没有分配字符串内存的,buffer指针为空
	RtlInitString(&strtemp, "strtemp hello world !");
	// 用ansi字符串初始化unicodestring 这里的TRUE表示对temp自动创建缓存,使用完成后需要释放一下temp
	RtlAnsiStringToUnicodeString(&temp, &strtemp, TRUE);

以上使用一个ansi的字符串结构变量,初始化一个unicode string的变量,初始化的过程也完成了buffer的内存申请,所以最终是需要释放的,由于是自己申请的内存,所以是可以对其内部数据进行修改的,下面的操作多基于temp

将字符串转化为大写

	RtlUpcaseUnicodeString(&temp, &temp, FALSE);  // 这里的temp是可以改变的
	DbgPrint("MYTEST: ----%wZ-----", &temp);

从一个字符串复制到另一个字符串中

	UNICODE_STRING copytest = { 0 };
	copytest.Buffer = ExAllocatePool(NonPagedPool, 0X100);
	copytest.MaximumLength = 0X100; // 不指定大小会导致下面的步骤无法填充内容
	
	RtlCopyUnicodeString(&copytest, &temp);
	DbgPrint("MYTEST: ----%wZ-----", &copytest);
	ExFreePool(copytest.Buffer);

或者

	PWCHAR copytest2 = ExAllocatePool(NonPagedPool, 0x100);
	RtlZeroMemory(copytest2, 0x100);
	RtlStringCbCopyW(copytest2, 0x100, L"mytest copystr2 hello world");
	DbgPrint("MYTEST: ----%ws-----", copytest2);
	ExFreePool(copytest2);

注意: 格式化字符串输出的区别

比较字符串的大小

	UNICODE_STRING comtemp1 = { 0 };
	UNICODE_STRING comtemp2 = { 0 };
	RtlInitUnicodeString(&comtemp1, L"hello world");
	RtlInitUnicodeString(&comtemp2, L"HEllo world");
	LONG ret = RtlCompareUnicodeString(&comtemp1, &comtemp2, FALSE); // 第三个参数表明是否忽略大小写差异,true 表明忽略大小写差异
	DbgPrint("MYTEST: ret = %d", ret);
	if (ret == 0)
	{
		DbgPrint("MYTEST: %wZ = %wZ", &comtemp1, &comtemp2);
	}
	else if (ret > 0)
	{
		DbgPrint("MYTEST: %wZ > %wZ", &comtemp1, &comtemp2);
	}
	else
	{
		DbgPrint("MYTEST: %wZ < %wZ", &comtemp1, &comtemp2);
	}

追加字符串

  1. 在unicode string类型基础上直接进行追加
    UNICODE_STRING appendtest = { 0 };
    appendtest.Buffer = ExAllocatePool(NonPagedPool, 0X100);
    RtlZeroMemory(appendtest.Buffer, 0x100);
    RtlStringCbCopyW(appendtest.Buffer, 0x100, L"APPENDTEST---");
    appendtest.MaximumLength = 0X100;
    appendtest.Length = sizeof(L"APPENDTEST---")-sizeof(WCHAR);
    DbgPrint("MYTEST: ----%wZ-----", &appendtest);
    // 开始在appendtest后追加
    UNICODE_STRING appendtest2 = { 0 };
    RtlInitUnicodeString(&appendtest2, L"hello world");
    RtlAppendUnicodeStringToString(&appendtest, &appendtest2);
    DbgPrint("MYTEST: ----%wZ-----", &appendtest);
    ExFreePool(appendtest.Buffer);
    
  2. 在wchar *基础上进行追加
    PWCHAR wcharappend = ExAllocatePool(NonPagedPool, 0X100);
    RtlZeroMemory(wcharappend, 0x100);
    RtlStringCbCopyW(wcharappend, 0x100, L"APPENDTEST2---");
    DbgPrint("MYTEST: ----%ws-----", wcharappend);
    RtlStringCbCatW(wcharappend, 0x100, L"hello world!");
    DbgPrint("MYTEST: ----%ws-----", wcharappend);
    ExFreePool(wcharappend);
    

判断是否含有子字符串

	UNICODE_STRING srcstr = { 0 };
	RtlInitUnicodeString(&srcstr, L"HELLO world ");
	UNICODE_STRING tarstr = { 0 };
	RtlInitUnicodeString(&tarstr, L"*WORLD*");// 注意1
	if (FsRtlIsNameInExpression(&tarstr, &srcstr, TRUE, NULL)) // 注意2
	{
		DbgPrint("MYTEST: %wZ has substr %wZ---", &srcstr, &tarstr);
	}

注意1: 需要查找的目标字符串,如果是断定位于开始或者结束的位置可以不需要前面或者后面的 * 号,比如只需要查找位于字符串开始位置的字符串的时候前面的 * 号就可以不要。
注意2: 这里在查找目标字符串的时候选定了不区分大小写,且最后一个参数设置为NULL,查看函数的定义可以知道:第四个参数用于不区分大小写匹配的大写字符表的可选指针。如果未提供此值,则使用默认的系统大写字符表进行匹配。所以当这个值设置为空的时候,目标字符串就需要自行转化为大写,系统会将源字符串进行大写转换进行查找。

如有错误,还请指正,谢谢

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风里雨里守护着你

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值