65.9K
CodeProject 正在变化。 阅读更多。
Home

驱动程序的调度例程挂钩

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2018年1月18日

CPOL

5分钟阅读

viewsIcon

12285

downloadIcon

411

本文介绍了如何挂钩驱动程序的派遣例程。

引言

本文所述的驱动程序允许您记录给定设备对象的派遣例程调用(及其相对顺序)。步骤如下:

  1. 引用设备对象
  2. 为选定的派遣例程设置挂钩
  3. 释放挂钩并检索日志信息
  4. 取消引用设备对象

此外,该驱动程序还允许查看设备堆栈、设备父级和设备子级。

我使用此驱动程序来查看设备子树以及我们在插入/拔出/安全移除/启用/禁用 USB 闪存驱动器时调用的 Pnp 派遣例程(及其相对顺序)。

背景

由于我们将考虑 USB 闪存驱动器,我们将概述用于表示它的设备节点。有关设备树和设备节点的更多详细信息,请参阅 https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/device-tree

在这里,两个 USB 闪存驱动器连接到 **USB 集线器**,每个闪存驱动器由一个 **大容量存储** 设备节点及其子节点 - **磁盘** 设备节点表示。**USB 集线器** 连接到 **USB 主机控制器**,后者连接到 **PCI 总线**,PCI 总线连接到 **ACPI**,ACPI 连接到 **根**(为简单起见,此处未显示)。您可以打开设备管理器并选择 **查看** - **按连接查看设备** 来查看更多详细信息。

每个设备节点都有关联的设备堆栈。在此图表中,**FDO** 位于顶部,**PDO** 位于底部,其他设备对象(主要的 **Filter DO**)为简单起见未显示。

  • disk.sys 作为磁盘设备的函数驱动程序。
  • usbstor.sys 作为磁盘设备的总线驱动程序,并作为大容量存储设备的函数驱动程序。
  • iusb3hub.sys 作为大容量存储设备的总线驱动程序,并作为集线器设备的函数驱动程序。
  • iusb3xhc.sys 作为集线器设备的总线驱动程序,并作为主机控制器设备的函数驱动程序。

注意:Pnp 请求会发送到设备堆栈顶部的设备对象,然后向下传递到较低的设备对象,以便每个驱动程序都可以做出贡献。

我观察 Pnp 请求时编写的伪代码

Plugged:    // device is plugged event { USB hub PDO }
{
    PnpEnumerate(PDO);
}

Unplugged:    // device is unplugged event { USB hub PDO }
{
    PnpEnumerate(PDO);
}

bool SafeRemove(PDO)                // safe remove request (from tray) { USB mass storage PDO }
{
    // handle removal and ejection relations...
    b = PnpQueryRemoveDevice(PDO);
    if (b) PnpRemoveDevice(PDO);
    else PnpCancelRemoveDevice(PDO);
    return b;
}

bool Enable(PDO)        // enable request (from device manager)
{
    PnpGatherInfo(PDO);
    PnpSendRequest(PDO, { IRP_MN_DEVICE_ENUMERATED });
    PnpConstructStack(PDO);
    PnpSendRequest(PDO, { 0x18 });
    ResInfo[0] = PnpSendRequest(PDO, { IRP_MN_QUERY_RESOURCE_REQUIREMENTS });
    ResInfo[1] = PnpSendRequest(PDO, { IRP_MN_FILTER_RESOURCE_REQUIREMENTS });
    SetInfo(PDO, ResInfo);
    b = PnpSendRequest(PDO, { IRP_MN_START_DEVICE });
    if (b)
    {
        CapInfo = PnpSendRequest(PDO, { IRP_MN_QUERY_CAPABILITIES });
        SetInfo(PDO, CapInfo);
        StateInfo = PnpSendRequest(PDO, { IRP_MN_QUERY_PNP_DEVICE_STATE });
        SetInfo(PDO, StateInfo);
        SetState(PDO, STATE_STARTED);
        PnpEnumerate(PDO);
    }
    else
    {
        PnpSendRequest(PDO, { IRP_MN_REMOVE_DEVICE });
        SetState(PDO, STATE_FAILED);
    }
    return b;
}

bool Disable(PDO)        // disable request (from device manager)
{
    // handle removal relations...
    b = PnpQueryRemoveDevice(PDO);
    if (b)
    {
        PnpRemoveDevice(PDO);
        PnpGatherInfo(PDO);
        PnpSendRequest(PDO, { IRP_MN_QUERY_REMOVE_DEVICE });
        PnpSendRequest(PDO, { IRP_MN_REMOVE_DEVICE });
        PnpSendRequest(PDO, { IRP_MN_DEVICE_ENUMERATED });
    }
    else
    {
        PnpCancelRemoveDevice(PDO);
    }
    return b;
}

void PnpRemoveDevice(PDO)
{
    Children[] = GetChildren(PDO);
    for each Child in Children
    {
        PnpRemoveDevice(Child);
    }
    PnpSendRequest(PDO, { IRP_MN_REMOVE_DEVICE });
    SetState(PDO, STATE_REMOVED);
}

void PnpCancelRemoveDevice(PDO)
{
    PnpSendRequest(PDO, { IRP_MN_CANCEL_REMOVE_DEVICE });
    SetState(PDO, STATE_STARTED);
    Children[] = GetChildren(PDO);
    for each Child in Children
    {
        PnpCancelRemoveDevice(Child);
    }
}

void PnpSurpriseRemoval(PDO)
{
    Children[] = GetChildren(PDO);
    for each Child in Children
    {
        PnpSurpriseRemoval(Child);
    }
    PnpSendRequest(PDO, { IRP_MN_SURPRISE_REMOVAL  });
    SetState(PDO, STATE_SURPRISE_REMOVED);
}

bool PnpQueryRemoveDevice(PDO)
{
    Children[] = GetChildren(PDO);
    for each Child in Children
    {
        if (!PnpQueryRemoveDevice(Child)) return false;
    }
    b = PnpSendRequest(PDO, { IRP_MN_QUERY_REMOVE_DEVICE });
    if (b) SetState(PDO, STATE_REMOVE_PENDING);
    return b;
}

void PnpGatherInfo(PDO)
{
    IdInfo[0] = PnpSendRequest(PDO, { IRP_MN_QUERY_ID, BusQueryDeviceID });
    IdInfo[1] = PnpSendRequest(PDO, { IRP_MN_QUERY_ID, BusQueryInstanceID });
    IdInfo[2] = PnpSendRequest(PDO, { IRP_MN_QUERY_ID, BusQueryHardwareIDs });
    IdInfo[3] = PnpSendRequest(PDO, { IRP_MN_QUERY_ID, BusQueryCompatibleIDs });
    IdInfo[4] = PnpSendRequest(PDO, { IRP_MN_QUERY_ID, BusQueryContainerID });
    SetInfo(PDO, IdInfo);
    CapInfo = PnpSendRequest(PDO, { IRP_MN_QUERY_CAPABILITIES });
    SetInfo(PDO, CapInfo);
    TextInfo[0] = PnpSendRequest(PDO, { IRP_MN_QUERY_DEVICE_TEXT, DeviceTextDescription });
    TextInfo[1] = PnpSendRequest(PDO, { IRP_MN_QUERY_DEVICE_TEXT, DeviceTextLocationInformation });
    SetInfo(PDO, TextInfo);
    BusInfo = PnpSendRequest(PDO, { IRP_MN_QUERY_BUS_INFORMATION });
    SetInfo(PDO, BusInfo);
    ResInfo[0] = PnpSendRequest(PDO, { IRP_MN_QUERY_RESOURCE_REQUIREMENTS });
    ResInfo[1] = PnpSendRequest(PDO, { IRP_MN_QUERY_RESOURCES });
    SetInfo(PDO, ResInfo);
}

void PnpEnumerate(PDO)
{
    if (CanBeParent(PDO))
    {
        ChildrenNew[] = PnpSendRequest(PDO, { IRP_MN_QUERY_DEVICE_RELATIONS, BusRelations  });
        ChildrenOld[] = GetChildren(PDO);
        for each Child in ChildrenNew
        {
            if (!Find(Child, ChildrenOld))
            {
                AddChild(PDO, Child);
                b = Enable(Child);
                // handle result...
            }
        }
        for each Child in ChildrenOld
        {
            if (!Find(Child, ChildrenNew))
            {
                if (GetState(Child) != STATE_REMOVED)
                {
                    // handle removal and ejection relations...
                    PnpSurpriseRemoval(Child);
                }
                else
                {
                    // handle ejection relations...
                }
                PnpRemoveDevice(Child);
                RemoveChild(PDO, Child);
            }
        }
    }
    else
    {
        PnpSendRequest(PDO, { IRP_MN_QUERY_DEVICE_RELATIONS, 0xffffffff });
    }
}

我们引入 **设备堆栈状态** 的概念,可能的值为:

  1. 设备堆栈(和设备节点)不存在
  2. 设备堆栈仅包含 PDO
  3. 设备堆栈包含 PDO 和 FDO

向量 { X, Y } 将表示 { disk, mass storage } 的设备堆栈状态。用户操作标题后的向量表示处理任何请求之前的初始设备堆栈状态。请求标题后的向量表示处理请求后的设备堆栈状态(如果没有向量,则设备堆栈状态未更改)。

用户操作的 Pnp 请求观察顺序

USB 闪存驱动器已插入 { 1, 1 }

  1. { IRP_MN_QUERY_DEVICE_RELATIONS, BusRelations } -> hub -> { 1, 2 }
  2. Configure -> mass storage -> { 1, 3 }
  3. { IRP_MN_QUERY_DEVICE_RELATIONS, BusRelations } -> mass storage -> { 2, 3 }
  4. Configure -> disk -> { 3, 3 }

USB 闪存驱动器已拔出(未经安全移除){ 3, 3 }

  1. { IRP_MN_QUERY_DEVICE_RELATIONS, BusRelations } -> hub
  2. { IRP_MN_SURPRISE_REMOVAL } -> disk
  3. { IRP_MN_SURPRISE_REMOVAL } -> mass storage
  4. { IRP_MN_REMOVE_DEVICE } -> disk -> { 1, 3 }
  5. { IRP_MN_REMOVE_DEVICE } -> mass storage -> { 1, 1 }

USB 闪存驱动器安全移除(从托盘){ 3, 3 }

  1. { IRP_MN_QUERY_REMOVE_DEVICE } -> disk
  2. { IRP_MN_QUERY_REMOVE_DEVICE } -> mass storage
  3. { IRP_MN_REMOVE_DEVICE } -> disk -> { 2, 3 }
  4. { IRP_MN_REMOVE_DEVICE } -> mass storage -> { 1, 2 }

USB 闪存驱动器已拔出(安全移除后){ 1, 2 }

  1. { IRP_MN_QUERY_DEVICE_RELATIONS, BusRelations } -> hub
  2. { IRP_MN_REMOVE_DEVICE } -> mass storage -> { 1, 1 }

磁盘禁用(来自设备管理器){ 3, 3 }

  1. { IRP_MN_QUERY_REMOVE_DEVICE } -> disk
  2. { IRP_MN_REMOVE_DEVICE } -> disk -> { 2, 3 }
  3. { IRP_MN_QUERY_REMOVE_DEVICE } -> disk
  4. { IRP_MN_REMOVE_DEVICE } -> disk

磁盘启用(来自设备管理器){ 2, 3 }

  1. Configure -> disk -> { 3, 3 }

大容量存储禁用(来自设备管理器){ 3, 3 }

  1. { IRP_MN_QUERY_REMOVE_DEVICE } -> disk
  2. { IRP_MN_QUERY_REMOVE_DEVICE } -> mass storage
  3. { IRP_MN_REMOVE_DEVICE } -> disk -> { 2, 3 }
  4. { IRP_MN_REMOVE_DEVICE } -> mass storage -> { 1, 2 }
  5. { IRP_MN_QUERY_REMOVE_DEVICE } -> mass storage
  6. { IRP_MN_REMOVE_DEVICE } -> mass storage

大容量存储启用(来自设备管理器){ 1, 2 }

  1. Configure -> mass storage -> { 1, 3 }
  2. { IRP_MN_QUERY_DEVICE_RELATIONS, BusRelations } -> mass storage -> { 2, 3 }
  3. Configure -> disk -> { 3, 3 }

Using the Code

驱动程序维护两个由自旋锁保护的链接列表

  1. 存储代表已引用设备对象的项(DEVICE_INFORMATION)的列表
  2. 存储代表已修补驱动程序对象(派遣例程指针已替换)的项(DRIVER_INFORMATION)的列表
struct DEVICE_INFORMATION
{
    LIST_ENTRY ListEntry;
    ULONG Id;
    LIST_ENTRY LogList;
    DEVICE_OBJECT *pDeviceObject;
    BOOLEAN HookActive;
    BOOLEAN HookDispatch[DISPATCH_TABLE_LENGTH];
};

struct DRIVER_INFORMATION
{
    LIST_ENTRY ListEntry;
    DRIVER_OBJECT *pDriverObject;
    ULONG HookCount;
    DRIVER_DISPATCH* OriginalDispatch[DISPATCH_TABLE_LENGTH];
};

// Each device object in device stack is represented by device entry:

struct DEVICE_ENTRY
{
    LIST_ENTRY ListEntry;
    ULONG ReferenceCount;
    ULONG DeviceType;
    ULONG Characteristics;
    BOOLEAN Dispatch[DISPATCH_TABLE_LENGTH];
    OBJECT_NAME_INFORMATION *pNameRecord[NAME_RECORD_TABLE_LENGTH];
};

// Each logged call is represented by log entry:

struct LOG_RECORD_INFORMATION
{
    UNICODE_STRING Record;
};

struct LOG_ENTRY
{
    LIST_ENTRY ListEntry;
    ULONG Id;
    LOG_RECORD_INFORMATION *pLogRecord[LOG_RECORD_TABLE_LENGTH];
};

我们可以使用 LiveKD 来查看未在提供的头文件中定义的内部结构布局。

dt nt!_DEVICE_NODE

struct DEVICE_NODE
{
    DEVICE_NODE *Sibling;
    DEVICE_NODE *Child;
    DEVICE_NODE *Parent;
    DEVICE_NODE *LastChild;
    DEVICE_OBJECT *PhysicalDeviceObject;
};

dt nt!_OBJECT_HEADER

struct OBJECT_HEADER
{
    LONG PointerCount;
    union
    {
        LONG HandleCount;
        PVOID NextToFree;
    };
    PVOID Lock;
    UCHAR TypeIndex;
    UCHAR TraceFlags;
    UCHAR InfoMask;
    UCHAR Flags;
    union
    {
        PVOID ObjectCreateInfo;
        PVOID QuotaBlockCharged;
    };
    PVOID SecurityDescriptor;
    QUAD Body;
};

现在我将展示支持的 ioctl 及其处理程序

  1. #define IOCTL_DEVICE_REF CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
    
    struct DEVICE_REF_INPUT
    {
        ULONG Length;
        LONG Shift;
        ULONG PathOffset;
    };
    
    struct DEVICE_REF_OUTPUT
    {
        ULONG Length;
        ULONG Id;
    };
    
    NTSTATUS IoctlDeviceRef(DEVICE_REF_INPUT *pInputBuffer, DEVICE_REF_OUTPUT *pOutputBuffer)
    {
        NTSTATUS Status;
        WCHAR *pDevicePath;
        DEVICE_OBJECT *pDeviceObject;
        DEVICE_INFORMATION *pDeviceInfo;
    
        pDevicePath = (WCHAR*)GET_FIELD_POINTER(pInputBuffer, PathOffset);
        Status = GetDeviceObject(pDevicePath, pInputBuffer->Shift, &pDeviceObject);
    
        if (NT_SUCCESS(Status))
        {
            Status = GetDeviceByPointer(pDeviceObject, &pDeviceInfo);
    
            if (!NT_SUCCESS(Status))
            {
                Status = AddDevice(pDeviceObject, &pDeviceInfo);
    
                if (NT_SUCCESS(Status)) pOutputBuffer->Id = pDeviceInfo->Id;
            }
            else Status = STATUS_ALREADY_REGISTERED;
    
            ObDereferenceObject(pDeviceObject);
        }
    
        return Status;
    }
  2. #define IOCTL_DEVICE_UNREF CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
    
    struct DEVICE_UNREF_INPUT
    {
        ULONG Length;
        ULONG Id;
    };
    
    NTSTATUS IoctlDeviceUnref(DEVICE_UNREF_INPUT *pInputBuffer)
    {
        NTSTATUS Status;
        DEVICE_INFORMATION *pDeviceInfo;
    
        Status = GetDevice(pInputBuffer->Id, &pDeviceInfo);
    
        if (NT_SUCCESS(Status)) RemoveDevice(pDeviceInfo);
    
        return Status;
    }
    
  3. #define IOCTL_DEVICE_LIST        
    CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
    
    enum DEVICE_LIST_TYPE
    {
        DeviceListSingle,
        DeviceListStack,
        DeviceListDriver,
        DeviceListParent,
        DeviceListChildren
    };
    
    struct DEVICE_LIST_INPUT
    {
        ULONG Length;
        ULONG Id;
        ULONG DeviceListType;
    };
    
    struct DEVICE_LIST_OUTPUT
    {
        ULONG Length;
        LIST_PACKED List;
    };
    
    NTSTATUS IoctlDeviceList(DEVICE_LIST_INPUT *pInputBuffer, DEVICE_LIST_OUTPUT *pOutputBuffer)
    {
        NTSTATUS Status;
        ULONG Count, Length;
        DEVICE_INFORMATION *pDeviceInfo;
        DEVICE_OBJECT **DeviceObjectList;
        DEVICE_OBJECT *pDeviceObject;
        DEVICE_ENTRY *pDeviceEntry;
        DEVICE_NODE *pDeviceNode;
        LIST_ENTRY DeviceList;
        LIST_INFO ListInfo;
        HANDLE Handle;
        IO_STATUS_BLOCK IoStatusBlock;
        OBJECT_ATTRIBUTES ObjectAttributes;
        OBJECT_NAME_INFORMATION *pNameRecord;
    
        Status = GetDevice(pInputBuffer->Id, &pDeviceInfo);
    
        if (NT_SUCCESS(Status))
        {
            InitializeListHead(&DeviceList);
    
            switch (pInputBuffer->DeviceListType)
            {
            case DeviceListDriver:
                pDeviceObject = pDeviceInfo->pDeviceObject;
    
                Status = CreateDeviceObjectList
                         (pDeviceObject->DriverObject, &DeviceObjectList, &Count);
    
                if (NT_SUCCESS(Status))
                {
                    for (ULONG i = 0; i < Count; ++i)
                    {
                        Status = CreateDeviceEntry(DeviceObjectList[i], &pDeviceEntry);
    
                        if (NT_SUCCESS(Status)) InsertTailList(&DeviceList, &pDeviceEntry->ListEntry);
                        else break;
                    }
    
                    ReleaseDeviceObjectList(DeviceObjectList, Count);
                }
    
                break;
            case DeviceListSingle:
                pDeviceObject = pDeviceInfo->pDeviceObject;
    
                Status = CreateDeviceEntry(pDeviceObject, &pDeviceEntry);
    
                if (NT_SUCCESS(Status)) InsertTailList(&DeviceList, &pDeviceEntry->ListEntry);
    
                break;
            case DeviceListStack:
                pDeviceObject = pDeviceInfo->pDeviceObject;
    
                pDeviceNode = GetDeviceNode(pDeviceObject);
    
                if (pDeviceNode)        // PDO
                {
                    Status = CreateNameRecord(pDeviceObject, &pNameRecord);
    
                    if (NT_SUCCESS(Status))
                    {
                        InitializeObjectAttributes(&ObjectAttributes, 
                                &pNameRecord->Name, OBJ_CASE_INSENSITIVE, NULL, NULL);
    
                        Status = ZwOpenFile(&Handle, FILE_GENERIC_READ, 
                                 &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | 
                                 FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0);
    
                        if (NT_SUCCESS(Status))   // IRP_MN_REMOVE_DEVICE will not be 
                                                  // sent to the device stack untill handle is opened
                        {
                            pDeviceObject = GetTopDeviceObject(pDeviceObject);
    
                            while (pDeviceObject)
                            {
                                Status = CreateDeviceEntry(pDeviceObject, &pDeviceEntry);
    
                                if (NT_SUCCESS(Status)) InsertTailList
                                          (&DeviceList, &pDeviceEntry->ListEntry);
                                else break;
    
                                pDeviceObject = GetLowerDeviceObject(pDeviceObject);
                            }
    
                            ZwClose(Handle);
                        }
                        else
                        {
                            Status = CreateDeviceEntry(pDeviceObject, &pDeviceEntry);
    
                            if (NT_SUCCESS(Status)) InsertTailList
                                     (&DeviceList, &pDeviceEntry->ListEntry);
                        }
    
                        ReleaseNameRecord(pNameRecord);
                    }
                }
                else Status = STATUS_NOT_SUPPORTED;
    
                break;
            case DeviceListParent:
                pDeviceObject = pDeviceInfo->pDeviceObject;
    
                pDeviceNode = GetDeviceNode(pDeviceObject);
    
                if (pDeviceNode)        // PDO
                {
                    // acquire tree lock...
    
                    pDeviceNode = pDeviceNode->Parent;
    
                    if (pDeviceNode)
                    {
                        pDeviceObject = pDeviceNode->PhysicalDeviceObject;
    
                        Status = CreateDeviceEntry(pDeviceObject, &pDeviceEntry);
    
                        if (NT_SUCCESS(Status)) 
                              InsertTailList(&DeviceList, &pDeviceEntry->ListEntry);
                    }
    
                    // release tree lock...
                }
                else Status = STATUS_NOT_SUPPORTED;
    
                break;
            case DeviceListChildren:
                pDeviceObject = pDeviceInfo->pDeviceObject;
    
                pDeviceNode = GetDeviceNode(pDeviceObject);
    
                if (pDeviceNode)        // PDO
                {
                    // acquire tree lock...
    
                    pDeviceNode = pDeviceNode->Child;
    
                    while (pDeviceNode)
                    {
                        pDeviceObject = pDeviceNode->PhysicalDeviceObject;
    
                        Status = CreateDeviceEntry(pDeviceObject, &pDeviceEntry);
    
                        if (NT_SUCCESS(Status)) InsertTailList(&DeviceList, &pDeviceEntry->ListEntry);
                        else break;
    
                        pDeviceNode = pDeviceNode->Sibling;
                    }
    
                    // release tree lock...
                }
                else Status = STATUS_NOT_SUPPORTED;
    
                break;
            }
    
            if (NT_SUCCESS(Status))
            {
                GetDeviceListInfo(&DeviceList, &ListInfo);
    
                Length = sizeof(DEVICE_LIST_OUTPUT) + ListInfo.HeaderLength + ListInfo.ListLength;
    
                if (Length <= pOutputBuffer->Length)
                {
                    StoreDeviceList(&DeviceList, &ListInfo, &pOutputBuffer->List);
                }
                else Status = STATUS_BUFFER_TOO_SMALL;
    
                pOutputBuffer->Length = Length;
            }
    
            ReleaseDeviceList(&DeviceList);
        }
    
        return Status;
    }
  4. #define IOCTL_DEVICE_HOOK        
    CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
    
    struct DEVICE_HOOK_INPUT
    {
        ULONG Length;
        ULONG Id;
        BOOLEAN Dispatch[DISPATCH_TABLE_LENGTH];
    };
    
    NTSTATUS IoctlDeviceHook(DEVICE_HOOK_INPUT *pInputBuffer)
    {
        NTSTATUS Status;
        DRIVER_OBJECT *pDriverObject;
        DEVICE_INFORMATION *pDeviceInfo;
        DRIVER_INFORMATION *pDriverInfo;
    
        Status = GetDevice(pInputBuffer->Id, &pDeviceInfo);
    
        if (NT_SUCCESS(Status))
        {
            if (!pDeviceInfo->HookActive)
            {
                pDriverObject = pDeviceInfo->pDeviceObject->DriverObject;
    
                Status = GetDriver(pDriverObject, &pDriverInfo);
    
                if (!NT_SUCCESS(Status)) Status = AddDriver(pDriverObject, &pDriverInfo);
    
                if (NT_SUCCESS(Status))
                {
                    ReleaseLogList(&pDeviceInfo->LogList);
    
                    EnableDeviceHook(pDeviceInfo, &pInputBuffer->Dispatch);
    
                    ++pDriverInfo->HookCount;
                }
            }
            else Status = STATUS_UNSUCCESSFUL;
        }
    
        return Status;
    }
  5. #define IOCTL_DEVICE_UNHOOK        
    CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
    
    struct DEVICE_UNHOOK_INPUT
    {
        ULONG Length;
        ULONG Id;
    };
    
    struct DEVICE_UNHOOK_OUTPUT
    {
        ULONG Length;
        LIST_PACKED List;
    };
    
    NTSTATUS IoctlDeviceUnhook(DEVICE_UNHOOK_INPUT *pInputBuffer, DEVICE_UNHOOK_OUTPUT *pOutputBuffer)
    {
        NTSTATUS Status;
        DRIVER_OBJECT *pDriverObject;
        DEVICE_INFORMATION *pDeviceInfo;
        DRIVER_INFORMATION *pDriverInfo;
        LIST_INFO ListInfo;
        ULONG Length;
    
        Status = GetDevice(pInputBuffer->Id, &pDeviceInfo);
    
        if (NT_SUCCESS(Status))
        {
            if (pDeviceInfo->HookActive)
            {
                DisableDeviceHook(pDeviceInfo);
    
                pDriverObject = pDeviceInfo->pDeviceObject->DriverObject;
    
                Status = GetDriver(pDriverObject, &pDriverInfo);
    
                if (NT_SUCCESS(Status))
                {
                    --pDriverInfo->HookCount;
    
                    if (!pDriverInfo->HookCount) RemoveDriver(pDriverInfo);
                }
            }
    
            GetLogListInfo(&pDeviceInfo->LogList, &ListInfo);
    
            Length = sizeof(DEVICE_UNHOOK_OUTPUT)+ListInfo.HeaderLength + ListInfo.ListLength;
    
            if (Length <= pOutputBuffer->Length)
            {
                StoreLogList(&pDeviceInfo->LogList, &ListInfo, &pOutputBuffer->List);
    
                ReleaseLogList(&pDeviceInfo->LogList);
            }
            else Status = STATUS_BUFFER_TOO_SMALL;
    
            pOutputBuffer->Length = Length;
        }
    
        return Status;
    }

派遣例程(替换原始派遣例程)

NTSTATUS DispatchHook(DEVICE_OBJECT *pDeviceObject, IRP *pIrp)
{
    IO_STACK_LOCATION *Stack;
    KLOCK_QUEUE_HANDLE LockHandle;
    DEVICE_INFORMATION *pDeviceInfo;
    DRIVER_INFORMATION *pDriverInfo;
    DEVICE_EXTENSION *pDeviceExtension;
    DRIVER_DISPATCH *pDriverDispatch;
    LOG_ENTRY *pLogEntry;
    NTSTATUS Status;

    pDeviceExtension = (DEVICE_EXTENSION*)g_pDeviceObject->DeviceExtension;

    IoAcquireRemoveLock(&pDeviceExtension->RemoveLock, NULL);

    Stack = IoGetCurrentIrpStackLocation(pIrp);

    KeAcquireInStackQueuedSpinLockAtDpcLevel(&g_DeviceLock, &LockHandle);

    pDeviceInfo = (DEVICE_INFORMATION*)g_DeviceList.Flink;

    while (pDeviceInfo != (DEVICE_INFORMATION*)&g_DeviceList)
    {
        if (pDeviceObject == pDeviceInfo->pDeviceObject)
        {
            if ((pDeviceInfo->HookActive) && (pDeviceInfo->HookDispatch[Stack->MajorFunction]))
            {
                Status = CreateLogEntry(pDeviceObject, Stack, &pLogEntry);

                if (NT_SUCCESS(Status)) InsertTailList(&pDeviceInfo->LogList, &pLogEntry->ListEntry);
            }

            break;
        }

        pDeviceInfo = (DEVICE_INFORMATION*)pDeviceInfo->ListEntry.Flink;
    }

    KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);

    pDriverDispatch = NULL;

    KeAcquireInStackQueuedSpinLockAtDpcLevel(&g_DriverLock, &LockHandle);

    pDriverInfo = (DRIVER_INFORMATION*)g_DriverList.Flink;

    while (pDriverInfo != (DRIVER_INFORMATION*)&g_DriverList)
    {
        if (pDeviceObject->DriverObject == pDriverInfo->pDriverObject)
        {
            pDriverDispatch = pDriverInfo->OriginalDispatch[Stack->MajorFunction];
            break;
        }

        pDriverInfo = (DRIVER_INFORMATION*)pDriverInfo->ListEntry.Flink;
    }

    KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);

    if (!pDriverDispatch) pDriverDispatch = 
              pDeviceObject->DriverObject->MajorFunction[Stack->MajorFunction];

    Status = pDriverDispatch(pDeviceObject, pIrp);

    IoReleaseRemoveLock(&pDeviceExtension->RemoveLock, NULL);

    return Status;
}

创建设备条目例程

NTSTATUS CreateDeviceEntry(DEVICE_OBJECT *pDeviceObject, DEVICE_ENTRY **ppDeviceEntry)
{
    NTSTATUS Status;
    DEVICE_ENTRY *pDeviceEntry;
    DRIVER_OBJECT *pDriverObject;
    OBJECT_HEADER *pObjectHeader;

    pDeviceEntry = (DEVICE_ENTRY*)ExAllocatePool(PagedPool, sizeof(DEVICE_ENTRY));

    if (pDeviceEntry)
    {
        pObjectHeader = OBJECT_TO_OBJECT_HEADER(pDeviceObject);
        pDeviceEntry->ReferenceCount = pObjectHeader->PointerCount;
        pDeviceEntry->DeviceType = pDeviceObject->DeviceType;
        pDeviceEntry->Characteristics = pDeviceObject->Characteristics;

        pDriverObject = pDeviceObject->DriverObject;

        for (ULONG i = 0; i < DISPATCH_TABLE_LENGTH; ++i)
        {
            if (pDriverObject->MajorFunction[i]) pDeviceEntry->Dispatch[i] = TRUE;
            else pDeviceEntry->Dispatch[i] = FALSE;
        }

        Status = CreateNameRecord(pDeviceObject, &pDeviceEntry->pNameRecord[NAME_RECORD_DEVICE]);

        if (NT_SUCCESS(Status))
        {
            Status = CreateNameRecord(pDeviceObject->DriverObject, 
                              &pDeviceEntry->pNameRecord[NAME_RECORD_DRIVER]);

            if (NT_SUCCESS(Status))
            {
                *ppDeviceEntry = pDeviceEntry;
            }
            else
            {
                ReleaseNameRecord(pDeviceEntry->pNameRecord[NAME_RECORD_DEVICE]);
                ExFreePool(pDeviceEntry);
            }
        }
        else ExFreePool(pDeviceEntry);
    }
    else Status = STATUS_INSUFFICIENT_RESOURCES;

    return Status;
}

创建日志条目例程

NTSTATUS CreateLogEntry(DEVICE_OBJECT *pDeviceObject, IO_STACK_LOCATION *Stack, LOG_ENTRY **ppLogEntry)
{
    ULONG Value;
    NTSTATUS Status;
    LOG_ENTRY *pLogEntry;
    DEVICE_NODE *pDeviceNode;
    DEVICE_INFORMATION *pDeviceInfoChild;

    pLogEntry = (LOG_ENTRY*)ExAllocatePool(PagedPool, sizeof(LOG_ENTRY));

    if (pLogEntry)
    {
        pLogEntry->Id = g_LogId;
        ++g_LogId;

        for (ULONG i = 0; i < LOG_RECORD_TABLE_LENGTH; ++i) pLogEntry->pLogRecord[i] = NULL;

        switch (Stack->MajorFunction)
        {
        case IRP_MJ_PNP:
            if (Stack->MinorFunction < ARRAYSIZE(g_PNPMinorFunction))
            {
                Status = CreateLogRecord(&pLogEntry->pLogRecord[LOG_RECORD_0], 
                         0, 0, 0, L"%ls - %ls", g_MajorFunction[Stack->MajorFunction], 
                         g_PNPMinorFunction[Stack->MinorFunction]);
            }
            else
            {
                Status = CreateLogRecord(&pLogEntry->pLogRecord[LOG_RECORD_0], 0, 0, 0, 
                         L"%ls - %xh", g_MajorFunction[Stack->MajorFunction], Stack->MinorFunction);
            }

            if (NT_SUCCESS(Status))
            {
                switch (Stack->MinorFunction)
                {
                case IRP_MN_QUERY_DEVICE_RELATIONS:
                    Value = Stack->Parameters.QueryDeviceRelations.Type;

                    if (Value < ARRAYSIZE(g_PNPRelations))
                    {
                        Status = CreateLogRecord(&pLogEntry->pLogRecord[LOG_RECORD_1], 
                                 0, 0, 0, L"%s", g_PNPRelations[Value]);
                    }
                    else
                    {
                        Status = CreateLogRecord(&pLogEntry->pLogRecord[LOG_RECORD_1], 
                                 0, 0, 0, L"%xh", Value);
                    }

                    break;
                }
            }

            break;
        default:
            Status = STATUS_NOT_SUPPORTED;
            break;
        }

        if (NT_SUCCESS(Status))
        {
            *ppLogEntry = pLogEntry;
        }
        else
        {
            for (ULONG i = 0; i < LOG_RECORD_TABLE_LENGTH; ++i)
            {
                if (pLogEntry->pLogRecord[i]) ReleaseLogRecord(pLogEntry->pLogRecord[i]);
            }

            ExFreePool(pLogEntry);
        }
    }
    else Status = STATUS_INSUFFICIENT_RESOURCES;

    return Status;
}

我们可以使用以下代码运行我们的驱动程序(以管理员身份运行)

#include <Windows.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
    SC_HANDLE hSCManager;
    SC_HANDLE hService;
    SERVICE_STATUS ServiceStatus;

    hSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_CREATE_SERVICE);

    if (!hSCManager) DbgRaiseAssertionFailure();

    hService = CreateServiceW(hSCManager, L"Device Info Service Name",
        L"Device Info Display Name",
        SERVICE_START | DELETE | SERVICE_STOP,
        SERVICE_KERNEL_DRIVER,
        SERVICE_DEMAND_START,
        SERVICE_ERROR_IGNORE,
        L"D:\\Data\\New\\DeviceOwner\\x64\\Win7Debug\\DeviceOwner.sys",
        NULL, NULL, NULL, NULL, NULL);

    if (!hService)
    {
        hService = OpenServiceW(hSCManager, L"Device Info Service Name",
            SERVICE_START | DELETE | SERVICE_STOP);

        if (!hService) DbgRaiseAssertionFailure();
    }

    if (!StartServiceW(hService, 0, NULL)) DbgRaiseAssertionFailure();

    printf("Press Enter to close service\n");
    getchar();

    ControlService(hService, SERVICE_CONTROL_STOP, &ServiceStatus);

    DeleteService(hService);
    CloseServiceHandle(hService);

    CloseServiceHandle(hSCManager);
    return 0;
}

现在让我们检查 USB 闪存驱动器子树并设置挂钩,以查看当我们安全移除和拔出它时会发生什么。用户模式代码如下所示(省略了错误检查)

...

int main(int argc, char* argv[])
{    
    HANDLE Handle;
    ULONG *IdTable, Value, N;
    DEVICE_ENTRY_PACKED *pDeviceEntry;
    BOOLEAN Dispatch[DISPATCH_TABLE_LENGTH];

    Handle = CreateFileW(L"\\\\.\\DeviceInfo", 0, FILE_SHARE_READ | FILE_SHARE_WRITE | 
                         FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);

    if (Handle != INVALID_HANDLE_VALUE)
    {
        N = 4;
        IdTable = (ULONG*)AllocateMemory(sizeof(ULONG) * N);

        IdTable[0] = USBDriveRef(Handle, 0);

        if (IdTable[0] != INVALID_ID)
        {
            SubtreeRef(Handle, IdTable, N);

            for (ULONG i = 0; i < DISPATCH_TABLE_LENGTH; ++i) Dispatch[i] = FALSE;
            Dispatch[IRP_MJ_PNP] = TRUE;

            while (TRUE)
            {
                SubtreeViewStack(Handle, IdTable, N);

                wprintf(L"press Enter to apply hook\n");
                getchar();
                SubtreeHook(Handle, IdTable, N, &Dispatch);

                wprintf(L"press Enter to release hook\n");
                getchar();
                SubtreeUnhook(Handle, IdTable, N, &Dispatch);

                wprintf(L"0: terminate; 1: continue;\n");
                wscanf(L"%u", &Value);
                getchar();
                if (!Value) break;
            }

            SubtreeUnref(Handle, IdTable, N);
        }

        FreeMemory(IdTable);

        CloseHandle(Handle);
    }

    return 0;
}

运行驱动程序,插入 USB 闪存驱动器,运行用户模式应用程序

应用挂钩,安全移除 USB 闪存驱动器,释放挂钩

继续,应用挂钩,拔出 USB 闪存驱动器,释放挂钩

终止用户模式应用程序并关闭服务。

© . All rights reserved.