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

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

2003 年 7 月 31 日

GPL3

3分钟阅读

viewsIcon

334848

downloadIcon

6247

本文向您展示如何钩取中断,以及如何从注册表中获取设备资源信息

引言

您好,这是我的第一篇文章,英语不是我的母语。所以,首先请原谅我糟糕的语言。本文是关于钩取软件/硬件中断的,并且还将向您展示如何从注册表中检索设备资源信息。我假设您对驱动程序编码有一些基本的了解!

背景/工作原理

好的,现在我将解释中断钩取的工作原理。当发生中断(软件或硬件)时,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 上发布了这篇文章
© . All rights reserved.