文件系统驱动编程基本篇之3——Ioctl节制操纵
副标题#e#
二、工具打点与定名空间(Namespace)
内核空间中差异范例的工具都通过工具打点器统一打点,并通过定名空间这一逻辑上的观念来组织各个工具,雷同于资源打点器。Device目次存放着通过IoCreateDevice建设的各类设备工具,包罗文件系统驱动下建设的卷工具。FileSystem目次存放着文件系统驱动工具和文件系统识别器设备工具(这些内容将在进阶篇论述)。更详细的描写请参看资料2。
到今朝为止,我们还未接头过用户模式下的应用措施如何与驱动措施产生交互,请临时健忘“间断门”、“陷阱门”这类“高妙莫测”的术语(大举鼓吹这些术语反而有引入歧途的念头),这些包括在CPU硬件理论中的基本常识不会对我们进修驱动编程有直接的影响,相反,值得一提的却是CreateFile函数。文件是一个高度抽象的观念,既然内核中的工具可以被统一打点,外部的各类设备自然也不破例,它们都可以用文件来加以描写。从图中我们看到计较机中的串口COM1,它对应着设备工具Serial0,而C:盘,对应着是卷设备工具HarddiskVolume4,这是一种称为“标记链接”的映射,通过这个映射,用户模式下的措施才气看到内核中的设备工具,也才可以通过CreateFile打开它们。形象的说,标记链接雷同于奶名,如大狗一般就称为“旺财”,小狗就叫做“小白”。在内核中成立标记毗连可利用IoCreateSymbolicLink,用户模式下可用DefineDosDevice。
CreateFile的利用示例,留意“.”对应着定名空间里的“GLOBAL??”:
if ((hDevice = CreateFile( "\\\\.\\IoctlTest",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL)) == INVALID_HANDLE_VALUE) {
另一种途径就是Ioctl节制操纵。
三、Ioctl节制码
Ioctl节制码的布局雷同于动静(如WM_XXX)或NTSTATUS的界说方法,它是一个驱动措施预界说的4字节整数,界说它的宏为:
#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)
16-31 2-13 0-1 14-15
通过提供设备范例、成果码(可看作函数的序号)、缓冲方法和存取权限,该宏就建设了一个Ioctl码。设备驱动可以界说多个Ioctl码(通过差异的成果码来区分差异的成果函数)以提供差异的节制成果。
#p#副标题#e#
四、Ioctl的同异步与缓冲区操纵
利用DeviceIoControl函数来实现用户模式下的Ioctl操纵,它的界说如下:
BOOL DeviceIoControl( HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuffer,
DWORD nInBufferSize,
LPVOID lpOutBuffer,
DWORD nOutBufferSize,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped,
);
按照Ioctl码的差异,DeviceIoControl函数可以发出两种IRP:IRP_MJ_FILE_SYSTEM_CONTROL和IRP_MJ_DEVICE_CONTROL,前者代表了file system I/O control (FSCTL)请求,后者代表了设备的IOCTL请求。尚有一种仅仅在内核模式下利用的IRP_MJ_INTERNAL_DEVICE_CONTROL,它用于内核差异组件间的通信。
DeviceIoControl需要提供输入和输出缓冲区:
该函数的利用请参看资料1的第九章三小节,最后一个参数的存在,使Ioctl可以以同步或异步(前提是hDevice以FILE_FLAG_OVERLAPPED方法打开)的方法来完成。同步方法下,函数必需期待内核中的操纵完成才返回,不然将当即返回。
Ioctl码的缓冲区范例可分为三类:METHOD_BUFFERED、METHOD_IN_DIRECT和METHOD_OUT_DIRECT、METHOD_NEITHER。差异范例的缓冲区浮现了操纵的效率上的差异:
我们暂不剖析驱动措施如何找到用户模式下的输入、输出缓冲区,以及如何界说拷贝缓冲区,先来存眷一下这三种方法的差异之处。上两图清晰的表达了自身的特点:Buffered方法在输入、输出数据时都要发生用户缓冲区与内核中的拷贝缓冲区间的复制操纵,效率上较低,而Direct方法从字面上领略即为“直接”,从上图也可看出,它的输出操纵是通过MDL方法完成的,这是一种用户模式内存映射到系统(内核)内存的要领,制止了复制操纵,效率上就提高了。
在Neither方法下,I/O打点器直接将用户模式下的缓冲区地点通报给内核驱动措施,不做任何映射调动。驱动假如想直接利用这个地点,必需处于这个历程的上下文中,因为只有在同一个上下文中,用户历程和驱动例程利用的同一个地点值,才气被系统映射到同一个内存页面。
#p#分页标题#e#
也许各人会迷惑驱动例程毕竟是由哪个线程执行的?事实上,差异例程的代码很大概由差异范例的线程来执行,有的属于用户模式下的线程,有的属于操纵系统的线程,有的甚至基础不是线程工具。我们以“上下文”来描写驱动例程运行的线程情况,某时刻的它运行在三种上下文的一种中:
- 系统历程上下文System process context
- 特定用户线程(和历程)上下文A specific user thread (and process) context
- 任意用户线程(和历程)上下文Arbitrary user thread (and process) context
我们不妨简朴的领略为,在某种上下文中,CPU正执行着我们的驱动例程指令。“上下文”做为一个观念上的抽象,在详细实现上有着明晰的数据布局,为了更好的领略上下文,请阅读资料6。跟着实践的增加,读者对此将有更深入的领略。
五、Ioctl上的实践
Ioctl有着遍及的应用,它利用简朴,成果却很强大。笔者选择了一些示例,读者可以按照需要选读。
(一)WINDDK\3790\src\general\ioctl
这个示例堪称Ioctl应用的尺度样本。SioctlDeviceControl是实现自界说Ioctl码的函数,需重点研究。IO_STACK_LOCATION子域Parameters的操纵类型,读者可以查阅Msdn上IRP_MJ_DEVICE_CONTROL节的说明。Io仓库的重要性不亚于IRP,需要熟悉它的布局与和相关的函数。
这个示例还演示了如何手动加载处事,这是一个三板斧的进程:
安装驱动措施流程:
1、挪用OpenSCManager()打开处事节制打点器
2、挪用CreateService()建设一个处事,处事范例为内核驱动
3、挪用OpenService()取得处事句柄
启动处事:
4、挪用StartService()启动处事
遏制处事:
4、挪用ControlService()遏制处事
删除处事:
4、挪用DeleteService()删除处事
5、挪用CloseServiceHandle()封锁处事句柄
操纵驱动措施流程:
1、挪用CreateFile()取得设备句柄
2、挪用DeviceIoControl()通报I/O节制代码
3、挪用CloseHandle()封锁设备句柄
(二)《Rootkits——Windows内核的安详防护》第4章2小节的内核钩子
(三)《城里城外看SSDT》
(四)《被占用文件操纵三法》
示例henum需要在c文件首部添加#pragma comment(lib, "ntdll.lib"),并将DDK中的ntdll.lib复制到目次下,要害函数为NtQuerySystemInformation与NtQueryInformationFile。后两个示例和文件自删除技能一样,思路来自对内核工具的领略。
(五)实现了读取磁盘序列号等操纵的diskid32源码
六、结语
本篇内容难度不大,我们在领略Ioctl设计思路的同时也进一步熟悉了各类数据布局。除了示例一,其他示例仅要求以最大本领去领略,本文的参考完成时间为不高出两礼拜。