这差不多是去年写的学习笔记了...当时后面部分好像偷懒了...没写全的样子...呵呵~当时本来还打算A掉Virtual CD ROM的VcdRom.sys,不过debugman上已经有人放出src了...所以也就学习了一下...下面凑合着看.
filedisk是一个网上流传比较广泛的开源程序,它能够 mount / unmount CD/DVD images.通俗点说就是实现虚拟光驱的功能,也是一个学习磁盘过滤驱动非常好的例子.
你可以从Windows driver examples下载最新的filedisk源代码,下面的代码是基于最新的filedisk-17.
[1.0] FileDisk的使用
打开install.txt文件.里面讲述的很清楚.
首先,将filedisk.sys驱动程序复制到%systemroot%\system32\drivers\.目录下.
然后,双击filedisk.reg文件.添加到注册表中.
最后,重启即可.
上面这些其实就是实现对 filedisk.sys的驱动安装而已,在启动的时候将filedisk.sys加载到内核中.
重启之后,就可以在控制台中运行filedisk.exe程序,可以使用 mount 和 unmount 命令了.
[2.0] FileDisk驱动分析.
FileDisk驱动程序,通过增加一个设备,实现磁盘过滤.
对磁盘的过滤,重点要关注的是对磁盘的读,写,以及关于磁盘信息的获取.
[2.1] filedisk.h
1.定义了设备名和符号链接名
2.定义三个IOCTL_CODE,分别与Read,Write,Query三个操作相关
3.定义了 OPEN_FILE_INFORMATION结构,该结构保存和传递关于被mount的文件的相关信息.
typedef struct _OPEN_FILE_INFORMATION {
LARGE_INTEGER FileSize;// 文件大小
BOOLEAN ReadOnly;// 是否可读写
UCHAR DriveLetter;// 盘符
USHORT FileNameLength;// 文件名的长度
UCHAR FileName[1];// 文件名
} OPEN_FILE_INFORMATION, *POPEN_FILE_INFORMATION;
[2.2]filedisk.c
再分析函数之前先来熟悉下几个结构
1.DEVICE_EXTENSION结构.
其实关于DEVICE_EXTENSION,它是由I/O管理器自动分配这个内存,并把设备对象中的DeviceExtension指针指向这块内存.
typedef struct _DEVICE_EXTENSION {
BOOLEAN media_in_device;//是否指定了一个文件作为存储媒介
HANDLE file_handle; // 文件句柄
ANSI_STRING file_name;// 文件名
LARGE_INTEGER file_size;// 文件大小
BOOLEAN read_only;// 是否只读
BOOLEAN is_cd_or_dvd;// 是否为cd/dvd
PSECURITY_CLIENT_CONTEXT security_client_context;// 线程客户安全context
LIST_ENTRY list_head;// 链表头
KSPIN_LOCK list_lock;// 自旋锁
KEVENT request_event;// 请求事件
PVOID thread_pointer;// 线程函数指针
BOOLEAN terminate_thread;// 线程结束标志
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
media_in_device是指这个设备是否已经指定了一个文件作为存储媒质。这是一个用文件来虚拟磁盘的驱动。那么一个磁盘应该对应一个实际存在的文件。读写这个磁盘的请求最终转变为对文件的读写。如果一个磁盘设备对象还没有指定文件,那么这个内容是FALSE.
file_handle是文件句柄。也就是这个虚拟磁盘所对应的文件。
file_information是这个文件的一些信息。
read_only是否只读。
security_client_context 访问文件的时候需要使用的一个线程客户安全性上下文。
list_head是一个链表头。一部分irp(windows发来的请求包)被放入这个链表中。我们为每个磁盘对象开启一个系统线程(处理线程),专门用来处理这些请求。
list_lock是为了保证链表读写同步的锁。
request_event是一个事件。当链表中没有请求的时候,处理请求的系统线程并不做任何事情,而只等待这个事件。当有请求到来,我们把请求放入链表,然后设置这个事件。处理线程就会开始处理这些请求。
thread_pointer是线程的指针,用来最后等待这个线程的结束。
terminate_thread是一个标志。如果设置为TRUE,处理线程执行的时候检测到这个,就会把自己终止掉。
在DriverEntry中.
1.首先通过RtlQueryRegistryValues()函数,获得注册表相关信息.
主要是为了获得n_devices的值.n_devices决定生成多少个磁盘设备对象,默认的数值为4.
2.然后通过FileDiskCreateDevice()创建磁盘设备对象.
3.设置好IRP分发函数.
NTSTATUS
DriverEntry (
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
.....
//查询注册表信息,获取n_devices
status = RtlQueryRegistryValues(
RTL_REGISTRY_ABSOLUTE,
parameter_path.Buffer,
&query_table[0],
NULL,
NULL
);
// 如果失败,将n_devices设为默认值4
if (!NT_SUCCESS(status))
{
KdPrint(("FileDisk: Query registry failed, using default values.\n"));
n_devices = DEFAULT_NUMBEROFDEVICES;
}
....
// 创建n_devices个磁盘设备对象,FILE_DEVICE_DISK类型的
for (n = 0, n_created_devices = 0; n < n_devices; n++)
{
status = FileDiskCreateDevice(DriverObject, n, FILE_DEVICE_DISK);
if (NT_SUCCESS(status))
{
n_created_devices++;
}
}
// 创建n_devices个磁盘设备对象,FILE_DEVICE_CD_ROM类型的.
for (n = 0; n < n_devices; n++)
{
status = FileDiskCreateDevice(DriverObject, n, FILE_DEVICE_CD_ROM);
if (NT_SUCCESS(status))
{
n_created_devices++;
}
}
.....
// 设置分发函数
DriverObject->MajorFunction[IRP_MJ_CREATE] = FileDiskCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = FileDiskCreateClose;
DriverObject->MajorFunction[IRP_MJ_READ] = FileDiskReadWrite;
DriverObject->MajorFunction[IRP_MJ_WRITE] = FileDiskReadWrite;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = FileDiskDeviceControl;
DriverObject->DriverUnload = FileDiskUnload;
return STATUS_SUCCESS;
}
FileDiskCreateDevice()函数.
1.首先判断DeviceType,然后根据类型的不同,添加的Devicename的前缀也不一样.CD类型的前缀带"Cd"
2.调用IoCreateSecureDevice(),创建设备.初始化链表头,初始化SpinLock(自旋锁),初始化Event(事件),这些都用于后面的线程同步.
3.调用PsCreateSystemThread(),创建系统线程函数.FileDiskThread(). thread_handle保存线程处理函数的句柄.
4.调用ObReferenceObjectByHandle(),保存线程函数的指针.
FileDiskThread()函数
1.调用KeSetPriorityThread(),设置当前线程的优先级为LOW_REALTIME_PRIORITY.
2.for(;;)死循环中调用KeWaitForSingleObject(),等待reques_event事件.如果等到事件,则判断事件类型,分别处理.
IRP_MJ_READ:调用ZwReadFile读取文件,从内核到用户缓冲区;
IRP_MJ_WRITE:调用ZwWriteFile写入文件,从用户到内核缓冲区;
IRP_MJ_DEVICE_CONTROL:在FileDiskDeviceControl设置事件才会触发,主要有如下两种操作码:
IOCTL_FILE_DISK_OPEN_FILE:调用FileDiskOpenFile。
IOCTL_FILE_DISK_CLOSE_FILE:调用FileDiskCloseFile。
FileDiskOpenFile:根据用户程序传入的映像文件全路径,调用ZwCreateFile在内核中打开它,如果文件不存在则再创建它,返回文件句柄。
FileDiskCloseFile:调用ZwClose关闭文件。