• 64位内核第七讲.内核中字符串编程注意事项


    内存中字符串编程

    一丶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字符.

  • 相关阅读:
    mysql保存中文乱码的原因和解决办法
    NetSetMan IP地址切换工具
    使用批处理文件,自动设置计算机IP地址
    神逸之作:国产快速启动软件神品ALTRun
    Apache详细介绍
    利用sqoop对mysql执行DML操作
    Mysql定时清空表
    azkaban group分组,权限
    azkaban使用
    sqoop无法导出parquet文件到mysql
  • 原文地址:https://www.cnblogs.com/iBinary/p/10990678.html
Copyright © 2020-2023  润新知