FileDisk 源代码

转自:http://www.ccgcn.com/bbs/redirect.php?tid=974&goto=lastpost

FileDisk.h

FileDisk 是把一个镜像文件Mount成一个卷
fileDisk.h中包含以下内容
1.OPEN_FILE_INFORMATION结构
复制内容到剪贴板
代码:
typedef struct _OPEN_FILE_INFORMATION {
    LARGE_INTEGER   FileSize;    //镜像文件的大小,并不一定是到文件尾。
    BOOLEAN         ReadOnly;    //是否只读
    USHORT          FileNameLength; //文件名长度
    UCHAR           FileName[1];    //文件名
} OPEN_FILE_INFORMATION, *POPEN_FILE_INFORMATION;
这个结构表明了镜像文件的几个必要信息。(查询信息操作就是返回这个结构.)

2.定义了几个宏:
复制内容到剪贴板
代码:
#define DEVICE_BASE_NAME    _T("//FileDisk")
#define DEVICE_DIR_NAME     _T("//Device")      DEVICE_BASE_NAME
#define DEVICE_NAME_PREFIX  DEVICE_DIR_NAME     DEVICE_BASE_NAME
实际上就是:
复制内容到剪贴板
代码:
#define DEVICE_BASE_NAME    _T("//FileDisk")
#define DEVICE_DIR_NAME     _T("//Device//FileDisk")
#define DEVICE_NAME_PREFIX  _T("//Device//FileDisk//FileDisk")
3.3个IO控制码.
复制内容到剪贴板
代码:
#define FILE_DEVICE_FILE_DISK       0x8000
#define IOCTL_FILE_DISK_OPEN_FILE   CTL_CODE(FILE_DEVICE_FILE_DISK, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_FILE_DISK_CLOSE_FILE  CTL_CODE(FILE_DEVICE_FILE_DISK, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_FILE_DISK_QUERY_FILE  CTL_CODE(FILE_DEVICE_FILE_DISK, 0x802, METHOD_BUFFERED, FILE_READ_ACCESS)
其中CTL_CODE就又是一个宏:
复制内容到剪贴板
代码:
#define CTL_CODE( DeviceType, Function, Method, Access ) ( ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) )
通过这个CTL_CODE得到的一是一个32位的IO控制码。这个码其实包含了IO操作的几个属性。
 

应用层filedisk.c

1.FileDiskSyntax(void)函数表明了我们应该如何使用FileDisk./mount /umount /status 三个参数分别代表用户可能用到的三个功能.这从字面意思就可以理解。

2.main(int argc, char* argv[])函数所做的事情:
从命令行取出以下参数。
A.(每次运行时,)初始化一个OpenFileInformation结构的各字段,在计算FileSize字段时考虑得比较周到,支持"G/M/k"等容量单位.这里我们可以对代码作相关修改,以让程序能适应与大小无关的参数.
B.CdImage、DriveLetter变量分别表示是否作为一个光盘挂载,和挂载之后期望的盘符。
C.DeviceNumber我暂时的理解是设备的编号.
如果参数无误,便调用:
FileDiskMount(DeviceNumber, OpenFileInformation, DriveLetter, CdImage);
FileDiskUmount(DriveLetter);
FileDiskStatus(DriveLetter);
反之,如参数有误,便显示FileDisk的用法.

3.FileDiskMount(DeviceNumber, OpenFileInformation, DriveLetter, CdImage);
从函数名字上看,这个函数在做什么事,就很明显,细看一下发现:
以 OPEN_EXISTING 标记打开盘符对应的卷,如果打开了,说明已经盘符已经被占用,就显示出错信息并退出.
如果该盘符未被占用,就创建一个符号连接,并再次打开卷,然后向该卷设备发送 IOCTL_FILE_DISK_OPEN_FILE IO控制码,同时传递 OpenFileInformation 结构给设备.
这里我觉得值得一提的是删除符号连接的方法:
DefineDosDevice(DDD_REMOVE_DEFINITION, &VolumeName[4], NULL);
DefineDosDevice这个函数还可以删除符号连接!(DefineDosDevice这个函数是供用户模式程序调用的,在内核模式则应该用IoCreateSymbolicLink函数.)


4.FileDiskUmount(char DriveLetter);
发现主要有以下动作:
打开盘符所对应的卷设备
向卷设备发 FSCTL_LOCK_VOLUME 命令,锁定该卷(可能是让该卷设备不再接受其余的命令)。
向卷设备发 IOCTL_FILE_DISK_CLOSE_FILE 命令。
向卷设备发 FSCTL_DISMOUNT_VOLUME 命令。
向卷设备发 FSCTL_UNLOCK_VOLUME 命令.
DefineDosDevice (带参数DDD_REMOVE_DEFINITION) 删除对应的符号链接。

5.FileDiskStatus(char DriveLetter);
就是向卷设备发 IOCTL_FILE_DISK_QUERY_FILE 命令,驱动应返回一个 OPEN_FILE_INFORMATION 结构.把这里的结构里的信息打印出来就OK了.
 

内核层filedisk.c

1.NTSTATUS  DriverEntry (IN PDRIVER_OBJECT   DriverObject,IN PUNICODE_STRING  RegistryPath)函数

从注册表中得到 设备的数量 并放到n_devices中.
因为期望的设备名是_T("//Device//FileDisk//FileDiskCdX")或_T("//Device//FileDisk//FileDiskX").
当设备目录_T("//Device//FileDisk")不存在的时候,创建_T("//Device//FileDisk//FileDiskCdX")设备会失败.所以应该先创建这个"对象目录".
创建对象目录对象,然后调用 FileDiskCreateDevice 创建 n_devices 个FILE_DEVICE_DISK 和 n_devices 个FILE_DEVICE_CDROM.显然 FileDiskCreateDevice 很值得一看.
最后,指定几个派谴例程和DriverUnload例程.

2.NTSTATUS FileDiskCreateDevice (IN PDRIVER_OBJECT DriverObject,IN ULONG Number,IN DEVICE_TYPE DeviceType)函数

调用IoCreateDevice创建一个设备名为device_name[_T("//Device//FileDisk//FileDiskCdX")或_T("//Device//FileDisk//FileDiskX")],设备类型为FILE_DEVICE_DISK/FILE_DEVICE_CD_ROM的设备.
指定device_object所指设备对象的Flags字段(一组标志位)值为DO_DIRECT_IO,即读写操作使用直接方式(内存描述符表)访问用户模式数据.
指定device_object设备扩展的media_in_device字段为FALSE,即媒体文件(即前面所说的镜像文件)还没有打开.
如果设备类型是FILE_DEVICE_CD_ROM就再设定Characteristics标志位(描述设备的可选特征)为只读设备,并设定设备扩展中的相应字段(read_only)的值为TRUE.
初始化链表头(设备扩展中的).并调用KeInitializeSpinLock初始化一个自旋锁.(方便以后,当代码运行在低于或等于DISPATCH_LEVEL级上时获取这个锁,并执行需要保护的代码,最后释放自旋锁.)在这里这个链表是用于存放IRP列表.
初始化一个内核事件为非信号态.(后面创建的系统线程运行的第一件事就是等这个内核事件变成信号态).
初始化设备扩展中的terminate_thread字段值为FALSE,(后面创建的系统线程运行时将检查这个字段,以决定是否退出线程).
调用PsCreateSystemThread创建系统线程FileDiskThread(device_object);
调用用ObReferenceObjectByHandle就是在权限可以的情况下,由系统线程对象句柄得到系统线程对象的指针.
如果不能得到系统线程指针将让其退出.并删除创建的设备.
关闭系统线程句柄.并不是终止系统线程的运行.

3.FileDiskUnload (IN PDRIVER_OBJECT DriverObject)调用FileDiskDeleteDevice(device_object)删除所有设备对象.

4.PDEVICE_OBJECT FileDiskDeleteDevice (IN PDEVICE_OBJECT DeviceObject);

让当前的设备对象对应的系统线程退出,等待线程关闭.
删除设备扩展对象中的对应的线程引用.
如果device_extension->security_client_context不为空,减少引用计数,并释放内存.
删除当前的设备对象.
返回链表中的下一个设备对象.
 
5.FileDiskCreateClose (IN PDEVICE_OBJECT   DeviceObject,IN PIRP Irp)
设定IoStatus的值,并完成该IRP

6.FileDiskReadWrite ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
如果没有媒体文件就完成该IRP,并返回STATUS_NO_MEDIA_IN_DEVICE.
如果读0字节的数据,就完成该IRP.
否则,将该IRP插入队列,并返回STATUS_PENDING.

7.FileDiskDeviceControl (IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)
如果还没有打开文件,并且IoControlCode不为 IOCTL_FILE_DISK_OPEN_FILE
完成该Irp 并且 返回状态 STATUS_NO_MEDIA_IN_DEVICE。

否则IoControlCode的处理情况就是:
A.IOCTL_FILE_DISK_OPEN_FILE: //打开文件  应用 /mount 命令发送这个Irp
    如果媒体文件已经打开了,status 赋值为 STATUS_INVALID_DEVICE_REQUEST.
    检验是否传进来的缓冲区大小,如果太少了,status 就赋值为 STATUS_INVALID_PARAMETER.
    然后模拟一个客户环境,并放到device_extension中.
    把IRP放入队列,status 赋值为 STATUS_PENDING.(交给所创建的系统线程处理.)

B.IOCTL_FILE_DISK_CLOSE_FILE:  //关闭文件
    把IRP放入队列,status 赋值为 STATUS_PENDING.(交给所创建的系统线程处理.)


C.以下这几个IoControlCode的处理情况是根据device_extension的信息返回相应状态:
IOCTL_DISK_CHECK_VERIFY
IOCTL_CDROM_CHECK_VERIFY
IOCTL_STORAGE_CHECK_VERIFY
IOCTL_STORAGE_CHECK_VERIFY2
IOCTL_DISK_IS_WRITABLE
IOCTL_DISK_MEDIA_REMOVAL
IOCTL_STORAGE_MEDIA_REMOVAL
IOCTL_DISK_SET_PARTITION_INFO
status值都不是STATUS_PENDING.

D.在返回的缓冲区容量大小足够的情况下,以下这几个IoControlCode的处理是:
IOCTL_FILE_DISK_QUERY_FILE     //查询状态
IOCTL_DISK_GET_DRIVE_GEOMETRY
IOCTL_CDROM_GET_DRIVE_GEOMETRY //得到盘的容量大小
IOCTL_DISK_GET_LENGTH_INFO
IOCTL_DISK_GET_PARTITION_INFO
IOCTL_DISK_GET_PARTITION_INFO_EX
IOCTL_CDROM_READ_TOC
IOCTL_DISK_VERIFY
Irp->AssociatedIrp.SystemBuffer指向要返回结构。并为 status 赋值 STATUS_SUCCESS;
Irp->IoStatus.Information 赋值为返回结构的大小.

E.如果是其他的IoControlCode就返回STATUS_INVALID_DEVICE_REQUEST;
F.如果在以上处理时status不是STATUS_PENDING,就完成该IRP.
 
//创建的系统线程.
8.VOID FileDiskThread (IN PVOID Context)
    将本线程的运行时优先级改为LOW_REALTIME_PRIORITY.
    KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);
    循环开始.
    //等待这个事件&device_extension->request_event变成信号态。
    //如果系统线程应该终止.就终止本线程。
      从IRP队列中取出IRP,判断IRP的主功能号进行处理:
        IRP_MJ_READ://IRP_MJ_READ读操作.
        用MmGetSystemAddressForMdlSafe宏得到irp->MdlAddress所描述的非分页系统空间虚拟地址.(即为system_buffer赋值)
        申请指定大小的分页内存,用ZwReadFile读取相应文件内容,复制所读取的内容到system_buffer. 释放刚才申请的分页内存.

        IRP_MJ_WRITE:
        判断参数正确性.
        将irp->MdlAddress内存用ZwWriteFile写入device_extension->file_handle所代表的文件中.

        IRP_MJ_DEVICE_CONTROL:
           IOCTL_FILE_DISK_OPEN_FILE:
           模仿用户安全环境,交给函数FileDiskOpenFile做具体的操作.
           IOCTL_FILE_DISK_CLOSE_FILE:
           交给函数FileDiskCloseFile做具体的操作.
      完成该IRP.
      从IRP队列中取出下一个IRP继续.
    所有IRP都处理完之后,返回循环开始处.

//关闭文件操作.
9.NTSTATUS FileDiskCloseFile (IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp )
取得设备扩展device_extension,关闭打开句柄,释放申请的内容.设定标志位device_extension->media_in_device.设定返回状态为STATUS_SUCCESS.

//打开文件,并获取文件的信息放入 DeviceObject 及设备扩展结构中.
10.NTSTATUS FileDiskOpenFile ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
        //根据Irp->AssociatedIrp.SystemBuffer所指OPEN_FILE_INFORMATION结构的值
        //设定DeviceObject->DeviceExtension各字段的值.
        //打开文件保存句柄到device_extension->file_handle.
        //如果文件不存在就创建文件.
整合FileDiskFilemon的MFC开源码,希望对新手有帮助! 说明:对大侠们可能没什么价值,仅以此献给像我一样的菜鸟! 软件环境和框架: 1.软件环境:VS.net2003 + XP IFS + DS3.2 2.框架:采用MFC框架; 功能: 1.自动加载FileDiskFilemon的驱动程序部分; 2.用FileDisk生成一个虚拟硬盘,并自动分配盘符; 3.对新生成的虚拟盘符挂接Filemon的过滤驱动; 4.退出时自动卸载FileDiskFilemon驱动; 不完善的地方(有兴趣的朋友可以完善下,并希望完善后请在我这里跟帖上传源码,就算是对我开源的支持,谢谢!): 1.FileDisk生成的虚拟硬盘需要先手动在资源管理器里进行格式化;可以在程序里调用SHFormat之类的启动格式化窗口进行改善,也可以使用第三方提供的格式化函数进行格式化; 2.Filemon目前是应用程序使用按照时间来进行查询的过滤方式,希望能改成驱动程序主动通知应用程序。 ------- 整理后面的回贴后追加的内容 ---------------- 1.在BOOL CMainFrame::Init(void)函数里有段网络通讯的代码,里面访问了我自己建的局域网服务器,大家那里应该是没有的,所以会报错,不好意思!注释或删掉以下内容就好了! CWebWrapper clsWebWrapper; CHttpDataPackage_Receive HttpData_Receive; //Call server function if (clsWebWrapper.PostCommand( _T(""), _T("abcd我你他"), &HttpData_Receive, _T("WebGrab"), this, FALSE, FALSE)) //if (clsWebWrapper.GetCommand( _T(""), _T("abcd我你他"), &HttpData_Receive, // _T("WebGrab"), this, FALSE, FALSE)) { AfxMessageBox(HttpData_Receive.GetContent_DataAll()); } else { AfxMessageBox("网络通讯失败"); return FALSE; } 2.大家如果想看文件过滤出来的信息,可以在BOOL CMainFrame::Init(void)函数里去掉对这句话//m_nTimer = SetTimer( 1, 500/*ms*/, NULL );的注释,那就相当于启动了定时器,然后会有信息显示在子窗口里,,当然我并没有去实现子窗口的滚动,所以子窗口里的内容满了后,就看不到最新的信息了,,哈哈,,我实在是没时间啊,,公司里的项目这2天在出演示版呢,,,忙啊。。。。大家自己加加代码哈,,,这也是个不完善的地方,希望完善了的朋友,,上传下代码啊,,,多谢! 3.另外,请下载使用了的朋友,有空的话来帮忙说下使用上有没有问题啊,如果有问题,我好及时做修改啊,谢谢了!如果可以用的话,有空就帮忙回个贴,说可以用,这样别的朋友就知道这个代码是可以用的,免得大家还以为下了个不能用的东东,那不是郁闷死了!先谢谢了! ------------- 最新补充说明 ------------------- 1.FileDiskFilemon我用的都是比较老的版本了,FileDisk是2004年的,Filemon是4.34 ;FileDisk的作者在其网站上已经出了新版本了,是2006年的,我下载了,还没时间看过,所以大家有空也可以整合2006的驱动来替换我的老版本,毕竟老版本是可能有些不完善的地方的;而Filemon则没有再继续开放源码,,所以如果有朋友有幸获得了新版本的源码,能不能上传个,,多谢了! 2.Filemon.exe会自动在其目录下创建File.img文件做为虚拟磁盘使用的文件,大家只要手动格式化一次创建出来的虚拟磁盘,以后就不用再格式化,可以不断使用了,所以以后大家要是做成产品的话,可以直接把已经格式化好的对应的File.img文件一起打包到安装包里,呵呵! 3.我放弃了FileDisk中对CDROM部分的支持,以及Filemon中对Win9x的VxD驱动的支持,如果有朋友需要的话,可以自行添加!
转自看雪的CCDeath 【文章标题】: WinMount虚拟磁盘深入研究(-)之filedisk源代码详细分析 【下载地址】: 自己搜索下载 【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教! -------------------------------------------------------------------------------- 【详细过程】 我的驱动入门三终结版,还再学习中。由于个人也有些事情要处理,研究起刘涛涛WinMount的虚拟 磁盘,而且这方面的书籍,貌似乎没见过,只有傻傻的几K代码存在,没注释什么的,整体框架也没说。 虚拟光驱用实现文件来模拟磁盘的原理,是文件系统驱动程序。 把filedisk驱动安装,查看install.txt文件。 1.Copy the driver (filedisk.sys) to %systemroot%\system32\drivers\. 2.Import filedisk.reg to the Registry. 3.Reboot. 4.Use the program filedisk.exe to mount/umount files, for an example of use see the file example.txt. 可以不用重起机子的方法,找一个动载加载驱动工具:DriverMonitor不错了。然后在“开始菜单”-> "运行"输入 "net start filedisk" 出现:“ 请求的服务已经启动”。这个必须得成功才行哦。 接下来注意点。cmd后 出现这个目录 C:\Documents and Settings \Administrator>,在接下来敲入 filedisk /mount 0 c:\temp\filedisk.img 8M f: C:\Documents and Settings \Administrator>filedisk /mount 0 c:\temp\filedisk.img 8M r: 回车一下。 出现"FileDisk:系统找不到指定路径" 。原因就出在这 "c:\temp\filedisk.img" 中的C:\temp要这个目录才行。至于 filedisk.img不是必须,会自动创建。 如果有出现"FileDisk:函数不正确" 中的“filedisk /mount 0 ”中"0"代号已经被使用。可以改为"1". 查看一下,结果就出现一个还未格式化 8M R磁盘,查看C:\temp下生成一个filedisk.img也8M。想卸载 掉"filedisk /umount r:". 还可以创建很大的虚拟磁盘,你把"8M"改换其他的就是了。 以上如果都没出现结果,基本上就没兴趣继续研究下去了,我看到了很多人初学filedisk都遇到以上这 些问题(包括我在内) ,把我折腾了半天。 先来分析驱动层代码,后来分析应用层代码。更详细的请看附件里的源代码。我对代码工程方式重新布 局,用起来更方便。 ===================================//先来分析驱动层代码 1.对filedisk.h进行分析 #define FILE_DEVICE_FILE_DISK 0x8000//用户定义范围0x8000~ #define IOCTL_FILE_DISK_OPEN_FILE CTL_CODE(FILE_DEVICE_FILE_DISK, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_FILE_DISK_CLOSE_FILE CTL_CODE(FILE_DEVICE_FILE_DISK, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_FILE_DISK_QUERY_FILE CTL_CODE(FILE_DEVICE_FILE_DISK, 0x802, METHOD_BUFFERED, FILE_READ_ACCESS) typedef struct _OPEN_FILE_INFORMATION { LARGE_INTEGER FileSize;//文件大小 BOOLEAN ReadOnly;//只读属性 USHORT Fil
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值