阅读:33879回复:78
共享我写的一个USB过滤驱动,实现U盘只读控制
看到很多人都把自己的东西拿出来共享,我也来写一点。
功能: 这是一个简单的USB过滤驱动,采用标准的WDM过滤,以DDK中的filter为原形。实现了U盘的只读控制。 说明: 1 在整个编写过程中,受到tiamo等众多高手的帮助,感激不尽。还要向他们多多学习。 2 这是我写的具有具体功能的第一个驱动,高兴。 3 本人是一位就读于排名在300名以外的大学的大四学生,水平有限,有错误的地方请各位指出。见笑了。 开发中使用的工具和环境: vc 6.0+2000ddk 用DbgViewer查看信息。 用DeviceTree查看驱动加载情况 正文: 我从开始看驱动就认为,一个完整的驱动是改出来的。 因此我修改了ddk中toaster/filter程序。这个程序写的思路很清晰,把一个标准的过滤驱动程序应该有的各个模块都写了出来。特 别是很完整的做了PNP的相关操作。 作为一个标准过滤驱动,应该在AddDevice中将创建的设备IoAttachDeviceToDeviceStack到欲过滤的设备上。而这个设备就是 AddDevice的第二个参数。这里要说明的是,我过滤的设备对象是usbstor.sys,是一个FlowerFilters。 在注册表中加入相应的项(这个 过程应该是INF做的,我不会做,因此直接加的),系统在加载usbstor.sys这个驱动的时候,根据注册表中FlowerFilters对应的值就会 自动去加载你的驱动,非常的方便。 系统加载你的设备以后就进入了AddDevice,此时你可以IoAttachDeviceToDeviceStack,填写你的PDEVICE_EXTENSION等等。 这样完成以后,你的设备就可以在DeviceTree中看到,位置在USBSTOR打开以后,展开可以看到。 剩下的不用我说,大家也知道。就是拦截相应的IRP了。对应不同的过滤功能,需要拦截的IRP也不同,以我这为例子,我想拦截 所有的写操作,就是使U盘成为只读的。开始我去拦截IRP_MJ_WRITE,跟踪发现,写U盘的时候根本不走这个地方。询问后知道要 拦截相应的scsi命令,关于SCSI命令有相关的规范去看。对SCSI命令的取得和操作大致如下: 1 得到当前的SCSI命令: DbgPrint(\"IRP_MJ_SCSI\"); irpStack = IoGetCurrentIrpStackLocation(irp); CurSrb=irpStack->Parameters.Scsi.Srb; cdb=CurSrb->Cdb; opCode=cdb->CDB6GENERIC.OperationCode; 现在opCode中就是当前的SCSI命令。 2 分析SCSI命令(由于SCSI命令很多,找到真正要拦截的SCSI命令,还需要参看实例) if(opCode==SCSIOP_MODE_SENSE)//一个mode sense结构 { } if(opCode==SCSIOP_WRITE||opCode==SCSIOP_WRITE6)//写操作 { } 其他的参看SCSI规范 当我拦截到SCSI命令以后,认为一切边的顺利起来。但是实际上,我在只拦截SCSIOP_WRITE的时候,虽然系统不会真正的写东西到 U盘上,但却要过很久才会提示延时写错误。这样的程序显然是不能给用户的。这个时候tiamo告诉我一个方法,这个方法来源于市场 上的一种“带写保护功能”的U盘,他可以象软盘一样的写保护。当然他是硬件上实现的。软件实现更加的简单,如下: irpStack = IoGetCurrentIrpStackLocation(Irp); CurSrb=irpStack->Parameters.Scsi.Srb;//Get Current Scsi SRB, Analysis SCSI Command here! cdb=CurSrb->Cdb; opCode=cdb->CDB6GENERIC.OperationCode; if(opCode==SCSIOP_MODE_SENSE && CurSrb->DataBuffer && CurSrb->DataTransferLength >= sizeof(MODE_PARAMETER_HEADER) { modeData = (PMODE_PARAMETER_HEADER)CurSrb->DataBuffer; modeData->DeviceSpecificParameter|=MODE_DSP_WRITE_PROTECT; } 具体就是modeData->DeviceSpecificParameter|=MODE_DSP_WRITE_PROTECT;这句话了。 写到这里,U盘的只读功能就完成了。效果很好。 写这个文章的时候我正在完善这个程序,希望用一个应用层的程序和他配合。可以控制U盘只读、读写、全锁定等等。遇到一些大问题。 当我完成那个功能的时候,再把这些详细的东西加上。 献丑了。。 |
|
沙发#
发布于:2018-09-04 18:57
用户被禁言,该主题自动屏蔽! |
|
板凳#
发布于:2015-07-17 18:47
注意,以上代码是用户态的,但是核心思想就是发送irp下去
|
|
|
地板#
发布于:2015-07-17 18:47
#pragma once #include <tchar.h> #include <winioctl.h> /* * USB 输助函数 * Author: M.Y * Version: 1.0 * Revision: 1 */ typedef enum _Sg_disk_type { NONE_TYPE=0, IDE_DISK, USB_DISK, F1394_DISK, // 1394总线盘 } SG_DISK_TYPE; // SetupDiGetInterfaceDeviceDetail所需要的输出长度 #define INTERFACE_DETAIL_SIZE (1024) // IOCTL_STORAGE_GET_MEDIA_TYPES_EX可能返回不止一条DEVICE_MEDIA_INFO,故定义足够的空间 #define MEDIA_INFO_SIZE (sizeof(GET_MEDIA_TYPES) + sizeof(DEVICE_MEDIA_INFO) * 15) #define IOCTL_STORAGE_QUERY_PROPERTY CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) // 查询存储设备属性的类型 typedef enum _STORAGE_QUERY_TYPE { PropertyStandardQuery = 0, // 读取描述 PropertyExistsQuery, // 测试是否支持 PropertyMaskQuery, // 读取指定的描述 PropertyQueryMaxDefined // 验证数据 } STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE; // 查询存储设备还是适配器属性 typedef enum _STORAGE_PROPERTY_ID { StorageDeviceProperty = 0, // 查询设备属性 StorageAdapterProperty // 查询适配器属性 } STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID; // 查询属性输入的数据结构 typedef struct _STORAGE_PROPERTY_QUERY { STORAGE_PROPERTY_ID PropertyId; // 设备/适配器 STORAGE_QUERY_TYPE QueryType; // 查询类型 UCHAR AdditionalParameters[1]; // 额外的数据(仅定义了象征性的1个字节) } STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY; // 查询属性输出的数据结构 typedef struct _STORAGE_DEVICE_DESCRIPTOR { ULONG Version; // 版本 ULONG Size; // 结构大小 UCHAR DeviceType; // 设备类型 UCHAR DeviceTypeModifier; // SCSI-2额外的设备类型 BOOLEAN RemovableMedia; // 是否可移动 BOOLEAN CommandQueueing; // 是否支持命令队列 ULONG VendorIdOffset; // 厂家设定值的偏移 ULONG ProductIdOffset; // 产品ID的偏移 ULONG ProductRevisionOffset; // 产品版本的偏移 ULONG SerialNumberOffset; // 序列号的偏移 STORAGE_BUS_TYPE BusType; // 总线类型 ULONG RawPropertiesLength; // 额外的属性数据长度 UCHAR RawDeviceProperties[1]; // 额外的属性数据(仅定义了象征性的1个字节) } STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR; class SgUSB { public: static SG_DISK_TYPE GetDriveType(TCHAR chDrv) { TCHAR szDeviceName[7]=_T("\\\\?\\x:"); SG_DISK_TYPE disktype; PSTORAGE_DEVICE_DESCRIPTOR pDevDesc; pDevDesc = (PSTORAGE_DEVICE_DESCRIPTOR)new BYTE[sizeof(STORAGE_DEVICE_DESCRIPTOR) + 512 - 1]; pDevDesc->Size = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 512 - 1; szDeviceName[4]=chDrv; HANDLE hHandle=OpenDevice(szDeviceName); if( hHandle == INVALID_HANDLE_VALUE ) return NONE_TYPE; if(GetDriveProperty(hHandle,pDevDesc) ) { switch(pDevDesc->BusType ) { case BusType1394: disktype = F1394_DISK; break; case BusTypeUsb: disktype =USB_DISK; break; default: disktype= NONE_TYPE; } } else disktype= NONE_TYPE; delete []pDevDesc; CloseHandle(hHandle); return disktype; } static HANDLE OpenDevice(LPCTSTR pszDevicePath) { HANDLE hDevice; // 打开设备 hDevice= ::CreateFile(pszDevicePath, // 设备路径 GENERIC_READ | GENERIC_WRITE, // 读写方式 FILE_SHARE_READ | FILE_SHARE_WRITE, // 共享方式 NULL, // 默认的安全描述符 OPEN_EXISTING, // 创建方式 0, // 不需设置文件属性 NULL); // 不需参照模板文件 return hDevice; } static BOOL GetDriveGeometry(HANDLE hDevice, PDISK_GEOMETRY pGeometry) { PGET_MEDIA_TYPES pMediaTypes; // 内部用的输出缓冲区 DWORD dwOutBytes; // 输出数据长度 BOOL bResult; // DeviceIoControl的返回结果 // 申请内部用的输出缓冲区 pMediaTypes = (PGET_MEDIA_TYPES)::GlobalAlloc(LMEM_ZEROINIT, MEDIA_INFO_SIZE); // 用IOCTL_STORAGE_GET_MEDIA_TYPES_EX取介质类型参数 bResult = ::DeviceIoControl(hDevice, // 设备句柄 IOCTL_STORAGE_GET_MEDIA_TYPES_EX, // 取介质类型参数 NULL, 0, // 不需要输入数据 pMediaTypes, MEDIA_INFO_SIZE, // 输出数据缓冲区 &dwOutBytes, // 输出数据长度 (LPOVERLAPPED)NULL); // 用同步I/O if(bResult) { // 注意到结构DEVICE_MEDIA_INFO是在结构DISK_GEOMETRY的基础上扩充的 // 为简化程序,用memcpy代替如下多条赋值语句: // pGeometry->MediaType = (MEDIA_TYPE)pMediaTypes->MediaInfo[0].DeviceSpecific.DiskInfo.MediaType; // pGeometry->Cylinders = pMediaTypes->MediaInfo[0].DeviceSpecific.DiskInfo.Cylinders; // pGeometry->TracksPerCylinder = pMediaTypes->MediaInfo[0].DeviceSpecific.DiskInfo.TracksPerCylinder; // ... ... ::memcpy(pGeometry, pMediaTypes->MediaInfo, sizeof(DISK_GEOMETRY)); } // 释放内部缓冲区 ::GlobalFree(pMediaTypes); return bResult; } static BOOL GetDriveProperty(HANDLE hDevice, PSTORAGE_DEVICE_DESCRIPTOR pDevDesc) { STORAGE_PROPERTY_QUERY Query; // 查询输入参数 DWORD dwOutBytes; // IOCTL输出数据长度 BOOL bResult; // IOCTL返回值 // 指定查询方式 Query.PropertyId = StorageDeviceProperty; Query.QueryType = PropertyStandardQuery; // 用IOCTL_STORAGE_QUERY_PROPERTY取设备属性信息 bResult = ::DeviceIoControl(hDevice, // 设备句柄 IOCTL_STORAGE_QUERY_PROPERTY, // 取设备属性信息 &Query, sizeof(STORAGE_PROPERTY_QUERY), // 输入数据缓冲区 pDevDesc, pDevDesc->Size, // 输出数据缓冲区 &dwOutBytes, // 输出数据长度 (LPOVERLAPPED)NULL); // 用同步I/O return bResult; } }; |
|
|
地下室#
发布于:2015-07-17 18:40
对于u盘只读控制,还有一个办法,就是在文件系统中文件打开或写的时候检查一下卷下面的总线类型,如果是usb或1394等可热插拨的,就进行禁止操作。
关于检查总线类型,可以向下面发送irp解决: |
|
|
5楼#
发布于:2015-07-07 16:02
我觉得这个帖子基本解决不了问题,关键问题根本跳过部描述
|
|
6楼#
发布于:2015-05-01 18:37
谢谢分享
|
|
7楼#
发布于:2015-04-13 13:27
USB监控难么?
|
|
8楼#
发布于:2014-01-15 12:29
我大二时做了一个U盘过滤,在总线驱动上的。。。不知道扔哪去了
|
|
9楼#
发布于:2013-01-08 21:17
新人报道,求大神指点
|
|
10楼#
发布于:2013-01-08 21:15
新人冒泡。各位大神求指教
|
|
11楼#
发布于:2012-04-16 16:34
英雄不问出处,谢谢了。
|
|
12楼#
发布于:2010-08-17 22:29
特地赶来, 学习下!
|
|
13楼#
发布于:2010-07-14 17:09
好人啊,好人,支持这样的共享精神。
|
|
14楼#
发布于:2010-05-31 09:04
up~~~
|
|
15楼#
发布于:2010-04-13 12:52
代码被删掉了?
|
|
16楼#
发布于:2010-03-12 09:21
我晕. 这两天我也在看这个. 我怎摸拦不住读写U盘的请求. 看网上有文章说拦截IRP_MJ_SCSI, 我拦截也没拦截住.
谁告诉你拦截身摸的? |
|
|
17楼#
发布于:2010-03-11 15:07
最近在做这个,特地赶来看看,不错~ 多谢楼主分享
![]() |
|
18楼#
发布于:2010-02-13 10:54
学习了!
![]() ![]() ![]() ![]() |
|
19楼#
发布于:2010-02-01 15:36
回 楼主(zhjie374) 的帖子
学习,好人 |
|
上一页
下一页