在 Windows NT/2000/XP 上进行中断钩取和检索设备信息






4.84/5 (35投票s)
本文向您展示如何钩取中断,以及如何从注册表中获取设备资源信息
引言
您好,这是我的第一篇文章,英语不是我的母语。所以,首先请原谅我糟糕的语言。本文是关于钩取软件/硬件中断的,并且还将向您展示如何从注册表中检索设备资源信息。我假设您对驱动程序编码有一些基本的了解!
背景/工作原理
好的,现在我将解释中断钩取的工作原理。当发生中断(软件或硬件)时,CPU 使用 IDT(中断描述符表)来读取有关如何处理该中断的必要信息。您可以像这样获取表的内存地址
#pragma pack(1) // 2 works, too typedef struct tagIDT { WORD wLimit; DWORD dwBase; } IDT, *PIDT; #pragma pack() VOID LoadIDT( OUT PIDT pIdt ) { __asm { MOV EAX, [pIdt] // load offset into EAX SIDT [EAX] } }
命令 SIDT 将 IDT 信息(包括 IDT 基址和表的大小(限制))保存到指定的内存地址。
现在您可以从表中读取单个描述符。以下是它们的结构
#pragma pack(1) typedef struct tagINT_VECTOR { WORD wLowOffset; // LOWORD of the handler's offset WORD wSelector; // selector of the handler's offset BYTE bAccess; // 0-3: Type // 4: ?(=0) // 5-6: DPL // 7: Present BYTE wUnused; // 0, 0, 0, unused (binary) WORD wHighOffset; // HIWORD of the handler's offset } INT_VECTOR, *PINT_VECTOR; #pragma pack()
以下是加载/保存描述符的函数
VOID LoadINTVector( IN PIDT pIdt, IN UCHAR iVector, OUT PINT_VECTOR pVector ) { __try { DWORD dwBase = pIdt->dwBase + iVector * sizeof(INT_VECTOR); memcpy( pVector, (const void *)dwBase, sizeof(INT_VECTOR) ); } __except( 1 ) { DPRINT( "LoadINTVector: Exception\n" ); } DPRINT( "LoadINTVector: Vector 0x%.2X successfully dumped\n", iVector ); } VOID SaveINTVector( IN PIDT pIdt, IN UCHAR iVector, IN PINT_VECTOR pVector ) { __try { DWORD dwBase = pIdt->dwBase + iVector * sizeof(INT_VECTOR); __asm{ PUSHFD }; __asm{ CLI }; // IMPORTANT: clear interrupt flag!!! memcpy( (void *)dwBase, pVector, sizeof(INT_VECTOR) ); __asm{ POPFD }; } __except( 1 ) { DPRINT( "SaveINTVector: Exception\n" ); } DPRINT( "SaveINTVector: Vector 0x%.2X successfully set\n", iVector ); }
内置 ioctl 代码
IOCTL_HOOK_INT
仅钩取一个指定的中断。- 参数:
IN UCHAR iVector
- 参数:
IOCTL_UNHOOK_INT
仅取消钩取一个指定的中断。- 参数:
IN UCHAR iVector
- 参数:
IOCTL_HOOK_ALL_INT
钩取所有有用的中断。- 参数:无
IOCTL_DUMP_IDT
转储整个 IDT 表。- 参数:
OUT IDT_ENTRY Array[256]
- 参数:
IOCTL_GET_INT_COUNTS
检索中断调用计数。- 参数:
OUT __int64 Array[256]
- 参数:
IOCTL_GET_START_TIME
检索调用IOCTL_HOOK_ALL_INT
的时间(PerformanceCounter)。- 参数:OUT __int64 Time
要调用这些 ioctl 代码,请像这样打开驱动程序
DWORD dwOut; HANDLE hDriver = CreateFile( \\\\.\\InterruptHook, GENERIC_READ |GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); DeviceIoControl( hDriver, IOCTL_X, inbuffer, inbufferlen, outbuffer, outbufferlen, &dwRet, NULL );
从注册表中检索设备信息
好的,这很简单。请查看注册表的 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum!在那里您将找到几个子项,如“ACPI”或“STORAGE”。这些子项有进一步的子项,它们有点像类,我不确切知道它们的作用。它们也有子项,代表设备驱动程序!它们的子项“Control”可能包含一个名为“AllocConfig”的值(类型 REG_RESOURCE_LIST
)。REG_RESOURCE_LIST
未由微软记录!
它是在 ntddk.h 中定义的结构体
typedef struct _CM_RESOURCE_LIST { ULONG Count; CM_FULL_RESOURCE_DESCRIPTOR List[1]; } CM_RESOURCE_LIST, *PCM_RESOURCE_LIST; typedef struct _CM_FULL_RESOURCE_DESCRIPTOR { INTERFACE_TYPE InterfaceType; ULONG BusNumber; CM_PARTIAL_RESOURCE_LIST PartialResourceList; } CM_FULL_RESOURCE_DESCRIPTOR, *PCM_FULL_RESOURCE_DESCRIPTOR; typedef struct _CM_PARTIAL_RESOURCE_LIST { USHORT Version; USHORT Revision; ULONG Count; CM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptors[1]; } CM_PARTIAL_RESOURCE_LIST, *PCM_PARTIAL_RESOURCE_LIST; typedef struct _CM_PARTIAL_RESOURCE_DESCRIPTOR { UCHAR Type; UCHAR ShareDisposition; USHORT Flags; union { struct { PHYSICAL_ADDRESS Start; ULONG Length; } Generic; struct { PHYSICAL_ADDRESS Start; ULONG Length; } Port; struct { ULONG Level; ULONG Vector; ULONG Affinity; } Interrupt; struct { PHYSICAL_ADDRESS Start; ULONG Length; } Memory; struct { ULONG Channel; ULONG Port; ULONG Reserved1; } Dma; struct { ULONG Data[3]; } DevicePrivate; struct { ULONG Start; ULONG Length; ULONG Reserved; } BusNumber; struct { ULONG DataSize; ULONG Reserved1; ULONG Reserved2; } DeviceSpecificData; } u; } CM_PARTIAL_RESOURCE_DESCRIPTOR, *PCM_PARTIAL_RESOURCE_DESCRIPTOR;
您可以在 此处 找到这些结构的文档。
关于 GUI
GUI 只是每 50 毫秒刷新一次中断调用计数并显示它。CPS 表示每秒调用次数!从列表中选择 IRQ 条目时,打开上下文菜单!
关于编译的信息
- GUI 可以由 Visual Studio 6.0 或更高版本编译。
- 对于驱动程序,您将需要 Windows NT(或更高版本)DDK(从 Microsoft 订购)。
要编译它,请使用 DDK 构建环境,切换到源目录并键入 "build -cZ"。 如果您没有 DDK,请使用来自二进制 zip 文件的已编译驱动程序。这里对 Visual Studio 6.0 C++ 编译器/预处理器有一些评论
在编译我的软件时,编译器似乎错误地解释了 CTL_CODE
宏,所以我不得不使用十六进制值而不是宏。
已知问题
- 在钩取中断后,我的软盘不再工作(重启解决了问题)
- 在钩取 Windows 过程时,Spy++ 最终出现蓝屏(重启...)
- 请向我发送有关其他问题的反馈!!!
免责声明
请自行承担使用此软件的风险!!!我不知道其他计算机会发生什么!对于可能造成的损害,不提供任何保证!!!
历史
- 2003 年 7 月 31 日 - 在 CodeProject 上发布了这篇文章