1)驱动中的字符串使用如下结构:

  1. typedef struct _UNICODE_STRING{
  2. USHORT Length; //字符串的长度(字节数)
  3. USHORT MaximumLength; //字符串缓冲区长度(字节数)
  4. PWSTR Buffer; //字符串缓冲区
  5. }UNICODE_STRING,*PUNICODE_STRING;

这个为UNICODE字符串,一个字符占用双字节。对应的有Ansi字符串:

  1. typedef struct _STRING{
  2. USHORT Length; //字符串的长度(字节数)
  3. USHORT MaximumLength; //字符串缓冲区长度(字节数)
  4. PSTR Buffer; //字符串缓冲区
  5. }ANSI_STRING,*PANSI_STRING;

  内核程序是Unicode编码的,窄字符只在十分罕见的特殊场合使用。UNICODE_STRING不是空结束的,初始化和拷贝要使用指定的函数处理。

2)字符串初始化:UNICODE_STRING中的Buffer只是一个未分配内存的指针。定义常量字符串使用ntdef.h中的一个宏如下:

  1. #include <ntdef.h>
  2. UNICODE_STRING str;
  3. RtlInitUnicodeString(&str,Lmy first string!”);

  这种方式初始化字符串并不分配内存,不用当心内存释放问题。

3)拷贝字符串:使用RtlCopyUnicodeString进行拷贝,但是目的串必须有足够的缓冲区,否则只能部分拷贝。如:

  1. UNICODE_STRING dst; //目的字符串
  2. WCHAR dst_buf[]; //定义缓冲区
  3. UNICODE_STRING src = RTL_CONSTANT_STRING(LMy source string!”);
  4.  
  5. //把目的字符串初始化为拥有缓冲区长度为256的UNICODE_STRING空串,如果不这样初始化,则dst默认长度为0
  6. RtlInitEmptyString(dst,dst_buf,*sizeof(WCHAR));
  7. RtlCopyUnicodeString(&dst,&src); //字符串拷贝

4)链接字符串:RtlAppendUnicodeToString(UNICODE_STRING连接宽字节字符串)或者RtlAppendUnicodeStringToString(UNICODE_STRING连接UNICODE_STRING),如:

  1. NTSTATUS status = RtlAppendUnicodeToString(&dst,Lmy second string!”);
  2. if ( status != STATUS_SUCCESS )
  3. {
  4. //链接失败
  5. }

  NTSTATUS是常见返回值,STATUS_SUCCESS代表返回成功;否则返回一个错误码,这个连接函数在目标字符串空间不足时依然可以连接,只不过返回一个警告STATUS_BUFFER_TOO_SMALL。

5)字符串格式化输出:使用RtlStringCbPrintfW(头文件ntstrsafe.h,库文件ntstrsafe.lib)

  1. #include <ntstrsafe.h>
  2. WCHAR dst_buf[] = {};//任何时候都动态分配内存,这里还没讲,暂时用固定长度的缓冲区
  3. UNICODE_STRING des;
  4. RtlInitEmptyString(dst,dst_buf,*sizeof(WCHAR));
  5. NTSTATUS status = RtlStringCbPrintfW(
  6. dst->Buffer,*sizeof(WCHAR),Lfile path = %wZ file size = %d\r\n”,&file_path,file_size);
  7. dst->Length = wcslen(dst->Buffer)*sizeof(WCHAR);// RtlStringCbPrintfW以空结束,所以这里可以使用wcslen

  这个函数在目标缓冲区不足时返回STATUS_BUFFER_OVERFLOW,不能确定缓冲区要多长时,一般采用倍增尝试,直到返回STATUS_SUCCESS为止。

  UNICODE_STRING类型的指针,用%wZ可以格式化,但是在不能保证字符串以空结束时,必须避免使用%ws或者%s。其它的格式化形式同C语言。

  使用KdPrint输出格式化的调试信息时要使用下面的形式:

  1. KdPrint((Lfile path = %wZ file size = %d\r\n”,&file_path,file_size));

6)内存分配和释放:ExAllocatePoolWithTag(分配)、ExFreePool(释放)

  1. //定义一个内存分配标记
  2. #define MEM_TAG MyTt
  3. //目标字符串
  4. UNICODE_STRING dst = {};
  5. //根据源字符串长度分配空间给目标字符串
  6. dst.Buffer = (PWCHAR)ExAllocatePoolWithTag(NopagedPool,src->Length,MEM_TAG);
  7. if(dst.Buffer == NULL)
  8. {
  9. //分配失败
  10. status = STATUS_INSUFFICIENT_RESOURCE;
  11. ……
  12. }
  13. dst.Length = dst.MaximumLength = src->Length;
  14. status = RtlCopyUnicodeString(&dst,&src);
  15. ASSERT(status == STATUS_SUCCESS)

  ExAllocatePoolWithTag的第一个参数NopagedPool,src指定锁定在物理内存(不被置换到硬盘),第二个参数是要分配的内存的长度,第三个参数是所谓的“内存分配标记”---用于检测内存泄漏。分配的内存如果不释放,则永远泄漏,除非重启(卸载驱动也没用),释放内存如下:

  1. ExFreePool(dst.Buffer);
  2. dst.Buffer = NULL;
  3. dst.Length = dst.MaximumLength = ;

  ExFreePool不能释放栈空间(临时变量),否则系统立即崩溃,导致蓝屏

7)LIST_ENTRY:一个双向循环链表结构,由内核开发者们开发,内核开发经常使用这个结果。比如说要使用到这样一个结构:

  1. typedef struct{
  2. PFILE_OBJECT file_object;
  3. UNICODE_STRING file_name;
  4. LARGE_INTEGER file_length;
  5. }MY_FILE_INFO,*PMY_FILE_INFO;

  使用LIST_ENTRY可以如下构造该结构:

  1. typedef struct{
  2. LIST_ENTRY list_entry;
  3. PFILE_OBJECT file_object;
  4. UNICODE_STRING file_name;
  5. LARGE_INTEGER file_length;
  6. }MY_FILE_INFO,*PMY_FILE_INFO;

即使用LIST_ENTRY作为链表头,使用流程:

  1. LIST_ENTRY my_list_head;//我们的链表头
  2. InitializeListHead(&my_list_head);//链表头初始化
  3. //以下为插入操作
  4. PMY_FILE_INFO my_file_info = (PMY_FILE_INFO)ExAllocatePoolWithTag(PagedPool,sizeof(MY_FILE_INFO),MEM_TAG);//新建一个节点
  5. if (my_file_info == NULL) return STATUS_INSUFFICIENT_RESOURES;//内存分配失败
  6. my_file_info->file_object = file_object;
  7. my_file_info->file_name = file_name;
  8. my_file_info->file_length = file_length;//赋值,填写数据成员
  9. InsertHeadList(&my_list_head,(PLIST_ENTRY)&my_file_info);//执行插入
  10. //以下为遍历操作
  11. for ( p = my_list_info.Flink ; p != &my_list_head.Flink ; p = p->Flink)
  12. {
  13. PMY_FILE_INFO elem = CONTAINING_RECORD(p , MY_FILE_INFO, list_entry)
  14. }
  15. /*其中,CONTAINING_RECORD宏是通过LIST_ENTRY结构的指针,找个指定节点目标结构的指针,该宏被定义为:
  16. #define CONTAINING_RECORD(address,type,field) (\
  17. (type*)((PCHAR)(adress)-(ULONG_PTR)(&((type*)0)->field)))
  18. */

8)LARGE_INTEGER:长长整型数据,这是一个共用体:

  1. typedef union _LARGE_INTEGER{
  2. struct {
  3. ULONG LowPart;
  4. LONG HighPart;
  5. };
  6. struct {
  7. ULONG LowPart;
  8. LONG HighPart;
  9. }u;
  10. LONGLONG QuadPart;
  11. }

  它可以方便的得到高32位和低32位,一把用QuadPart即可。实际使用时大部分是PLARGE_INTEGER类型。

9)自旋锁:目的是为了实现多线程同步。使用自旋锁如下:

  1. KSPIN_LOCK my_spin_lock;
  2. KeInitializeSpinLock(&my_spin_lock);//初始化
  3.  
  4. KIRQL irql;//中断级
  5. KeAcquireSpinLock(&my_spin_lock,&irql);//进入区
  6. //to do something…//临界区
  7. KeReleaseSplinLock(&my_spin_lock,irql);//退出区

  锁要被所有进程共享,所以必须定义为全局变量、静态变量、或者分配在堆中。LIST_ENTRY提供一系列对加锁时的操作,只需要为每个链表定义并且初始化一个锁,然后使用对应提供的函数即可:

  1. LIST_ENTER my_list_head;//链表头
  2. KSPIN_LOCK my_list_lock;//链表锁
  3. InitializeListHead(&my_list_head);//链表头初始化
  4. KeInitializeSpinLock(&my_list_head);//链表锁初始化
  5. //加锁时的插入操作
  6. ExInsertlockedInsertHeadList(&my_list_head,(PLIST_ENTRY)&my_file_info,&my_list_lock);
  7. //加锁时的移除操作
  8. my_file_info = ExInsertlockedRemoveHeadList(&my_list_head,&my_list_lock);

《天书夜读:从汇编语言到windows内核编程》七 内核字符串与内存的更多相关文章

  1. 《天书夜读:从汇编语言到windows内核编程》五 WDM驱动开发环境搭建

    (原书)所有内核空间共享,DriverEntery是内核程序入口,在内核程序被加载时,这个函数被调用,加载入的进程为system进程,xp下它的pid是4.内核程序的编写有一定的规则: 不能调用win ...

  2. 《天书夜读:从汇编语言到windows内核编程》八 文件操作与注册表操作

    1)Windows运用程序的文件与注册表操作进入R0层之后,都有对应的内核函数实现.在windows内核中,无论打开的是文件.注册表或者设备,都需要使用InitializeObjectAttribut ...

  3. 《天书夜读:从汇编语言到windows内核编程》十一 用C++编写内核程序

    ---恢复内容开始--- 1) C++的"高级"特性,是它的优点也是它的缺点,微软对于使用C++写内核程序即不推崇也不排斥,使用C++写驱动需注意: a)New等操作符不能直接使用 ...

  4. 《天书夜读:从汇编语言到windows内核编程》六 驱动、设备、与请求

    1)跳入到基础篇的内核编程第7章,驱动入口函数DriverEnter的返回值决定驱动程序是否加载成功,当打算反汇编阅读驱动内核程序时,可寻找该位置. 2)DRIVER_OBJECT下的派遣函数(分发函 ...

  5. Win64 驱动内核编程-7.内核里操作进程

    在内核里操作进程 在内核里操作进程,相信是很多对 WINDOWS 内核编程感兴趣的朋友第一个学习的知识点.但在这里,我要让大家失望了,在内核里操作进程没什么特别的,就标准方法而言,还是调用那几个和进程 ...

  6. Win64 驱动内核编程-3.内核里使用内存

    内核里使用内存 内存使用,无非就是申请.复制.设置.释放.在 C 语言里,它们对应的函数是:malloc.memcpy.memset.free:在内核编程里,他们分别对应 ExAllocatePool ...

  7. Windows核心编程第二章,字符串的表示以及宽窄字符的转换

    目录 Windows核心编程,字符串的表示以及宽窄字符的转换 1.字符集 1.1.双字节字符集DBCS 1.2 Unicode字符集 1.3 UTF-8编码 1.4 UTF - 32编码. 1.5 U ...

  8. Win64 驱动内核编程-8.内核里的其他常用

    内核里的其他常用 1.遍历链表.内核里有很多数据结构,但它们并不是孤立的,内核使用双向链表把它们像糖 葫芦一样给串了起来.所以遍历双向链表能获得很多重要的内核数据.举个简单的例子,驱 动对象 Driv ...

  9. 《天书夜读:从汇编语言到windows内核编程》四 windows内核调试环境搭建

    1) 基础篇是讲理论的,先跳过去,看不到代码运行的效果要去记代码是一个痛苦的事情.这里先跳入探索篇.其实今天的确也很痛苦,这作者对驱动开发的编译与调试环境介绍得太模糊了,我是各种尝试,对这个环境的搭建 ...

  10. 《天书夜读:从汇编语言到windows内核编程》十 线程与事件

    1)驱动中使用到的线程是系统线程,在system进程中.创建线程API函数:PsCreateSystemThread:结束线程(线程内自行调用)API函数:PsTerminateSystemThrea ...

随机推荐

  1. CentOS7 Redis安装

    Redis介绍 1.安装Redis 官方下载地址:http://download.redis.io 使用Linux下载:wget http://download.redis.io/redis-stab ...

  2. mysql explain 分析sql语句

    鉴于最近做的事情,需要解决慢sql的问题,现补充一点sql语句性能分析之explain的使用方式! 综合返回数据情况,分析各个参数,可以了解sql 使用方法:explain  + sql语句 如 :e ...

  3. angular实现输入框输入添加 搜索框查询

    !DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"&g ...

  4. Scala基础之注解(annotation

    在学习Scala的过程中,总会碰到一些注解: // Predef.scala @inline def implicitly[T](implicit e: T) = e @deprecated(&quo ...

  5. 在 JPA、Hibernate 和 Spring 中配置 Ehcache 缓存

    jpa, hibernate 和 spring 时配置 ehcache 二级缓存的步骤. 缓存配置 首先在 persistence.xml 配置文件中添加下面内容: <property name ...

  6. 【NOIP2015提高组】Day2 T1 跳石头

    题目描述 这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石.组委会已经选择好了两块岩石作为比赛起点和终点.在起点和终点之间,有 N 块岩石(不含起点和终 点的岩石).在比赛过程中,选手们将从 ...

  7. [CF] Final Exam Arrangement

    问题链接:http://www.bnuoj.com/v3/contest_show.php?cid=4329#problem/F   问题大意:         就是有1--N们课程,每一个课程都有一 ...

  8. PhiloGL学习(2)——骚年,让我们荡起双桨

     前言 上一篇文章中简单介绍了PhiloGL框架如何上手.GLSL语言以及简单的绘制一个方块(见PhiloGL学习(1)--场景创建及二维方块加载).本文很简单,我们一起来让这个方块动起来.  一.  ...

  9. SpringMVC 集成redis

    一.下载导入jar 二.配置redis 1.创建redis.properties # Redis settings #redis.host=192.168.20.101 #redis.port= #r ...

  10. Dagger2 入门解析

    前言 在为dropwizard选择DI框架的时候考虑了很久.Guice比较成熟,Dagger2主要用于Android.虽然都是google维护的,但Dagger2远比guice更新的频率高.再一个是, ...