USBFiletr 总结

很简单的一个USB过滤驱动,是按照张帆的那本书里最后两个驱动例子写的;主要功能就是使USB存储设备写保护;

当U盘插入计算机时,USB总线会枚举一个PDO,并将一个USBSTOR的驱动作为FDO加载到PDO,USBSTOP会创建一个设备PDO2,上面挂载这DISK.sys驱动,再其上是Partmgr驱动(分区驱动),具体结构如下图:

USB过滤驱动 - 逍遥凌辰 - IT_问天

 

安装驱动的话可以自己写INF文件,我写INF文件的时候遇到了一个问题,驱动是成功安装了,但是不起作用,连DriverEntry都没有进去,仔细查找才发现原来驱动在Service域里的ServiceBinary=%10%\system32\drivers\USBFilter.sys 这个路径写错了,多写了个字母,结果是驱动运行去找这个sys文件的时候发现找不到,就不工作了,所以写INF文件还是要小心;

驱动的加载:USB 存储设备一般每个U盘都会在注册表SYSTEM\CurrentControlSet\Enum\USBStor 中留有记录,每个USB设备都不一样,只要在那台机子上插过就会有,如图:

USB过滤驱动 - 逍遥凌辰 - IT_问天

 只要在希望禁止的U盘设备下,建立LowerFilter,值为USBFiter就行了,当然这样的话只能限制该USB设备为只读,不能限制其他USB设备;曾经尝试过在Class注册表里的磁盘GUID里加入LowerFilter,来限制所有USB,但是需要判断是否是USB存储还是磁盘存储,不能限制磁盘存储,否则系统会起不来的,下面会讲遇到的这个问题;还有一种方法就是在Class中的USB串口控制器里加入这个,这样会导致所有是USB设备的都会被限制到,如键盘,鼠标之类的,所以这个方法不是很好;

现在讲讲代码里的一些问题吧:

DriverEntry基本没什么问题,大多程序都是这么写的;

在AddDevice里因为粗心,出了一个还是很令人头疼的问题的,在我IoAttachDeviceToDeviceStack一个设备之后,再设置本层DEVICE_OBJECT的Flags属性的时候不小心设置成被Attach的那个设备,也就是下一层设备的Flags,导致设备一加载,然后系统就调用Unload函数卸载驱动了;刚开始还莫名其妙不知道错在哪里,这个根本调式不出来到底错哪里了;讲讲Flags设置吧,一般都用被挂载的下一层设备的flags赋值给当前的这层的,

pUSBDeviceObject->Flags |= pfido->Flags&(DO_DIRECT_IO | DO_BUFFERED_IO |DO_POWER_PAGABLE);因为不知道下层的是DO_DIRECT_IO 还是DO_BUFFERED_IO,所以这里两个都做了判断,DO_POWER_PAGEBLE是WDM驱动必须设置的,详情可以看WDK;pUSBDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; 当创建设备的时候,io管理器会设置这个值,表示该设备正常被初始化,一旦在本层设备attach下层设备且设置了power state就可以取消这个值,表示设备已经初始化完了;

接下来要讲的是,磁盘过滤命令;磁盘过滤主要就是对IRP_MJ_SCSI这个IRP的拦截,其实就是 IRP_MJ_INTERNAL_DEVICE_CONTROL的一个别名,在disk.sys和USBSTOR.sys之间传递的是标准的SCSI指令;

对于SCSI命令,都在DDK的srb.h和scsi.h中有定义:

USB过滤驱动 - 逍遥凌辰 - IT_问天

 

如果想变为只读,只要将SCSIOP_WRITE 请求设置为失败就行,所有U盘的写请求最终都会变成SCSIOP_WIRTER ,然后用这种方法的话,系统会提示很久才失败,因为系统在接受到SCSIOP_WRITER失败时候就会不断的尝试再发送该请求的;所以我们一般在SCSIOP_MODE_SENSE 请求时,设置SCSIOP_MODE_SENSE的MODE_DSP_WRITER_PROTECT位,以达到保护效果,简略代码如下:

if(opCode == SCSIOP_MODE_SENSE && pCurSrb->DataBuffer &&

pCurSrb->DataTransferLength>=sizeof(MODE_PARAMETER_HEADER))

{

KdPrint(("SCSIOP_MODE_SENSE COMING!\n"));

PMODE_PARAMETER_HEADER pModeData = (PMODE_PARAMETER_HEADER)pCurSrb->DataBuffer;

pModeData->DeviceSpecificParameter |= MODE_DSP_WRITE_PROTECT;

}

在我们的这层设备层刚开始工作的时候上层会有很多SCSI检测指令下来,SCSIOP_MODE_SENSE也就是在那时候设置的;

挂载磁盘而能正常工作,在网上搜到的是在接收到的SCSI指令之后发送指令给总线判断一下是什么总线,如果是USB总线的话就设置protect位,否则不设置;具体怎么做没有尝试;而是尝试了自己的一种方法,当然先说明结果是失败的,但是也能明白点事儿:

我的想法是每个磁盘除了系统盘都装这层的device,我们的目的主要是验证下保护能否正常工作就行,只要系统盘不被保护,系统启动时临时文件这些都能写入系统盘,系统就不会蓝屏;基于这种考虑,我就在IRP_MN_START_DEVICE 指令处,自己build了一个Control向底层发送一个IOCTL_STORAGE_GET_DEVICE_NUMBER指令:

status = DeviceIoctl(pDevEx->m_LowDeviceObject,

IOCTL_STORAGE_GET_DEVICE_NUMBER,

NULL,

0,

&storageDeviceNum,

sizeof(STORAGE_DEVICE_NUMBER),

&ioStatusBlock);

在STORAGE_DEVICE_NUMBER这个结构体里有一个DeviceNumber的变量是指明磁盘的disk索引号的,我记录下每个磁盘的索引号,当遇到0的时候,就不设置保护位;理论设想是好的,但是实际证明还是会蓝屏,经过调式发现调用IOCTL_STORAGE_GET_DEVICE_NUMBER这个指令返回的却是STATUS_NOT_SUPPORT,这就奇怪了,以前我用的都是好好的,我怀疑是不是被中间的lowerfilters拒绝掉了,我直接发送最底层也是这个结果,后来与同事讨论发现,这个IRP是在Disk.sys支持的,disk.sys处理完之后就直接结束了,不会往下传了,我们的驱动在Disk.sys下层,所以会失败,以前都是用的disk.sys的上层,所以成功;

还遇到一个问题就是在IRP_MN_START_DEVICE 中调用函数发送IOCTL_STORAGE_GET_DEVICE_NUMBER去底层发挥STATUS_NOT_SUPPORT,我不小心直接把这个值当成case里的返回值,倒是系统认为IRP_MN_START_DEVICE这个IRP是STATUS_NOT_SUPPORT而导致一直起不来而蓝屏;

case IRP_MN_START_DEVICE:

KdPrint(("IRP_MN_START_DEVICE\n"));

Status = SynchorousSendPnpIrpDown(pDevEX->m_LowDeviceObject,pIrp);

if(NT_SUCCESS(status))

{

//status = FindDiskIndexpDevEX)原来的

FindDiskIndex(pDevEX);

}

IoReleaseRemoveLock(&pDevEX->m_RemoveLock,NULL);

return status;

总的来说驱动比较简单,关键是对底下的驱动每层作用以及以下IRP的作用不了解所以老出问题,这也是不开源,难找文档的鄙陋之处;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值