驱动程序的调度例程挂钩





5.00/5 (4投票s)
本文介绍了如何挂钩驱动程序的派遣例程。
引言
本文所述的驱动程序允许您记录给定设备对象的派遣例程调用(及其相对顺序)。步骤如下:
- 引用设备对象
- 为选定的派遣例程设置挂钩
- 释放挂钩并检索日志信息
- 取消引用设备对象
此外,该驱动程序还允许查看设备堆栈、设备父级和设备子级。
我使用此驱动程序来查看设备子树以及我们在插入/拔出/安全移除/启用/禁用 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 });
}
}
我们引入 **设备堆栈状态** 的概念,可能的值为:
- 设备堆栈(和设备节点)不存在
- 设备堆栈仅包含 PDO
- 设备堆栈包含 PDO 和 FDO
向量 { X, Y } 将表示 { disk, mass storage } 的设备堆栈状态。用户操作标题后的向量表示处理任何请求之前的初始设备堆栈状态。请求标题后的向量表示处理请求后的设备堆栈状态(如果没有向量,则设备堆栈状态未更改)。
用户操作的 Pnp 请求观察顺序
USB 闪存驱动器已插入 { 1, 1 }
{ IRP_MN_QUERY_DEVICE_RELATIONS, BusRelations }
-> hub ->{ 1, 2 }
- Configure -> mass storage ->
{ 1, 3 }
{ IRP_MN_QUERY_DEVICE_RELATIONS, BusRelations }
-> mass storage ->{ 2, 3 }
- Configure -> disk ->
{ 3, 3 }
USB 闪存驱动器已拔出(未经安全移除){ 3, 3 }
{ IRP_MN_QUERY_DEVICE_RELATIONS, BusRelations }
-> hub{ IRP_MN_SURPRISE_REMOVAL }
-> disk{ IRP_MN_SURPRISE_REMOVAL }
-> mass storage{ IRP_MN_REMOVE_DEVICE }
-> disk ->{ 1, 3 }
{ IRP_MN_REMOVE_DEVICE }
-> mass storage ->{ 1, 1 }
USB 闪存驱动器安全移除(从托盘){ 3, 3 }
{ IRP_MN_QUERY_REMOVE_DEVICE }
-> disk{ IRP_MN_QUERY_REMOVE_DEVICE }
-> mass storage{ IRP_MN_REMOVE_DEVICE }
-> disk ->{ 2, 3 }
{ IRP_MN_REMOVE_DEVICE }
-> mass storage ->{ 1, 2 }
USB 闪存驱动器已拔出(安全移除后){ 1, 2 }
{ IRP_MN_QUERY_DEVICE_RELATIONS, BusRelations }
-> hub{ IRP_MN_REMOVE_DEVICE }
-> mass storage ->{ 1, 1 }
磁盘禁用(来自设备管理器){ 3, 3 }
{ IRP_MN_QUERY_REMOVE_DEVICE }
-> disk{ IRP_MN_REMOVE_DEVICE }
-> disk ->{ 2, 3 }
{ IRP_MN_QUERY_REMOVE_DEVICE }
-> disk{ IRP_MN_REMOVE_DEVICE }
-> disk
磁盘启用(来自设备管理器){ 2, 3 }
- Configure -> disk ->
{ 3, 3 }
大容量存储禁用(来自设备管理器){ 3, 3 }
{ IRP_MN_QUERY_REMOVE_DEVICE }
-> disk{ IRP_MN_QUERY_REMOVE_DEVICE }
-> mass storage{ IRP_MN_REMOVE_DEVICE }
-> disk ->{ 2, 3 }
{ IRP_MN_REMOVE_DEVICE }
-> mass storage ->{ 1, 2 }
{ IRP_MN_QUERY_REMOVE_DEVICE }
-> mass storage{ IRP_MN_REMOVE_DEVICE }
-> mass storage
大容量存储启用(来自设备管理器){ 1, 2 }
- Configure -> mass storage ->
{ 1, 3 }
{ IRP_MN_QUERY_DEVICE_RELATIONS, BusRelations }
-> mass storage ->{ 2, 3 }
- Configure -> disk ->
{ 3, 3 }
Using the Code
驱动程序维护两个由自旋锁保护的链接列表
- 存储代表已引用设备对象的项(
DEVICE_INFORMATION
)的列表 - 存储代表已修补驱动程序对象(派遣例程指针已替换)的项(
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 及其处理程序
-
#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; }
-
#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; }
-
#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; }
-
#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; }
-
#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 闪存驱动器,释放挂钩
终止用户模式应用程序并关闭服务。