|
阅读:34648回复: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) 的帖子
学习,好人 |
|
上一页
下一页
