字符串处理

在驱动中一般使用的是ANSI字符串和宽字节字符串,在驱动中我们仍然可以使用C中提供的字符串操作函数,但是在DDK中不提倡这样做,由于C函数容易导致缓冲区溢出漏洞,针对字符串的操作它提供了一组函数分别用来处理ANSI字符串和UNICODE字符串。

针对两种字符串,首先定义了它们的结构体

typedef struct _STRING {
USHORT Length;//字符串的长度
USHORT MaximumLength;//字符缓冲的长度
PCHAR Buffer;//字符缓冲的地址
} ANSI_STRING, *PANSI_STRING; typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

对于这两个字符串的打印,可以使用%wZ打印UNICODE_STRING用%Z打印ANSI_STRING

字符串的初始化

VOID
RtlInitAnsiString(
IN OUT PANSI_STRING DestinationString,
IN PCSZ SourceString
); VOID
RtlInitUnicodeString(
IN OUT PUNICODE_STRING DestinationString,
IN PCWSTR SourceString
);

这两个函数只是简单的将SourceString 的首地址赋值给Buffer成员,并初始化相关的长度,所以在使用时需要考虑缓冲的生命周期,权限,同时如果我们改变SourceString 里面存储的字符串,那么对应的UNICODE_STRING 或者ANSI_STRING中的值也会改变,比如下面的代码

RtlInitUnicodeString(&uTest, L"Hello World");
RtlCopyMemory(uTest.Buffer, L"Test");

由于Buffer指向的是不可修改的常量内存部分,所以后面试图修改它的时候会造成程序崩溃。

void InitString(&pUnicodeString)
{
WCHAR szBuf[255] = L"Hello world";
RtlInitUnicodeString(pUnicodeString, szBuffer);
} void test()
{
UNICODE_STRING uTest;
InitString(&uTest);
//后面的操作
}

我们在另外一个函数中利用局部变量来初始化这个字符串的时候由于当函数调用完成,函数中局部变量被销毁,这个时候指向的那块内存可能已经被其他函数所占用,而我们后面通过操作UNICODE_STRING,又要操作这段内存,这个时候一定会出现问题,所以一般如果要在多个函数中使用这个UNICODE_STRING时一般申请一段堆内存,但是在使用完成后一定要记得自己回收这段内存,否则会造成内存泄露,对此DDK专门提供了一组函数来销毁字符串中的堆内存

VOID
RtlFreeAnsiString(
IN PANSI_STRING AnsiString
); VOID
RtlFreeUnicodeString(
IN PUNICODE_STRING UnicodeString
);

字符串拷贝:

VOID
RtlCopyString(
IN OUT PSTRING DestinationString,
IN PSTRING SourceString OPTIONAL
); VOID
RtlCopyUnicodeString(
IN OUT PUNICODE_STRING DestinationString,
IN PUNICODE_STRING SourceString
);

字符串比较

LONG
RtlCompareString(
IN PSTRING String1,
IN PSTRING String2,
BOOLEAN CaseInSensitive//是否忽略大小写
); LONG
RtlCompareUnicodeString(
IN PUNICODE_STRING String1,
IN PUNICODE_STRING String2,
IN BOOLEAN CaseInSensitive
);

字符串转化为大写

VOID
RtlUpperString(
IN OUT PSTRING DestinationString,
IN PSTRING SourceString
); NTSTATUS
RtlUpcaseUnicodeString(
IN OUT PUNICODE_STRING DestinationString,
IN PCUNICODE_STRING SourceString,
IN BOOLEAN AllocateDestinationString//是否要求该函数自行为输出参数分配内存
);

这两个函数在调用是目标字符串和源字符串可以是同一个字符串

字符串与整形数字之间的转化可以使用函数

NTSTATUS
RtlUnicodeStringToInteger(
IN PUNICODE_STRING String,
IN ULONG Base OPTIONAL,//需要的数的进制
OUT PULONG Value
); NTSTATUS
RtlIntegerToUnicodeString(
IN ULONG Value,
IN ULONG Base OPTIONAL,
IN OUT PUNICODE_STRING String
);

ANSI与UNICODE字符串的相互转化可以使用下面的函数

NTSTATUS
RtlUnicodeStringToAnsiString(
IN OUT PANSI_STRING DestinationString,
IN PUNICODE_STRING SourceString,
IN BOOLEAN AllocateDestinationString
); NTSTATUS
RtlAnsiStringToUnicodeString(
IN OUT PUNICODE_STRING DestinationString,
IN PANSI_STRING SourceString,
IN BOOLEAN AllocateDestinationString
);

文件操作

创建或者打开一个文件

文件的创建和打开都是使用函数ZwCreateFile

NTSTATUS
ZwCreateFile(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER AllocationSize OPTIONAL,
IN ULONG FileAttributes,
IN ULONG ShareAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN PVOID EaBuffer OPTIONAL,
IN ULONG EaLength
);
  1. FileHandle:这个函数通过这个参数返回文件句柄
  2. DesiredAccess:以何种权限打开或者创建这个文件,GENERIC_READ可读,GENERIC_WRITE可写,GENERIC_EXECUTE可执行,GENERIC_ALL所有权限
  3. ObjectAttributes:这是一个文件属性的结构体,里面包含有要打开的文件的名称
  4. IoStatusBlock:接受函数操作文件的结果状态
  5. AllocationSize:指定在创建爱女或者写文件时初始大小,如果给0,则文件大小会随着写入数据的增加而动态的增加
  6. FileAttributes:指定新创建文件的属性,一般给0或者FILE_ATTRIBUTE_NORMAL
  7. ShareAccess:文件的共享权限,其他线程或者进程通过这个句柄访问文件的权限,给0表示不允许其他进程通过这个句柄访问,FILE_SHARE_READ读, FILE_SHARE_WRITE写,FILE_SHARE_DELETE删除
  8. CreateDisposition:指定当文件存在或者不存在时这个函数的动作。它的取值可以有下面几个
取值 文件存在 文件不存在
FILE_SUPERSEDE 新建一个文件替代 新建文件
FILE_CREATE 返回一个错误 创建文件
FILE_OPEN 打开文件 返回一个错误
FILE_OPEN_IF 打开文件 创建文件
FILE_OVERWRITE 打开,并且将之前的内容覆盖 返回错误
FILE_OVERWRITE_IF 打开,并且将之前的内容覆盖 创建文件

9. CreateOptions打开或者创建文件时的附加操作,一般给FILE_SYNCHRONOUS_IO_NONALERT

10. EaBuffer指向扩展空间的指针

11. EaLength扩展空间的大小

这个函数与应用层的CreateFile不同的时,在指定打开或者创建文件名时是使用结构OBJECT_ATTRIBUTES来指定,针对这个结构,有一个函数能够初始化它

VOID
InitializeObjectAttributes(
OUT POBJECT_ATTRIBUTES InitializedAttributes,
IN PUNICODE_STRING ObjectName,//文件名
IN ULONG Attributes,
IN HANDLE RootDirectory,
IN PSECURITY_DESCRIPTOR SecurityDescriptor
);

Attributes:该对象的描述信息,一般给OBJ_CASE_INSENSITIVE 表示对大小写敏感

RootDirectory :该文件的根目录,一般给NULL

SecurityDescriptor :安全描述符,一般也是给NULL

另外这里的名称必须使用符号链接名或者设备名,而不是我们熟悉的“C:\”这种形式对于C盘可以使用名称“\??\C”或者“\Device\HarddiskVolum1”这种形式

当程序结束时需要调用ZwClose来清理文件句柄这个函数的参数比较简单,只是简单的传入文件句柄即可

获取和设置文件的相关信息

可以下面两个函数分别获取和设置文件的相关信息

NTSTATUS
ZwQueryInformationFile(
IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass
); NTSTATUS
ZwSetInformationFile(
IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass
);

其中FileInformationClass是一个枚举值,根据这个值得不同FileInformation可以被解析成不同的内容。

1. 当这个参数为FileStandardInformation时,使用结构体FILE_STANDARD_INFORMATION

typedef struct FILE_STANDARD_INFORMATION {
LARGE_INTEGER AllocationSize; //为文件分配簇所占空间的大小
LARGE_INTEGER EndOfFile;//距离文件结尾还有多少字节,当文件指针位于文件头时,这个值就是文件本身大小
ULONG NumberOfLinks;//有多少个链接文件
BOOLEAN DeletePending;//是否准备删除
BOOLEAN Directory;//是否为目录
} FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION;
  1. 当这个参数为FileBasicInformation使用结构体FILE_BASIC_INFORMATION
typedef struct FILE_BASIC_INFORMATION {
LARGE_INTEGER CreationTime; //创建时间
LARGE_INTEGER LastAccessTime;//上次访问时间
LARGE_INTEGER LastWriteTime;//上次写文件时间
LARGE_INTEGER ChangeTime;//上次修改时间
ULONG FileAttributes;//文件属性
} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;

其中时间参数是一个LARGE_INTEGER类型的整数,代表从1601年到现在经过多少个100ns。文件属性参数如果为FILE_ATTRIBUTE_DIRECTORY表示这是一个目录文件,FILE_ATTRIBUTE_NORMAL表示是一个普通文件,FILE_ATTRIBUTE_HIDDEN表示这是一个隐藏文件,FILE_ATTRIBUTE_SYSTEM表示这是一个系统文件,FILE_ATTRIBUTE_READONLY表示这是一个只读文件

3. 当这个参数为FileNameInformation时,使用结构体FILE_NAME_INFORMATION

typedef struct _FILE_NAME_INFORMATION {
ULONG FileNameLength;//文件名长度
WCHAR FileName[1];//文件名
} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION;
  1. 当这个参数是FilePositionInformation时,使用结构体FILE_POSITION_INFORMATION
typedef struct FILE_POSITION_INFORMATION {
LARGE_INTEGER CurrentByteOffset;//当前文件指针的位置
} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION;

读写文件

写文件调用函数ZwCreateFile

NTSTATUS
ZwWriteFile(
IN HANDLE FileHandle,//文件句柄
IN HANDLE Event OPTIONAL,//时间对象一般给NULL
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,//一般给NULL
IN PVOID ApcContext OPTIONAL,//一般给NULL
OUT PIO_STATUS_BLOCK IoStatusBlock,//记录写操作的状态用里面的Information成员记录实际写了多少字节
IN PVOID Buffer,//写入文件中缓冲区的指针
IN ULONG Length,//缓冲区中数据的长度
IN PLARGE_INTEGER ByteOffset OPTIONAL,//从文件的多少地址开始写
IN PULONG Key OPTIONAL//一般给NULL
);

读文件使用函数ZwReadFile

NTSTATUS
ZwReadFile(
IN HANDLE FileHandle,//文件句柄
IN HANDLE Event OPTIONAL,//一般给NULL
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,//一般给NULL
IN PVOID ApcContext OPTIONAL,//一般给NULL
OUT PIO_STATUS_BLOCK IoStatusBlock, //读取的字节数保存在结构的成员Information中
OUT PVOID Buffer,//缓冲区的指针
IN ULONG Length,//缓冲区的长度
IN PLARGE_INTEGER ByteOffset OPTIONAL,//从文件的多少位置开始读
IN PULONG Key OPTIONAL//一般给NULL
);

注册表操作

注册表中有下面几个概念:

1. 注册表项:注册表项类似于目录的概念,下面可以有子项或者注册表的键-值对

2. 注册表子项:类似于子目录的概念

3. 键名:通过键名可以寻找到相应的键值

4. 键值类别:每个键值在存储的时候有不同的类型,相当于变量的类型,主要有字符串和整型

5. 键值:键名下对应存储的数据

创建和关闭注册表

创建注册表使用函数ZwCreateKey

NTSTATUS
ZwCreateKey(
OUT PHANDLE KeyHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN ULONG TitleIndex,
IN PUNICODE_STRING Class OPTIONAL,
IN ULONG CreateOptions,
OUT PULONG Disposition OPTIONAL
);
  1. KeyHandle:输出一个注册表对应项的句柄,以后针对这个项操作都是以这个句柄作为标示
  2. DesiredAccess:访问权限,一般都设置为KEY_ALL_ACCESS
  3. ObjectAttributes:用法与文件操作中的用法相同

    其中应用层中注册表项与内核中注册表项的对应关系如下:
应用层中的子健 内核中的路径
HKEY_CLASSES_ROOT 没有对应的路径
HKEY_CURRENT_USER 没有简单的对应路径,但是可以求得
HKEY_USERS \Registry\User
HKEY_LOCAL_MACHINE \Registry\Machine

4. TitleIndex:一般设置为0

5. Class 一般给NULL

6. CreateOptions:创建选项,一般给REG_OPTION_NON_VOLATILE

7. Disposition:返回创建的状态,如果是REG_CREATED_NEW_KEY表示创建了一个新的注册表项如果是REG_OPENED_EXISTING_KEY表示打开一个已有的注册表项

8. ### 添加、修改注册表键

注册表中的键是类似与字典中的键值对,通过键名找到对应的值,键值的类型大致可以分为下面几种

分类 描述
REG_BINARY 键值采用二进制存储
REG_SZ 键值用宽字符串,以\0结尾
REG_EXPAND_SZ 与上面的REG_SZ相同,它是上面那个字符串的扩展字符
REG_MULTI_SZ 能够存储多个字符串,每个都以\0隔开
REG_DWORD 键值用4字节整型存储(这个类型的数据在驱动中使用ULONG来替代)
REG_QWORD 键值用8字节存储(这个用LONGLONG)

用函数ZwSetValueKey可以添加和修改注册表的一项内容

 NTSTATUS
ZwSetValueKey(
IN HANDLE KeyHandle, //注册表句柄
IN PUNICODE_STRING ValueName,//要修改或者新建的键名
IN ULONG TitleIndex OPTIONAL,//一般设置为0
IN ULONG Type,//在上面的表中选择一个
IN PVOID Data,//键值
IN ULONG DataSize//键值数据的大小
);

当传入的键值不存在则创建一个新键值,否则就修改原来的键值

查询注册表

查询注册表使用函数ZwQueryValueKey

NTSTATUS
ZwQueryValueKey(
IN HANDLE KeyHandle, //注册表句柄
IN PUNICODE_STRING ValueName,//注册表键名
IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
OUT PVOID KeyValueInformation,//接收返回信息的缓冲区
IN ULONG Length,//缓冲区的大小
OUT PULONG ResultLength//真实缓冲区的大小
);

使用这个函数时利用参数KeyValueInformationClass来指定接收数据的类型,根据这个值的不同,函数会返回不同的结构体放到一个缓冲区中。一般这个值可取:KeyValueBasicInformation 返回注册表项的基础信息

KeyValueFullInformation 返回注册表的全部信息

KeyValuePartialInformation 返回注册表的部分信息

一般情况下使用KeyValuePartialInformation查询键值数据

利用这个函数来查询时一般也是采用两次调用的方式,第一次返回数据所需缓冲,然后分配缓冲并进行第二次调用

枚举子项

DDK提供了两个函数用于这个功能

NTSTATUS
ZwQueryKey(
IN HANDLE KeyHandle,//注册表句柄
IN KEY_INFORMATION_CLASS KeyInformationClass,//保存注册表信息的结构体的类型
OUT PVOID KeyInformation,//返回查询到信息的缓冲
IN ULONG Length,//缓冲的大小
OUT PULONG ResultLength//真正信息的大小
); NTSTATUS
ZwEnumerateKey(
IN HANDLE KeyHandle,//句柄
IN ULONG Index,//这个值是表示第几个子项
IN KEY_INFORMATION_CLASS KeyInformationClass,//查询到的信息的结构体
OUT PVOID KeyInformation,//返回信息的缓冲
IN ULONG Length,//缓冲长度
OUT PULONG ResultLength//返回信息的长度
);

其中ZwQueryKey函数用于查询某个注册表项中有多少个子项,在调用这个函数时传入的KeyInformationClass的值一般给KeyFullInformation,在这个结构体中的SubKeys表示有多少个子项,而ZwEnumerateKey则是用于查询各个子项中的具体内容,通过指定Index表示我们要查询该项中的第几个子项,将KeyInformationClass填入KeyBasicInformation,这样在结构体的Name里面可以得到具体的注册表子项的名称

枚举子健

枚举子键的方法于上面的大致相同,首先利用ZwQueryKey查询注册表,然后取结构体KeyFullInformation的成员Values,根据这个值在循环中依次调用函数ZwEnumerateValueKey,结构体类填入 KeyValueBasicInformation查询基本信息即可

删除子项

删除子项使用的内核函数是ZwDeleteKey

NTSTATUS
ZwDeleteKey(
IN HANDLE KeyHandle
);

这个函数只能删除没有子项的项目,如果有子项,则需要先删除所有子项。

其他注册表函数

为了简化注册表操作,DDK提供了另外一组以Rtl开头的函数,把之前的Zw函数进行了封装,下面是这些函数与它们功能的对应关系

函数名 描述
RtlCreateRegistryKey 创建注册表项
RtlCheckRegistryKey 查看注册表中的某项是否存在
RtlWriteRegistryValue 写注册表
RtlDeleteRegistryValue 删除注册表的子键

Windows内核函数的更多相关文章

  1. 《Windows驱动开发技术详解》之Windows内核函数

    内核模式下字符串操作 ANSI_STRING和UNICODE_STRING分别定义如下:

  2. windows内核需要注意的

    修改windows内核函数 先屏蔽KdPrint 测试. Hook函数一律使用全局变量 妹的..KiTrap0E 修改.触发了已经断点.但是硬件断点Hook函数里只要使用KdPrint 就蓝屏

  3. Windows 驱动发展基金会(九)内核函数

    Windows 驱动发展基金会系列,转载请注明出处:http://blog.csdn.net/ikerpeng/article/details/38849861 这里主要介绍3类Windows的内核函 ...

  4. 几个常用内核函数(《Windows内核情景分析》)

    参考:<Windows内核情景分析> 0x01  ObReferenceObjectByHandle 这个函数从句柄得到对应的内核对象,并递增其引用计数. NTSTATUS ObRefer ...

  5. windows内核情景分析之—— KeRaiseIrql函数与KeLowerIrql()函数

    windows内核情景分析之—— KeRaiseIrql函数与KeLowerIrql()函数 1.KeRaiseIrql函数 这个 KeRaiseIrql() 只是简单地调用 hal 模块的 KfRa ...

  6. Windows内核-7-IRP和派遣函数

    Windows内核-7-IRP和派遣函数 IRP以及派遣函数是Windows中非常重要的概念.IRP 是I/O Request Pocket的简称,意思是I/O操作的请求包,Windows中所有Use ...

  7. Windows API 函数列表 附帮助手册

    所有Windows API函数列表,为了方便查询,也为了大家查找,所以整理一下贡献出来了. 帮助手册:700多个Windows API的函数手册 免费下载 API之网络函数 API之消息函数 API之 ...

  8. 【转载】64 位 Windows 内核虚拟地址空间布局(基于 X64 CPU)

    原文链接:http://shayi1983.blog.51cto.com/4681835/1734822 本文为原创翻译,原文出处为 http://www.codemachine.com/articl ...

  9. Windows内核遍历驱动模块源码分析

    要获取windows 内核中所有驱动模块信息,调用 系统服务函数 NtQuerySystemInformation,参数SystemInformationClass 传入SystemModuleInf ...

随机推荐

  1. Emoji表情符号录入MySQL数据库报错的解决方式

    前言:手机app应用评论的时候,恢复表情符号.提示失败.​1,查看tomcat后台日志,核心报错信息例如以下:  Caused by: java.sql.SQLException: Incorrect ...

  2. Spring Cache简单介绍和使用

    Spring Cache 缓存是实际工作中非经常常使用的一种提高性能的方法, 我们会在很多场景下来使用缓存. 本文通过一个简单的样例进行展开,通过对照我们原来的自己定义缓存和 spring 的基于凝视 ...

  3. 2015级C++第4周项目 函数

    [项目1-求最大公约数] 參考解答 (1)输入两个数.并求出其最大公约数 #include <iostream> using namespace std; //自己定义函数的原型(即函数声 ...

  4. 1692: [Usaco2007 Dec]队列变换|后缀数组|贪心

    将字符串翻转后接到原串的后面,中间加一个分隔符,每次都贪心选择rankrank小的那个 事实上就是练习一发后缀数组的模板 #include<algorithm> #include<i ...

  5. 由一道bash jail题引出的琐事@_@

    关键词:Terminal devices.shell.stdio 题目入口: (需要注册) root@kali:~# ssh level1@24.37.41.154 -p 1016 level1@24 ...

  6. Mac环境下实现alias重命名命令(永久生效)

    Mac环境下实现alias重命名命令 iOS Dev在使用Xcode完成代码编写后,可能需要上传至第三方分发应用给测试人员进行相关测试,比如蒲公英.FIR. 效率较高的上传方式是借助于Fastlane ...

  7. spring boot https --restful接口篇

    我们写的接口默认都是http形式的,不过我们的接口很容易被人抓包,而且一抓全是明文的挺尴尬的 spring boot配置https生成证书大的方向有3种: 1.利用keytool自己生成证书 2.从免 ...

  8. MySQL MVCC机制

    本文同时发表在https://github.com/zhangyachen/zhangyachen.github.io/issues/68 行结构 每一行额外包含三个隐藏字段: DB_TRX_ID:事 ...

  9. “乐”动人心--2017年10款最佳音乐类APP设计盘点

    在上下班的路上,听几首自己喜欢的音乐来打发无聊的等公交车和地铁的时间是现代年轻人的常态.音乐作为最能鼓动人心的"语言",也成为了人们在互联网生活里占比例最高的消费活动之一,一款好看 ...

  10. Nodejs进阶:crypto模块中你需要掌握的安全基础

    本文摘录自<Nodejs学习笔记>,更多章节及更新,请访问 github主页地址. 一. 文章概述 互联网时代,网络上的数据量每天都在以惊人的速度增长.同时,各类网络安全问题层出不穷.在信 ...