• 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)

  • 相关阅读:
    一维,二维差分 (P3397 地毯)
    P3406 海底高铁
    P2004 领地选择
    priority_queue 大顶堆与小顶堆的用法 & 常见数据结构时间复杂度
    AcWing 电影
    P2678 跳石头
    HDU2041超级楼梯
    HDU2087剪花布条
    n条线分平面问题解决方法总结
    HDU2034 人见人爱A-B(C++)
  • 原文地址:https://www.cnblogs.com/mydomain/p/1905051.html
Copyright © 2020-2023  润新知