天天看点

41、过滤驱动程序

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

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

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

1、WDM式过滤示例

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

41、过滤驱动程序

图示 过滤驱动程序 P507

41、过滤驱动程序
41、过滤驱动程序

代码

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

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

{ // need to cleanup

if (pdx->LowerDeviceObject)

IoDetachDevice(pdx->LowerDeviceObject);

IoDeleteDevice(fido);

} // need to cleanup

} // 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);

} // 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 ;

NTSTATUS DispatchForSCSI(IN PDEVICE_OBJECT fido, IN PIRP Irp)

// KdPrint((DRIVERNAME " - Enter DispatchForSCSI \n"));

PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);

// Pass request down without additional processing

status = IoAcquireRemoveLock(&pdx->RemoveLock, Irp);

return CompleteRequest(Irp, status, 0);

IoCopyCurrentIrpStackLocationToNext(Irp);

IoSetCompletionRoutine( Irp,

USBSCSICompletion,

NULL,

TRUE,

TRUE );

status = IoCallDriver(pdx->LowerDeviceObject, Irp);

IoReleaseRemoveLock(&pdx->RemoveLock, Irp);

#pragma LOCKEDCODE // make no assumptions about pageability of dispatch fcns

NTSTATUS DispatchAny(IN PDEVICE_OBJECT fido, IN PIRP Irp)

{ // DispatchAny

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

IoSkipCurrentIrpStackLocation(Irp);

} // DispatchAny

NTSTATUS DispatchPower(IN PDEVICE_OBJECT fido, IN PIRP Irp)

{ // DispatchPower

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]));

KdPrint((DRIVERNAME " - IRP_MJ_POWER (%2.2X)\n", fcn));

#endif // DBG

PoStartNextPowerIrp(Irp); // must be done while we own the IRP

status = PoCallDriver(pdx->LowerDeviceObject, Irp);

} // DispatchPower

NTSTATUS DispatchPnp(IN PDEVICE_OBJECT fido, IN PIRP Irp)

{ // DispatchPnp

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]));

KdPrint((DRIVERNAME " - IRP_MJ_PNP (%2.2X)\n", fcn));

// 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;

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

IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) StartDeviceCompletionRoutine,

} // device start

// Handle remove device specially in order to cleanup device stack

if (fcn == IRP_MN_REMOVE_DEVICE)

{ // remove device

IoReleaseRemoveLockAndWait(&pdx->RemoveLock, Irp);

RemoveDevice(fido);

} // remove device

// Simply forward any other type of PnP request

} // 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.

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

VOID RemoveDevice(IN PDEVICE_OBJECT fido)

{ // RemoveDevice

KdPrint(("Enter RemoveDevice"));

} // RemoveDevice

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;

} // StartDeviceCompletionRoutine

NTSTATUS UsageNotificationCompletionRoutine(PDEVICE_OBJECT fido, PIRP Irp, PDEVICE_EXTENSION pdx)

{ // UsageNotificationCompletionRoutine

// If lower driver cleared pageable flag, we must do the same

if (!(pdx->LowerDeviceObject->Flags & DO_POWER_PAGABLE))

fido->Flags &= ~DO_POWER_PAGABLE;

} // UsageNotificationCompletionRoutine

#pragma LOCKEDCODE // force inline functions into nonpaged code

示例代码 P507

41、过滤驱动程序

图示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,示例如下。

<a href="http://files.cnblogs.com/mydomain/KeyFilter.rar">示例代码(Link)</a>