⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 windows文件系统过滤驱动开发教程.txt

📁 微过滤器驱动开发指南,本文翻译仅仅用做交流学习。我不打算保留任何版权或者承担任何责任。不要引用到赢利出版物中给您带来版权官司。本文的翻译者是楚狂人
💻 TXT
📖 第 1 页 / 共 4 页
字号:
	下面是我常用的内存分配函数。

//-----------------wdf.h中的代码----------------------
// 最简单的分配内存的函数,可以指定分页非分页
_inline wd_pvoid wd_malloc(wd_bool paged,wd_size size)
{
	if(paged)
		return ExAllocatePool(PagedPool,size);
	else
		return ExAllocatePool(NonPagedPool,size);
}

// 释放内存
_inline wd_void wd_free(wd_pvoid point)
{
	ExFreePool(point);
}

_inline wd_void wd_memzero(
			wd_pvoid point,
			wd_size size)
{
	RtlZeroMemory(point,size);
}

	有了上边的基础,我就可以自己写一个初始化FastIoDispatch指针的函数。

//-----------------wdf.h中的代码----------------------
wd_bool wd_fio_disp_init(wd_drv *driver,wd_ulong size)
{
	wd_fio_disp *disp = wd_malloc(wd_false,size);
	if(disp == wd_null)
		return wd_false;
	wd_memzero((wd_pvoid)disp,size);
	driver->FastIoDispatch = disp;
	driver->FastIoDispatch->SizeOfFastIoDispatch = size;
	return wd_true;
}

	这个函数为FastIoDispacth指针分配足够的空间并填写它的大小。下面是再写一系列的函数来设置这个函数指针数组。实际上,FastIo接口函数实在太多了,所以我仅仅写出这些设置函数的几个作为例子:
//-----------------wdf.h中的代码----------------------
_inline wd_void wd_fio_disp_set_query_standard(
					wd_drv *driver,
					wd_fio_query_standard_func func)
{
    driver->FastIoDispatch->FastIoQueryStandardInfo = func;
}

_inline wd_void wd_fio_disp_set_io_lock(
					wd_drv *driver,
					wd_fio_io_lock_func func)
{
    driver->FastIoDispatch->FastIoLock = func;
}

_inline wd_void wd_fio_disp_set_io_unlock_s(
					wd_drv *driver,
					wd_fio_unlock_single_func func)
{
    driver->FastIoDispatch->FastIoUnlockSingle = func;
}

...

	好,如果你坚持读到了这里,应该表示祝贺了。我们回顾一下,wd_main中,应该做哪些工作。

	a.生成一个控制设备。当然此前你必须给控制设置指定名称。

	b.设置Dispatch Functions.

	c.设置Fast Io Functions.

// ----------------wd_main 的近况----------------------------

...

wd_dev			*g_cdo = NULL;

wd_stat wd_main(in wd_drv* driver,
		 in wd_ustr* reg_path)
{
	wd_ustr name;
	wd_stat status = wd_stat_suc;

	// 然后我生成控制设备,虽然现在我的控制设备什么都不干
	wd_ustr_init(&name,L"\\FileSystem\\Filters\\our_fs_filters");
	status = wdff_cdo_create(driver,0,&name,&g_cdo);

	if(!wd_suc(status))
	{
		if(status == wd_stat_path_not_found)
		{
			// 这种情况发生于\FileSystem\Filters路径不存在。这个路径是
			// 在xp上才加上的。所以2000下可能会运行到这里
			wd_ustr_init(&name,L"\\FileSystem\\our_fs_filters");
			status = wdff_cdo_create(driver,0,&name,&g_cdo);
		};
		if(!wd_suc(status))
		{
			wd_printf0("error: create cdo failed.\r\n");
			return status;
		}
	}	

	wd_printf0("success: create cdo ok.\r\n");

	// 开始设置几个分发例程
	wd_drv_set_dispatch(driver,my_disp_default);
	wd_drv_set_create(driver,my_disp_create);
	wd_drv_set_clean_up(driver,my_disp_clean_up);
	wd_drv_set_file_sys_control(driver,my_disp_file_sys_ctl);
	wd_drv_set_close(driver,my_disp_close);
	wd_drv_set_read(driver,my_disp_read);
	wd_drv_set_write(driver,my_disp_write);

	// 指定fast io处理函数
	if(!wd_fio_disp_init(driver,sizeof(wd_fio_disp)))
	{
		wd_dev_del(g_cdo);
		wd_printf0("error: fast io disp init failed.\r\n");
		return wd_stat_insufficient_res;
	}

	// 下面指定的这些函数都定义在wdf_filter_fio.h中,其实这些函数都统
	// 一的返回了false
	wd_fio_disp_set_check(
		driver,
		my_fio_check);
	wd_fio_disp_set_read(
		driver,
		my_fio_read);
	wd_fio_disp_set_write(
		driver,
		my_fio_write);
	wd_fio_disp_set_query_basic(
		driver,
		my_fio_query_basic_info);

	...

}
	FastIo函数个数数量不明,我只觉得很多。因此不打算全部罗列,以"..."敷衍之。某些读者可能会认为这些代码无法调试安装。其实您可以参考sfilter中的示例自己完成这些代码。
	
	现在我们的my_xxx系列的函数还没有开始写,因此驱动也不能编译通过。在后边的内容中再逐步介绍。

		

4.设备栈,过滤,文件系统的感知

	前边都在介绍文件系统驱动的结构,却还没讲到我们的过滤驱动如何能捕获所有发给文件系统驱动的irp,让我们自己来处理?前面已经解释过了设备对象。现在来解释一下设备栈。

	任何设备对象都存在于某个设备栈中。设备栈自然是一组设备对象。这些设备对象是互相关联的,也就是说,如果得到一个DO指针,你就可以知道它所处的设备栈。

	任何来自应用的请求,最终被windows io mgr翻译成irp的,总是发送给设备栈的顶端那个设备。

原始irp                irp           irp          irp
-------------->      ------>      ------->     ----->
             DevTop         Dev2           ...      DevVolumne ... ???
<--------------      <------      <-------      <-----
原始irp(返回)	       irp           irp          irp

	
	上图向右的箭头表示irp请求的发送过程,向左则是返回。可见irp是从设备栈的顶端开始,逐步向下发送。DevVolumue表示我们实际要过滤的Volume设备,DevTop表示这个设备栈的顶端。我们只要在这个设备栈的顶端再绑定一个设备,那发送给Volume的请求,自然会先发给我们的设备来处理。

	有一个系统调用可以把我们的设备绑定到某个设备的设备栈的顶端。这个调用是IoAttachDeviceToDeviceStack,这个调用2000以及以上系统都可以用(所以说到这点,是因为还有一个IoAttachDeviceToDeviceStackSafe,是2000所没有的。这常常导致你的filter在2000下不能用。)

	我自己写了一个函数来帮我实现绑定功能:

//----------------------wdf.h中的内容----------------------------------
//	这个例程把源设备绑定到目标设备的设备栈中去,并返回源设备所直
//  接绑定的设备。注意源设备未必直接绑定在目标设备上。它应绑定在
//  目标设备的设备栈的顶端。
_inline wd_stat wd_dev_attach(in wd_dev *src,
			      in wd_dev *dst,
			      in out wd_dev **attached)
{
	*attached = dst;
	*attached = IoAttachDeviceToDeviceStack(src,dst);
	if(*attached == NULL)
		return wd_stat_no_such_dev;
	return wd_stat_suc;
}

	
	到这里,我们已经知道过滤对Volume的请求的办法。比如“C:”这个设备,我已经知道符号连接为“C:”,不难得到设备名。得到设备名后,又不难得到设备。这时候我们IoCreateDevice()生成一个Device Object,然后调用wd_dev_attach绑定,不是一切ok吗?所有发给“C:”的irp,就必然先发送给我们的驱动,我们也可以捕获所有对文件的操作了!

	这确实是很简单的处理方法。我得到的FileMon的代码就是这样处理的,如果不想处理动态的Volume,你完全可以这样做。但是我们这里有更高的要求。当你把一个U盘插入usb口,一个“J:”之类的Volume动态诞生的时候,我们依然要捕获这个事件,并生成一个Device来绑定它。

	一个新的存储媒质被系统发现并在文件系统中生成一个Volume的过程称为Mounting.其过程开始的时候,FS的CDO将得到一个IRP,其Major Function Code为IRP_MJ_FILE_SYSTEM_CONTROL,Minor Function Code为IRP_MN_MOUNT。换句话说,如果我们已经生成了一个设备绑定文件系统的CDO,那么我们就可以得到这样的IRP,在其中知道一个新的Volume正在Mount.这时候我们可以执行上边所说的操作。

	现在的问题是如何知道系统中有那些文件系统,还有就是我应该在什么时候绑定它们的控制设备。

	IoRegisterFsRegistrationChange()是一个非常有用的系统调用。这个调用注册一个回调函数。当系统中有任何文件系统被激活或者是被注销的时候,你注册过的回调函数就会被调用。

//----------------------wdf.h中的内容----------------------------------
wd_stat wdff_reg_notify(
			in wd_drv *driver,
			in wdff_notify_func func
			)
{
	return IoRegisterFsRegistrationChange(driver,func);
}

	你有必要为此写一个回调函数。

//-------------------我的回调处理函数----------------------------------
wd_void my_fs_notify(
			in wd_dev *dev,
			in wd_bool active)
{
	wd_wchar name_buf[wd_dev_name_max_len];
	wd_ustr name;
	wd_ustr_init_em(&name,name_buf,wd_dev_name_max_len);

	// 如果注册了,就应该得到通知
	wd_printf0("notify: a file sys have been acitved!!! \r\n");
	
	// 得到文件系统对象的名字,然后打印出来
	wd_obj_get_name(dev,&name);
	wd_printf0("notify : file sys name = %wZ\r\n",&name);

	if(active)
	{
		wd_printf0("notify: try to attach.\r\n");
		// ... 请在这里绑定文件系统的控制设备
	}
	else
	{
		wd_printf0("notify: unactive.\r\n");
		// ...		
	}
}

	应该如何绑定一个文件系统CDO?我们在下面的章节再详细描述。

	现在我们应该再在wd_main函数中加上下边的内容:

	if(wdff_reg_notify(driver,my_fs_notify) != wd_stat_suc)
	{
		wd_printf0("error: reg notify failed.\r\n");
		wd_fio_disp_release(driver);
		wd_dev_del(g_cdo);
		g_cdo = wd_null;
		return wd_stat_insufficient_res;
	};
	
	wd_printf0("success: reg notify ok.\n");
	
	我们再次回顾一下,wd_main中,应该做哪些工作。

	a.生成一个控制设备。当然此前你必须给控制设置指定名称。

	b.设置Dispatch Functions.

	c.设置Fast Io Functions.
	
	d.编写一个my_fs_notify回调函数,在其中绑定刚激活的FS CDO.

	e.使用wdff_reg_notify调用注册这个回调函数。


5.绑定FS CDO,文件系统识别器,设备扩展

	上一节讲到我们打算绑定一个刚刚被激活的FS CDO.前边说过简单的调用wd_dev_attach可以很容易的绑定这个设备。但是,并不是每次my_fs_notify调用发现有新的fs激活,我就直接绑定它。

	首先判断是否我需要关心的文件系统类型。我用下面的函数来获取设备类型。

// ------------------wdf.h中的内容-------------------
_inline wd_dev_type wd_dev_get_type(in wd_dev *dev)
{
	return dev->DeviceType;
}

	文件系统的CDO的设备类型有下边的几种可能,你的过滤驱动可能只对其中某些感兴趣。

enum {
	wd_dev_disk_fs = FILE_DEVICE_DISK_FILE_SYSTEM,
	wd_dev_cdrom_fs = FILE_DEVICE_CD_ROM_FILE_SYSTEM,
	wd_dev_network_fs = FILE_DEVICE_NETWORK_FILE_SYSTEM
};

	你应该自己写一个函数来判断该fs是否你所关心的。

// -------------一个函数,判断是否我所关心的fs---------------
wd_bool my_care(wd_ulong type)
{
    return (((type) == wd_dev_disk_fs) ||
			((type) == wd_dev_cdrom_fs) ||
			((type) == wd_dev_network_fs));
}

	下一个问题是我打算跳过文件系统识别器。文件系统识别器是文件系统驱动的一个很小的替身。为了避免没有使用到的文件系统驱动占据内核内存,windows系统不加载这些大驱动,而代替以该文件系统驱动对应的文件系统识别器。当新的物理存储媒介进入系统,io管理器会依次的尝试各种文件系统对它进行“识别”。识别成功,立刻加载真正的文件系统驱动,对应的文件系统识别器则被卸载掉。对我们来说,文件系统识别器的控制设备看起来就像一个文件系统控制设备。但我们不打算绑定它。

	分辨的方法是通过驱动的名字。凡是文件系统识别器的驱动对象的名字(注意是DriverObject而不是DeviceObject!)都为“\FileSystem\Fs_Rec”.

//-------------------用这些代码来跳过文件系统识别器----------------------
wd_wchar name_buf[wd_dev_name_max_len];
wd_ustr name,tmp;

wd_ustr_init_em(&name,name_buf,wd_dev_name_max_len);
wd_ustr_init(&tmp,L"\\FileSystem\\Fs_Rec");

// 我不绑定识别器。所以如果是识别器,我直接返回成功。查看是否是识别
// 器的办法是看是否是\FileSystem\Fs_Rec的设备。
wd_obj_get_name(wd_dev_drv(fs_dev),&name);
if(wd_ustr_cmp(&name,&tmp,wd_true) == 0)
{
	wd_printf0("attach fs dev:is a recogonizer.\r\n");
	return wd_stat_suc;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -