Windows 内核函数

Windows 内核函数

因为C语言库是运行的R3应用层上的,而驱动程序是运行的内核模式下的。应用层的函数能调用内核层的函数库,但是内核层的函数由于一些安全性的权限措施,不能调用上层库。所以普通的C语言库是不能在内核模式下运行的,必须使用DDK提供的运行时函数。

1、内核模式下的字符串操作

1.1ASCII字符串和宽字符串

a)char型字符串:负责记录ANSI字符集,它指向一个char数组的指针,每个char型变 量的大小为一个字节,字符串是以0标志字符串结束的

Eg: char* str1 = “abc”;

b)wchar_t型的宽字符串:负责描述unicode字符集的字符串,它指向一个wchar_t数组的指针,wchar_t字符大小为两个字节,字符以0标志字符串结束

Eg: wchar_t * str2 = L”abc”;//使用关键字L,编译器会自动生成所需要的宽字符

在驱动程序开发中,DDKcharwchar_t类别,替换成CHARWCHAR类别。

以上两个例子分别替换成:

CHAR * string = abc;

KdPrint((%s,string));

WCHAR *string=Labc;

KdPrint((%S,string));

 

1.2 ANSI_STRING字符串与UNICODE_STRING字符串

DDK不鼓励程序员使用C语言的字符串,主要因为:标准C的字符串处理函数容易导致缓冲区溢出等错误,如果程序员不对字符串的长度进行检验,很容易导致这个错误,从而导致整个操作系统的崩溃。

DDK鼓励程序员使用DDK自定义的字符串,这种数据格式定义如下:

Typedef struct _STRING{

USHORT Length;//字符的长度

USHORT MaximumLength;//整个字符串缓冲区的最大长度

PCHAR Buffer;//缓冲区指针

}STRING;

Typedef STRING ANSI_STRING;

Typedef PSTRING PANSI_STRING;

Typedef STRING OEM_STRING;

Typedef PSTRING POEM_STRING;

 

ANSI_STRING相对应,DDK将宽字符串封装成UNICODE_STRING数据结构。

Typedef struct _UNICODE_STRING{

USHORT Length;//字符的长度,单位是字节,如果是N个字符,那么Length等于N2

USHORT MaximumLength;//整个字符串缓冲区的最大长度

PCHAR Buffer;//缓冲区指针

}UNICODE_STRING;

 

关于ANSISTRING字符串和UNICODE_STRING字符串,KdPrint同样提供了打印log的方法

ANSI_STRING ansiString;

KdPrint((“%Z\n”,&ansiString));//大写的Z

UNICODE_STRING uniString;

KdPrint((“%wz\n”,uniString));//小写的wz

 

1.3、字符初始化与销毁

ANSI_STRING字符串和UNICODE_STRING字符串使用前需要进行初始化,有两种办法构造该数据结构。

a)、使用DDK提供的相应函数

RtlInitAnsiString(IN OUT PANSI_STRING des,IN PCSZ sour)

该方法将sour的指针指向des,修改一个值,那么另一个值也会发生变化

b)、程序员自己申请内存,并初始化内存,当不用字符串时,需回收字符串占用的内存

ExAllocatePool(size)//用法与C语言的mallocate相似

ExFreePool(UNICODE_STRING);//释放内存

1.4、字符串复制

ANSI_STRING字符串复制函数:

VOID RtlCopyString(IN OUT PSTRING DestStr,IN PSTRING SourStr OPTIONAL);

UNICODE_STRING字符串复制函数:

VOID RtlCopyUnicodeString(IN OUT PSTRING DestStr,IN PSTRING SourStr );

 

1.5、字符串比较

ANSI_STRING比较函数:

//第三个参数表示是否忽略大小写

LONG RtlCompareString(IN PSTRING str1,IN PSTRING str2,BOOLEAN caseinSen);

UNICODE_STRING 比较函数:

LONG RtlCompareUnicodeString(IN PUNICODE_STRING str1,IN PUNICODE_STRING str2,BOOLEAN caseinSen);

以上两个函数,返回值:如果是0,表示两个字符串相等

小于0str1小于str2

大于0str1大于str2

比较是否相等的函数:RtlEqualStringRtlEqualUnicodeString函数,返回非零表示相等,翻译0表示不等

1.6、字符串转化成大写

a)、ANSI_STRING字符串转化成大写:

VOID RtlUpperString(IN OUT PSTRING destStr,IN PSTRING SourStr);

b)、UNICODE_STRING字符串转化成大写:

//第三个参数表示是否为摩的字符串分配内存

NTSTATUS RtlUpcaseUnicodeString(IN OUT PUNICODE_STRING destStr OPTIONAL,IN PCUNICODE_STRING sourStr,IN BOOLEAN allocateDestStr);

 

1.7、字符串与整型数字相互转换

UNICODE_STRING字符串转换成整数:

//参数分别表示:需要转换字符串,转化数的进制(2,8,10,16...),需要转换的数字

NTSTATUS RtlUnicodeStringToInteger(IN PUNICODE_STRING String,IN ULONG BASE OPTIONAL,OUT PULONG Value);

将整形转换成UNICODE_STRING字符串

//参数:需要转换的数字,进制,需要转换的字符串

RtlIntegerToUnicodeString(IN ULONG Value,IN ULONG Base OPTIONAL,IN OUT PUNICODE_STRING String);

 

1.8ANSI_STRING字符串与UNICODE_STRING字符串相互转换

UNICODE_STRING字符串转换为ANSI_STRING字符串

NTSTATUS RtlUnicodeStringToAnsiString(IN OUT PANSI_STRING DestStr,IN PUNICODE_STRING SourStr,IN BOOLEAN AllocateDestStr);

ANSI_STRING字符串转换成UNICODE_STRING字符串

NTSTATUS RtlAnsiStringToUnicodeString(IN PUNICODE_STRING SourStr,IN OUT PANSI_STRING DestStr,IN BOOLEAN AllocateDestStr);

2、内核模式下的文件操作

2.1 文件的创建

对文件的创建或者打开都是通过内核函数ZwCreateFile实现的。这个内核函数返回一个文件句柄,文件的所有操作都是依靠这个句柄进行操作的,在文件操作完毕后,需要关闭这个句柄

NTSTATUS 
  ZwCreateFile(
    __out PHANDLE  FileHandle,//返回打开文件的句柄
    __in ACCESS_MASK  DesiredAccess,//对打开文件操作的描述,读、写或者其他
    __in POBJECT_ATTRIBUTES  ObjectAttributes,//包含要打开的文件名
    __out PIO_STATUS_BLOCK  IoStatusBlock,//指向一个IO_STATUS_BLOCK结构,接收操作结果状态
    __in_opt PLARGE_INTEGER  AllocationSize,//指针,指定文件初始分配时的大小
    __in ULONG  FileAttributes,//指定新创建文件的属性
    __in ULONG  ShareAccess,//指定文件的共享方式
    __in ULONG  CreateDisposition,//指定文件存在或不存在时如何处理
    __in ULONG  CreateOptions,//指定控制打开操作和句柄使用的附加标志位
    __in_opt PVOID  EaBuffer,//指向可选的扩展属性区
    __in ULONG  EaLength //扩展属性区的长度
    );

文件路径是通过第三个参数来指定的,另外,文件名必须是符号链接或者是设备名。例如:盘符“c:”就是一个符号链接,这里应该用“\??\c:”代替,“c:\abc.txt”要写成“\??\c:\abc.txt

其中,“\??\c:”是符号链接,内核会将它转换成设备名“\Device\HarddiskVolume1”。

对应关闭文件句柄ZwClose(hfile);

 

2.2 文件打开

除了使用ZwCreateFile函数可以打开文件,DDK还提供了一个内核函数ZwOpenFile函数打开文件

NTSTATUS
  ZwOpenFile(
    OUT PHANDLE  FileHandle,//返回打开的文件句柄
    IN ACCESS_MASK  DesiredAccess,//打开的权限
    IN POBJECT_ATTRIBUTES  ObjectAttributes,//objectAttribute结构
    OUT PIO_STATUS_BLOCK  IoStatusBlock,//指向一个结构体指针,指明打开文件的状态
    IN ULONG  ShareAccess,//共享的权限
    IN ULONG  OpenOptions//打开选项
    );

对应关闭文件句柄ZwClose(hfile);

 

2.3、获取或修改文件属性

包括获取文件大小,获取或修改文件指针位置、获取或修改文件名、获取或修改文件属性(只读,隐藏属性)、获取或修改文件创建、修改如期等

DDK提供了内核函数ZwSetInformationFileZwQueryInformationFile函数来进行获取和修改文件属性

NTSTATUS 
  ZwSetInformationFile(
    IN HANDLE  FileHandle,//文件句柄
    OUT PIO_STATUS_BLOCK  IoStatusBlock,//返回设置状态
    IN PVOID  FileInformation,//依据FileInformationClass不同而不同,作为输入信息
    IN ULONG  Length,//FileInformation数据的长度
    IN FILE_INFORMATION_CLASS  FileInformationClass//描述修改属性的类型
    );

 

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

a)、当FileInformationClassFileStandardInformation时,输入和输出的数据是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;

b)、当FileInformationClassFileBasicInformation时,输入和输出的数据是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;

c)、当FileInformationClassFileNameInformation时,输入和输出的数据是FILE_NAME_INFORMATION结构体,描述文件名信息。

typedef struct _FILE_NAME_INFORMATION {
  ULONG  FileNameLength;//文件名长度
  WCHAR  FileName[1];//文件名
} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION;

d)、当FileInformationClassFilePositionInformation时,输入和输出的数据是FILE_POSITION_INFORMATION结构体,描述文件名信息

typedef struct FILE_POSITION_INFORMATION {
  LARGE_INTEGER  CurrentByteOffset;//代表当前文件指针位置
} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION;

 

2.4、文件的写操作

文件写操作的内核函数,其函数声明如下:

NTSTATUS 
  ZwWriteFile(
    IN HANDLE  FileHandle,
    IN HANDLE  Event  OPTIONAL,
    IN PIO_APC_ROUTINE  ApcRoutine  OPTIONAL,
    IN PVOID  ApcContext  OPTIONAL,
    OUT PIO_STATUS_BLOCK  IoStatusBlock,
    IN PVOID  Buffer,//从这个缓冲区开始往文件里写
    IN ULONG  Length,//准备写多少字节
    IN PLARGE_INTEGER  ByteOffset  OPTIONAL,//从文件的多少偏移地址开始写
    IN PULONG  Key  OPTIONAL
    );

2.5、文件的读操作

NTSTATUS 
  ZwReadFile(
    IN HANDLE  FileHandle,
    IN HANDLE  Event  OPTIONAL,
    IN PIO_APC_ROUTINE  ApcRoutine  OPTIONAL,
    IN PVOID  ApcContext  OPTIONAL,
    OUT PIO_STATUS_BLOCK  IoStatusBlock,
    OUT PVOID  Buffer,
    IN ULONG  Length,
    IN PLARGE_INTEGER  ByteOffset  OPTIONAL,
    IN PULONG  Key  OPTIONAL
    );

3、内核模式下的注册表操作

 

几个注册表相关的概念:

a)、注册表项:注册表中的一个项目,类似目录的概念。每个项中存储多个二元结构,键名—键值。每个项中,可以有若干个子项。

b)、注册表子项:类似于目录中的子目录

c)、键名:通过键名可以寻找相应的键值

d)、键值类别:每个键值存储的时候有不同的类别,可以是整型、字符型串等数据

e)、键值:键名下对应存储的数据

 

3.1、创建关闭注册表

NTSTATUS 
  ZwCreateKey(
    OUT PHANDLE  KeyHandle,//获得的注册表句柄
    IN ACCESS_MASK  DesiredAccess,//访问权限,一般设置为KEY_ALL_ACCESS
    IN POBJECT_ATTRIBUTES  ObjectAttributes,//OBJECT_ATTRIBUTE数据结构
    IN ULONG  TitleIndex,
    IN PUNICODE_STRING  Class  OPTIONAL,
    IN ULONG  CreateOptions,//创建时的选项
    OUT PULONG  Disposition  OPTIONAL//返回是创建成功还是打开成功
    );

 

3.2、打开注册表

NTSTATUS 
  ZwOpenKey(
    OUT PHANDLE  KeyHandle,//返回被打开的句柄
    IN ACCESS_MASK  DesiredAccess,//打开的权限
    IN POBJECT_ATTRIBUTES  ObjectAttributes
    );

 

3.3、添加、修改注册表键值

键值的分类

 

NTSTATUS 
  ZwSetValueKey(
    IN HANDLE  KeyHandle,
    IN PUNICODE_STRING  ValueName,//键名
    IN ULONG  TitleIndex  OPTIONAL,
    IN ULONG  Type,
    IN PVOID  Data

    IN ULONG  DataSize//记录键值数据的大小

    );

 

3.4、查询注册表

NTSTATUS 
  ZwQueryValueKey(
    IN HANDLE  KeyHandle,
    IN PUNICODE_STRING  ValueName,//要查询的键名
    IN KEY_VALUE_INFORMATION_CLASS  KeyValueInformationClass,//根据keyValueInformation选择查询类别
    OUT PVOID  KeyValueInformation,
    IN ULONG  Length,//要查询数据的长度
    OUT PULONG  ResultLength//实际查询数据的长度
    );

一般使用ZwQueryValueKey有以下4个步骤:

a)、用ZwQueryValueKey获取这个数据结构的长度

b)、分配如此长度的内存,用来查询

c)、再次调用ZwQueryValueKey,获取键值

d)、回收内存

 

3.5、枚举子项

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
    );

 

3.6、枚举子键

NTSTATUS 
  ZwQueryValueKey(
    IN HANDLE  KeyHandle,
    IN PUNICODE_STRING  ValueName,
    IN KEY_VALUE_INFORMATION_CLASS  KeyValueInformationClass,
    OUT PVOID  KeyValueInformation,
    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
    );

 

3.7、删除子项

NTSTATUS 
  ZwDeleteKey(//该函数只能删除没有子项的项目
    IN HANDLE  KeyHandle
    );

 

小结:本章介绍了WIndows内核模式下的一些常用内核函数,这些函数在驱动程序的开发中将会经常用到。对这些内核函数的熟练掌握十分必要。本章介绍了字符串相关的一些内核函数。这些函数包括操作标准C语言字符串,也包括操作UNICODE字符串的。另外,还介绍了与文件及注册表相关的内核函数。

 



以上内容参考自张帆 史彩成等编著的《Windows 驱动开发技术详解》第六章

 


Windows 内核函数,布布扣,bubuko.com

Windows 内核函数

上一篇:sql 入门经典(第五版) Ryan Stephens 学习笔记 (第一,二,三,,四,五章)


下一篇:VS 编译总是出现错误: "LC.EXE 已退出,代码为-1"