41、过滤驱动程序

    过滤驱动程序可以修改已有驱动的功能,也可以对数据进行过滤加密。WDM驱动需要通过注册表记录指定加裁的过滤驱动,OS会读取这些值完成加载,其可以是高层过滤,也可以是低层过滤。而NT较为灵活,不用设置注册表,直接在内存中寻找设备对象,然后自行创建过滤驱动并将自己附加在这个驱动之上。

    过滤驱动的入口函数需要将所有的IRP都设置派遣例程,因为过滤驱动要保证所有上层传递下来的IRP都能够接收到。如果想修改某个IRP的处理,例如键盘读取输入的键盘扫描码,可以在派遣例程中设置完成例程,在完成例程中修改IRP的处理。

    一般,将介于FDO与PDO间的过滤驱动程序称为低层过滤驱动程序,而将附加在FDO上面的称为高层过滤驱动程序。

1、WDM式过滤示例

    文件过滤驱动,它将挂载在磁盘驱动上,它将所有发往磁盘驱动的IRP全部拦截。一般都是将上层驱动程序传进来的IRP进行拦截并不做处理,而转发给底层驱动程序,称为Pass-Through。特殊IRP,特殊处理。将需要过滤的操作放在完成例程中。

wps_clip_image-8752

图示 过滤驱动程序 P507

代码
    
      
typedef struct tagDEVICE_EXTENSION {
PDEVICE_OBJECT DeviceObject;
// device object this extension belongs to
PDEVICE_OBJECT LowerDeviceObject; // next lower driver in same stack
PDEVICE_OBJECT Pdo; // the PDO
IO_REMOVE_LOCK RemoveLock;
} DEVICE_EXTENSION,
* PDEVICE_EXTENSION;


#pragma INITCODE
extern " C " NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)
{
// DriverEntry
KdPrint((DRIVERNAME " - Entering DriverEntry: DriverObject %8.8lX\n " , DriverObject));
// Initialize function pointers
DriverObject -> DriverUnload = DriverUnload;
DriverObject
-> DriverExtension -> AddDevice = AddDevice;
for ( int i = 0 ; i < arraysize(DriverObject -> MajorFunction); ++ i)
DriverObject
-> MajorFunction[i] = DispatchAny;
DriverObject
-> MajorFunction[IRP_MJ_POWER] = DispatchPower;
DriverObject
-> MajorFunction[IRP_MJ_PNP] = DispatchPnp;
DriverObject
-> MajorFunction[IRP_MJ_SCSI] = DispatchForSCSI;
return STATUS_SUCCESS;
}
// DriverEntry


////////////////////////////////////////////////////////////////////////////// /
#pragma PAGEDCODE
VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
// DriverUnload
PAGED_CODE();
KdPrint((DRIVERNAME
" - Entering DriverUnload: DriverObject %8.8lX\n " , DriverObject));
}
// DriverUnload

////////////////////////////////////////////////////////////////////////////// /

NTSTATUS AddDevice(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT pdo)
{
// AddDevice
PAGED_CODE();
NTSTATUS status;

PDEVICE_OBJECT fido;
status
= IoCreateDevice(DriverObject, sizeof (DEVICE_EXTENSION), NULL,
GetDeviceTypeToUse(pdo),
0 , FALSE, & fido);
if ( ! NT_SUCCESS(status))
{
// can't create device object
KdPrint((DRIVERNAME " - IoCreateDevice failed - %X\n " , status));
return status;
}
// can't create device object
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fido -> DeviceExtension;

do
{
// finish initialization
IoInitializeRemoveLock( & pdx -> RemoveLock, 0 , 0 , 0 );
pdx
-> DeviceObject = fido;
pdx
-> Pdo = pdo;
// 将过滤驱动附加在底层驱动之上
PDEVICE_OBJECT fdo = IoAttachDeviceToDeviceStack(fido, pdo);
if ( ! fdo)
{
// can't attach
KdPrint((DRIVERNAME " - IoAttachDeviceToDeviceStack failed\n " ));
status
= STATUS_DEVICE_REMOVED;
break ;
}
// can't attach
// 记录底层驱动
pdx -> LowerDeviceObject = fdo;
// 由于不知道底层驱动是直接IO还是BufferIO,因此将标志都置上
fido -> Flags |= fdo -> Flags & (DO_DIRECT_IO | DO_BUFFERED_IO | DO_POWER_PAGABLE);
// Clear the "initializing" flag so that we can get IRPs
fido -> Flags &= ~ DO_DEVICE_INITIALIZING;
}
while (FALSE); // finish initialization

if ( ! NT_SUCCESS(status))
{
// need to cleanup
if (pdx -> LowerDeviceObject)
IoDetachDevice(pdx
-> LowerDeviceObject);
IoDeleteDevice(fido);
}
// need to cleanup

return status;
}
// AddDevice


////////////////////////////////////////////////////////////////////////////// /
#pragma LOCKEDCODE
NTSTATUS CompleteRequest(IN PIRP Irp, IN NTSTATUS status, IN ULONG_PTR info)
{
// CompleteRequest
Irp -> IoStatus.Status = status;
Irp
-> IoStatus.Information = info;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
// CompleteRequest

NTSTATUS
USBSCSICompletion( IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context )
{
PDEVICE_EXTENSION pdx
= ( PDEVICE_EXTENSION )
DeviceObject
-> DeviceExtension;

IoAcquireRemoveLock(
& pdx -> RemoveLock,Irp);

PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation( Irp );

PSCSI_REQUEST_BLOCK CurSrb
= irpStack -> Parameters.Scsi.Srb;
PCDB cdb
= (PCDB)CurSrb -> Cdb;
UCHAR opCode
= cdb -> CDB6GENERIC.OperationCode;

if (opCode == SCSIOP_MODE_SENSE && CurSrb -> DataBuffer
&& CurSrb -> DataTransferLength >=
sizeof (MODE_PARAMETER_HEADER))
{
KdPrint((
" SCSIOP_MODE_SENSE comming!\n " ));

PMODE_PARAMETER_HEADER modeData
= (PMODE_PARAMETER_HEADER)CurSrb -> DataBuffer;

modeData
-> DeviceSpecificParameter |= MODE_DSP_WRITE_PROTECT;
}

if ( Irp -> PendingReturned )
{
IoMarkIrpPending( Irp );
}

IoReleaseRemoveLock(
& pdx -> RemoveLock,Irp);

return Irp -> IoStatus.Status ;
}

#pragma LOCKEDCODE
NTSTATUS DispatchForSCSI(IN PDEVICE_OBJECT fido, IN PIRP Irp)
{
// KdPrint((DRIVERNAME " - Enter DispatchForSCSI \n"));

PDEVICE_EXTENSION pdx
= (PDEVICE_EXTENSION) fido -> DeviceExtension;

PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp);

// Pass request down without additional processing
NTSTATUS status;
status
= IoAcquireRemoveLock( & pdx -> RemoveLock, Irp);
if ( ! NT_SUCCESS(status))
return CompleteRequest(Irp, status, 0 );

IoCopyCurrentIrpStackLocationToNext(Irp);

IoSetCompletionRoutine( Irp,
USBSCSICompletion,
NULL,
TRUE,
TRUE,
TRUE );
status
= IoCallDriver(pdx -> LowerDeviceObject, Irp);
IoReleaseRemoveLock(
& pdx -> RemoveLock, Irp);
return status;
}
////////////////////////////////////////////////////////////////////////////// /
#pragma LOCKEDCODE // make no assumptions about pageability of dispatch fcns
NTSTATUS DispatchAny(IN PDEVICE_OBJECT fido, IN PIRP Irp)
{
// DispatchAny
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fido -> DeviceExtension;
PIO_STACK_LOCATION stack
= IoGetCurrentIrpStackLocation(Irp);
#if DBG
static char * irpname[] =
{
" IRP_MJ_CREATE " ,
" IRP_MJ_CREATE_NAMED_PIPE " ,
" IRP_MJ_CLOSE " ,
" IRP_MJ_READ " ,
" IRP_MJ_WRITE " ,
" IRP_MJ_QUERY_INFORMATION " ,
" IRP_MJ_SET_INFORMATION " ,
" IRP_MJ_QUERY_EA " ,
" IRP_MJ_SET_EA " ,
" IRP_MJ_FLUSH_BUFFERS " ,
" IRP_MJ_QUERY_VOLUME_INFORMATION " ,
" IRP_MJ_SET_VOLUME_INFORMATION " ,
" IRP_MJ_DIRECTORY_CONTROL " ,
" IRP_MJ_FILE_SYSTEM_CONTROL " ,
" IRP_MJ_DEVICE_CONTROL " ,
" IRP_MJ_INTERNAL_DEVICE_CONTROL " ,
" IRP_MJ_SHUTDOWN " ,
" IRP_MJ_LOCK_CONTROL " ,
" IRP_MJ_CLEANUP " ,
" IRP_MJ_CREATE_MAILSLOT " ,
" IRP_MJ_QUERY_SECURITY " ,
" IRP_MJ_SET_SECURITY " ,
" IRP_MJ_POWER " ,
" IRP_MJ_SYSTEM_CONTROL " ,
" IRP_MJ_DEVICE_CHANGE " ,
" IRP_MJ_QUERY_QUOTA " ,
" IRP_MJ_SET_QUOTA " ,
" IRP_MJ_PNP " ,
};

UCHAR type
= stack -> MajorFunction;
// if (type >= arraysize(irpname))
// KdPrint((DRIVERNAME " - Unknown IRP, major type %X\n", type));
// else
// KdPrint((DRIVERNAME " - %s\n", irpname[type]));

#endif

// Pass request down without additional processing
NTSTATUS status;
status
= IoAcquireRemoveLock( & pdx -> RemoveLock, Irp);
if ( ! NT_SUCCESS(status))
return CompleteRequest(Irp, status, 0 );
IoSkipCurrentIrpStackLocation(Irp);
status
= IoCallDriver(pdx -> LowerDeviceObject, Irp);
IoReleaseRemoveLock(
& pdx -> RemoveLock, Irp);
return status;
}
// DispatchAny


////////////////////////////////////////////////////////////////////////////// /
NTSTATUS DispatchPower(IN PDEVICE_OBJECT fido, IN PIRP Irp)
{
// DispatchPower
#if DBG
PIO_STACK_LOCATION stack
= IoGetCurrentIrpStackLocation(Irp);
ULONG fcn
= stack -> MinorFunction;
static char * fcnname[] =
{
" IRP_MN_WAIT_WAKE " ,
" IRP_MN_POWER_SEQUENCE " ,
" IRP_MN_SET_POWER " ,
" IRP_MN_QUERY_POWER " ,
};

if (fcn == IRP_MN_SET_POWER || fcn == IRP_MN_QUERY_POWER)
{
static char * sysstate[] =
{
" PowerSystemUnspecified " ,
" PowerSystemWorking " ,
" PowerSystemSleeping1 " ,
" PowerSystemSleeping2 " ,
" PowerSystemSleeping3 " ,
" PowerSystemHibernate " ,
" PowerSystemShutdown " ,
" PowerSystemMaximum " ,
};

static char * devstate[] =
{
" PowerDeviceUnspecified " ,
" PowerDeviceD0 " ,
" PowerDeviceD1 " ,
" PowerDeviceD2 " ,
" PowerDeviceD3 " ,
" PowerDeviceMaximum " ,
};

ULONG context
= stack -> Parameters.Power.SystemContext;
POWER_STATE_TYPE type
= stack -> Parameters.Power.Type;
KdPrint((DRIVERNAME
" - IRP_MJ_POWER (%s) " , fcnname[fcn]));
if (type == SystemPowerState)
KdPrint((
" , SystemPowerState = %s\n " , sysstate[stack -> Parameters.Power.State.SystemState]));
else
KdPrint((
" , DevicePowerState = %s\n " , devstate[stack -> Parameters.Power.State.DeviceState]));
}
else if (fcn < arraysize(fcnname))
KdPrint((DRIVERNAME
" - IRP_MJ_POWER (%s)\n " , fcnname[fcn]));
else
KdPrint((DRIVERNAME
" - IRP_MJ_POWER (%2.2X)\n " , fcn));
#endif // DBG

PDEVICE_EXTENSION pdx
= (PDEVICE_EXTENSION) fido -> DeviceExtension;
PoStartNextPowerIrp(Irp);
// must be done while we own the IRP
NTSTATUS status;
status
= IoAcquireRemoveLock( & pdx -> RemoveLock, Irp);
if ( ! NT_SUCCESS(status))
return CompleteRequest(Irp, status, 0 );
IoSkipCurrentIrpStackLocation(Irp);
status
= PoCallDriver(pdx -> LowerDeviceObject, Irp);
IoReleaseRemoveLock(
& pdx -> RemoveLock, Irp);
return status;
}
// DispatchPower


////////////////////////////////////////////////////////////////////////////// /
NTSTATUS DispatchPnp(IN PDEVICE_OBJECT fido, IN PIRP Irp)
{
// DispatchPnp
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
ULONG fcn
= stack -> MinorFunction;
NTSTATUS status;
PDEVICE_EXTENSION pdx
= (PDEVICE_EXTENSION) fido -> DeviceExtension;
status
= IoAcquireRemoveLock( & pdx -> RemoveLock, Irp);
if ( ! NT_SUCCESS(status))
return CompleteRequest(Irp, status, 0 );
#if DBG
static char * pnpname[] =
{
" IRP_MN_START_DEVICE " ,
" IRP_MN_QUERY_REMOVE_DEVICE " ,
" IRP_MN_REMOVE_DEVICE " ,
" IRP_MN_CANCEL_REMOVE_DEVICE " ,
" IRP_MN_STOP_DEVICE " ,
" IRP_MN_QUERY_STOP_DEVICE " ,
" IRP_MN_CANCEL_STOP_DEVICE " ,
" IRP_MN_QUERY_DEVICE_RELATIONS " ,
" IRP_MN_QUERY_INTERFACE " ,
" IRP_MN_QUERY_CAPABILITIES " ,
" IRP_MN_QUERY_RESOURCES " ,
" IRP_MN_QUERY_RESOURCE_REQUIREMENTS " ,
" IRP_MN_QUERY_DEVICE_TEXT " ,
" IRP_MN_FILTER_RESOURCE_REQUIREMENTS " ,
"" ,
" IRP_MN_READ_CONFIG " ,
" IRP_MN_WRITE_CONFIG " ,
" IRP_MN_EJECT " ,
" IRP_MN_SET_LOCK " ,
" IRP_MN_QUERY_ID " ,
" IRP_MN_QUERY_PNP_DEVICE_STATE " ,
" IRP_MN_QUERY_BUS_INFORMATION " ,
" IRP_MN_DEVICE_USAGE_NOTIFICATION " ,
" IRP_MN_SURPRISE_REMOVAL " ,
" IRP_MN_QUERY_LEGACY_BUS_INFORMATION " ,
};

if (fcn < arraysize(pnpname))
KdPrint((DRIVERNAME
" - IRP_MJ_PNP (%s)\n " , pnpname[fcn]));
else
KdPrint((DRIVERNAME
" - IRP_MJ_PNP (%2.2X)\n " , fcn));
#endif // DBG

// Handle usage notification specially in order to track power pageable
// flag correctly. We need to avoid allowing a non-pageable handler to be
// layered on top of a pageable handler.
if (fcn == IRP_MN_DEVICE_USAGE_NOTIFICATION)
{
// usage notification
if ( ! fido -> AttachedDevice || (fido -> AttachedDevice -> Flags & DO_POWER_PAGABLE))
fido
-> Flags |= DO_POWER_PAGABLE;
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) UsageNotificationCompletionRoutine,
(PVOID) pdx, TRUE, TRUE, TRUE);
return IoCallDriver(pdx -> LowerDeviceObject, Irp);
}
// usage notification

// Handle start device specially in order to correctly inherit
// FILE_REMOVABLE_MEDIA
if (fcn == IRP_MN_START_DEVICE)
{
// device start
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) StartDeviceCompletionRoutine,
(PVOID) pdx, TRUE, TRUE, TRUE);
return IoCallDriver(pdx -> LowerDeviceObject, Irp);
}
// device start

// Handle remove device specially in order to cleanup device stack
if (fcn == IRP_MN_REMOVE_DEVICE)
{
// remove device
IoSkipCurrentIrpStackLocation(Irp);
status
= IoCallDriver(pdx -> LowerDeviceObject, Irp);
IoReleaseRemoveLockAndWait(
& pdx -> RemoveLock, Irp);
RemoveDevice(fido);
return status;
}
// remove device

// Simply forward any other type of PnP request
IoSkipCurrentIrpStackLocation(Irp);
status
= IoCallDriver(pdx -> LowerDeviceObject, Irp);
IoReleaseRemoveLock(
& pdx -> RemoveLock, Irp);
return status;
}
// DispatchPnp

////////////////////////////////////////////////////////////////////////////// /
// GetDeviceTypeToUse returns the device object type of the next lower device
// object. This helps overcome a bug in some Win2K file systems which expect the
// topmost FiDO in a storage stack to have a VPB and, hence, to have been created
// with a type such as FILE_DEVICE_DISK.
#pragma PAGEDCODE
ULONG GetDeviceTypeToUse(PDEVICE_OBJECT pdo)
{
// GetDeviceTypeToUse
PDEVICE_OBJECT ldo = IoGetAttachedDeviceReference(pdo);
if ( ! ldo)
return FILE_DEVICE_UNKNOWN;
ULONG devtype
= ldo -> DeviceType;
ObDereferenceObject(ldo);
return devtype;
}
// GetDeviceTypeToUse

////////////////////////////////////////////////////////////////////////////// /
#pragma PAGEDCODE
VOID RemoveDevice(IN PDEVICE_OBJECT fido)
{
// RemoveDevice
KdPrint(( " Enter RemoveDevice " ));
PAGED_CODE();
PDEVICE_EXTENSION pdx
= (PDEVICE_EXTENSION) fido -> DeviceExtension;
if (pdx -> LowerDeviceObject)
IoDetachDevice(pdx
-> LowerDeviceObject);
IoDeleteDevice(fido);
}
// RemoveDevice

////////////////////////////////////////////////////////////////////////////// /
#pragma LOCKEDCODE
NTSTATUS StartDeviceCompletionRoutine(PDEVICE_OBJECT fido, PIRP Irp, PDEVICE_EXTENSION pdx)
{
// StartDeviceCompletionRoutine
if (Irp -> PendingReturned)
IoMarkIrpPending(Irp);
// Inherit FILE_REMOVABLE_MEDIA flag from lower object. This is necessary
// for a disk filter, but it isn't available until start-device time. Drivers
// above us may examine the flag as part of their own start-device processing, too.
if (pdx -> LowerDeviceObject -> Characteristics & FILE_REMOVABLE_MEDIA)
fido
-> Characteristics |= FILE_REMOVABLE_MEDIA;
IoReleaseRemoveLock(
& pdx -> RemoveLock, Irp);
return STATUS_SUCCESS;
}
// StartDeviceCompletionRoutine

////////////////////////////////////////////////////////////////////////////// /
#pragma LOCKEDCODE
NTSTATUS UsageNotificationCompletionRoutine(PDEVICE_OBJECT fido, PIRP Irp, PDEVICE_EXTENSION pdx)
{
// UsageNotificationCompletionRoutine
if (Irp -> PendingReturned)
IoMarkIrpPending(Irp);
// If lower driver cleared pageable flag, we must do the same
if ( ! (pdx -> LowerDeviceObject -> Flags & DO_POWER_PAGABLE))
fido
-> Flags &= ~ DO_POWER_PAGABLE;
IoReleaseRemoveLock(
& pdx -> RemoveLock, Irp);
return STATUS_SUCCESS;
}
// UsageNotificationCompletionRoutine

#pragma LOCKEDCODE // force inline functions into nonpaged code

 

示例代码 P507

wps_clip_image-1252

图示U盘设备驱动拓扑 P509

    对于过滤驱动程序的AddDevice例程,需要创建一个过滤设备对象,然后把这个对象附加在设备栈上,并用设备扩展记录仅低于本层的驱动对象地址。对于磁盘过滤的关键在于IRP_MJ_SCSI这个IRP的捕获,DISK.sys与USBSTOR.sys间传递的就是标准SCSI指令。如果想让设备变成只读,只要将SCSI命令中的SCSIOP_WRITE请求设置为失败;还有另外一种方法是在SCSIOP_MODE_SENSE请求时,设置 MODE_DSP_WRITE_PROTECT位。

2、NT式过滤驱动

如将键盘中的CapsLock改成Ctrl,示例如下。

示例代码(Link)

你可能感兴趣的:(过滤)