中断描述符表寄存器(Interrupt Descriptor Table Register, IDTR)
中断描述符表(Interrupt Descriptor Table, IDT)
中断描述符表寄存器(Interrupt Descriptor Table Register, IDTR)存储了中断描述符表(Interrupt Descriptor Table, IDT)在内存中的基地址.
IDT是一个有256个入口的线形表,每个IDT的入口是个8字节的描述符,所以整个IDT表的大小为256*8=2048 bytes.
每个中断向量关联了一个中断处理过程。所谓的中断向量就是把每个中断或者异常用一个0-255的数字识别。
另外, 每个处理器拥有自己的IDTR寄存器, 所以都拥有自己的中断表, 因此, 在Hook IDT的时候, 要考虑这个问题.
我们可以使用KeGetCurrentProcessorNumber之类的函数来判断代码当前在哪个处理器上运行. 也可以使用KeSetTargetProcessorDpc等函数将代码调度到某个特定处理器上运行.
当中断发生时, 可以从中断指令或可编程中断控制器中获取中断编号, 然后通过IDT寻找要调用的中断服务例程.
IDT有三种不同的描述符或者说是入口,分别是:
- 任务门描述符
- 中断门描述符
陷阱门描述符
其中, 陷阱门和中断门非常相似, 他们区别只在于陷阱门能够被可屏蔽中断所中断, 而中断门不能. 另一方面, 任务门是一个非常过时的处理器特性, 它可以用于强制x86处理器的任务切换. 然而, Windows并不使用该特性, 所以我们也不加过多的研究.
为了获得IDT的内存地址, 必须读取IDTR, 这可以用SIDT指令完成. 当然, 我们也可以用LIDT指令来修改IDTR的内容.
SIDT指令以如下格式存储IDTR的内容:
typedef struct
{
unsigned short IDTLimit; //代表IDT的大小, 为7FFH
unsigned short LowIDTbase;
unsigned short HiIDTbase;
}IDTINFO;
其中的LowIDTbase和HiIDTbase分别指示了IDT地址的低半部分和高半部分.
IDT中每项的结构如下:
#pragma pack(1)
typedef struct
{
unsigned short LowOffset;
unsigned short selector;
unsigned char unused_lo;
unsigned char segment_type:4;
unsigned char system_segment_flag:1;
unsigned char DPL:2;
unsigned char P:1;
unsigned short HiOffect;
}IDTENTRY;
#pragma pack()
下面是一个小程序, 用来打印全部的ISR Interrupt Service Routines(中断服务程序)
#include <ntddk.h>
#include <stdio.h>
typedef struct
{
unsigned short IDTLimit; //代表IDT的大小, 为7FFH
unsigned short LowIDTbase;
unsigned short HiIDTbase;
}IDTINFO;
#pragma pack(1)
typedef struct
{
unsigned short LowOffset;
unsigned short selector;
unsigned char unused_lo;
unsigned char segment_type:4;
unsigned char system_segment_flag:1;
unsigned char DPL:2;
unsigned char P:1;
unsigned short HiOffect;
}IDTENTRY;
#pragma pack()
#define MAKELONG(a, b) \
((unsigned long) (((unsigned short) (a)) | ((unsigned long) ((unsigned short) (b))) << 16))
//IDT中的最大项数是256
#define MAX_IDT_ENTRIES 0xFF
VOID
DriverUnload(IN PDRIVER_OBJECT pDriverObj)
{
KdPrint(("[IDT] Unloading...\r\n"));
}
NTSTATUS
DriverEntry(IN PDRIVER_OBJECT pDriverObj, IN PUNICODE_STRING pRegistryString)
{
NTSTATUS status = STATUS_SUCCESS;
IDTINFO idt_info;
IDTENTRY* idt_entrys;
unsigned long count;
__asm sidt idt_info;
idt_entrys = (IDTENTRY*)MAKELONG(idt_info.LowIDTbase, idt_info.HiIDTbase);
KdPrint(("IDT Addr: 0x%08X\n", MAKELONG(idt_info.LowIDTbase, idt_info.HiIDTbase)));
KdPrint(("IDT Limit: %d\n", idt_info.IDTLimit));
for (count = 0; count<=MAX_IDT_ENTRIES; count++)
{
char _t[255];
unsigned long addr;
IDTENTRY* i = &idt_entrys[count];
addr = MAKELONG(i->LowOffset, i->HiOffect);
_snprintf(_t, 253, "Interrupt %3d\tISR 0x%08X", count, addr);
KdPrint(("%s", _t));
}
pDriverObj->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
打印结果如下: