KINTERRUPT

The KINTERRUPT structure (formally _KINTERRUPT) is the supporting data for the kernel’s distribution of a hardware interrupt to device drivers that register for the interrupt’s handling. Each processor has its own Interrupt Descriptor Table (IDT) of addresses to divert execution to. A given interrupt vector can therefore be handled differently on different processors. Drivers do not themselves provide the interrupt handler. They instead provide one or another sort of Interrupt Service Routine (ISR) that the kernel is to call from the true interrupt handler. The ISR and various parameters related to its calling are modelled by the KINTERRUPT. There is at least one KINTERRUPT for each processor that the interrupt is prepared for. That there can be more than one is because the kernel provides that its handler can call multiple ISRs, typically each from a different driver that indicates its willingness to share the interrupt’s handling.

Drivers register their interrupt handling through the I/O Manager’s IoConnectInterrupt function or, more recently, IoConnectInterruptEx. These allow the driver to specify multiple processors for the interrupt. They present the driver with apparently one KINTERRUPT structure but it is in a larger allocation with other data, which can include additional KINTERRUPT structures or at least pointers to them in their separate allocations. This note is not concerned with this larger-scale representation, just with the KINTERRUPT as representing the Core Kernel’s part of one interrupt’s handling for one processor.

Accessibility

On modern versions of Windows, the KINTERRUPT object that governs the handling of an interrupt vector for any one processor is relatively accessible from the processor’s KPRCB. In version 6.3 and higher, the x86 KPRCB has a member named VectorToInterruptObject which is formally an array of 0xD0 pointers to KINTERRUPT objects for the possible hardware interrupts numbered 0x30 to 0xFF. The x64 KPRCB does not have a similar array until version 10.0 and it is inevitably named differently, as InterruptObject. Formally, it is an array of pointers to void, but each pointer is indeed the address of a KINTERRUPT. Less of  a formality is that the x64 array has 0x0100 pointers for the whole range of interrupt vectors 0x00 to 0xFF.

Through much of the history of Windows, however, KINTERRUPT objects were much harder to locate. The only known way, as used for instance by the debugger extension command !idt, depends on knowing that connecting a hardware interrupt diverts the corresponding IDT entry to an interrupt handler that is inside the KINTERUPT object. Yes, even as late as 64-bit Windows 8.1, the KINTERRUPT is not just a data structure but contains executable code. This is more easily presented after the layout, below.

Documentation Status

The KINTERRUPT structure is documented only as an opaque object. Drivers obtain one by calling some such documented function as IoConnectInterrupt. They are then given its address as an argument to their Interrupt Service Routine and they may pass this address to other documented functions such as KeSynchronizeExecution and KeAcquireInterruptSpinLock. They may eventually release their access to the KINTERRUPT by calling some such documented function as IoDisconnectInterrupt. All the while, the KINTERRUPT is something whose existence they know of and which is theirs, in some sense, but it is not for their interpretation.

Variability

As an opaque structure that is allocated to drivers by the kernel, the KINTERRUPT has no compatibility constraints even for its size. The KINTERRUPT is, however, shared with the HAL and so it varies less within versions than do many other structures whose internal detail is undocumented. The following changes of size are known:

Version Size (x86) Size (x64)
3.10 to 3.5 1 0x01DC  
4.0 to 5.2 0x01E4 0x80
6.0 0x0270 0xA0
6.1 0x0278 0xA0
6.2 0x02A0 0xB0
6.3 0xA8 0x0100
10.0 to 1903 0xB0 0x0100
2004 0xD0 0x0120

Layout

These sizes, and the offsets, types and names in the table that follows, are from type information in public symbol files for the kernel, starting with Windows 2000 SP3. For earlier versions, Microsoft’s names and types are something of a guess from comparing different versions of the binaries. Where use of a member corresponds closely with that of a version for which type information is available in Microsoft’s symbol files, it seems reasonable to infer continuity.

As with other kernel objects, the KINTERRUPT begins with a Type from the KOBJECTS enumeration. For a KINTERRUPT, the Type is specifically InterruptObject. Note that the numerical value of this Type took a few versions to settle on 0x16. The Size is in bytes.

Offset (x86) Offset (x64) Definition Versions Remarks
0x00 0x00
SHORT Type;
all  
0x02 0x02
SHORT Size;
all  
0x04 0x08
LIST_ENTRY InterruptListEntry;
all  
0x0C 0x18
KSERVICE_ROUTINE *ServiceRoutine;
all  
0x10 0x20
KMESSAGE_SERVICE_ROUTINE *MessageServiceRoutine;
6.0 and higher  
0x14 0x28
ULONG MessageIndex;
6.0 and higher  
0x10 (3.10 to 5.2);
0x18
0x20 (5.2);
0x30
PVOID ServiceContext;
all  
0x14 (3.10 to 5.2);
0x1C
0x28 (5.2);
0x38
KSPIN_LOCK SpinLock;
all  
0x18 (4.0 to 5.2);
0x20
 
ULONG Spare1;
4.0 to 5.0  
0x30 (5.2);
0x40
ULONG TickCount;
5.1 and higher  
0x18 (3.10 to 3.51);
0x1C (4.0 to 5.2);
0x24
0x38 (5.2);
0x48
KSPIN_LOCK *ActualLock;
all  
0x1C (3.10 to 3.51);
0x20 (4.0 to 5.2);
0x28
0x40 (5.2);
0x50
VOID (*DispatchAddress) (VOID);
all  
0x20 (3.10 to 3.51);
0x24 (4.0 to 5.2);
0x2C
0x48 (5.2);
0x58
ULONG Vector;
all  
0x24 (3.10 to 3.51);
0x28 (4.0 to 5.2);
0x30
0x4C (5.2);
0x5C
KIRQL Irql;
all  
0x25 (3.10 to 3.51);
0x29 (4.0 to 5.2);
0x31
0x4D (5.2);
0x5D
KIRQL SynchronizeIrql;
all  
0x26 (3.10 to 3.51);
0x2A (4.0 to 5.2);
0x32
0x4E (5.2);
0x5E
BOOLEAN FloatingSave;
all  
0x27 (3.10 to 3.51);
0x2B (4.0 to 5.2);
0x33
0x4F (5.2);
0x5F
BOOLEAN Connected;
all  
0x28 (3.10 to 3.51);
0x2C (4.0 to 5.2);
0x34
0x50 (5.2);
0x60
CHAR Number;
3.10 to 6.0  
ULONG Number;
6.1 and higher  
0x2D (4.0 to 5.2);
0x35 (6.0);
0x38
0x51 (5.2);
0x61 (6.0);
0x64
BOOLEAN  ShareVector;
4.0 and higher previously 0x30
0x39 (6.1) 0x65 (6.1)
CHAR Pad [3];
6.1 only  
0x39 0x65
BOOLEAN EmulateActiveBoth;
6.3 and higher  
0x3A 0x66
USHORT ActiveCount;
6.2 and higher  
0x3C 0x68
LONG InternalState;
6.2 and higher  
0x2C (3.10 to 3.51);
0x30 (4.0 to 5.2);
0x38 (6.0);
0x3C (6.1);
0x40
 
INT Mode;
3.10 to 5.0  
0x54 (5.2);
0x64 (6.0);
0x68 (6.1);
0x6C
KINTERRUPT_MODE Mode;
5.1 and higher  
0x30 (3.10 to 3.51)  
BOOLEAN ShareVector;
3.10 to 3.51 next at 0x2D
0x3C (6.0);
0x40 (6.1);
0x44
0x68 (6.0);
0x6C (6.1);
0x70
KINTERRUPT_POLARITY Polarity;
6.0 and higher  
0x34 (4.0 to 5.2);
0x40 (6.0);
0x44 (6.1);
0x48
 
ULONG Spare2;
4.0 only  
0x58 (5.2);
0x6C (6.0);
0x70 (6.1);
0x74
ULONG ServiceCount;
5.0 and higher  
0x38 (4.0 to 5.2);
0x44 (6.0);
0x48 (6.1);
0x4C
 
ULONG Spare3;
5.0 only  
0x5C (5.2);
0x70 (6.0);
0x74 (6.1);
0x78
ULONG DispatchCount;
5.1 and higher  
0x48 (6.0);
0x50 (6.1)
0x78 (6.0 to 6.1)
ULONGLONG Rsvd1;
6.0 to 6.1  
0x50 0x80
KEVENT *PassiveEvent;
6.2 and higher  
  0x60 (5.2);
0x80 (6.0 to 6.1);
0x88
KTRAP_FRAME *TrapFrame;
5.2 and higher  
  0x68 (5.2);
0x88 (6.0 to 6.1)
PVOID Reserved;
5.2 to 6.1  
0x34 (3.10 to 3.51);
0x3C (4.0 to 5.2);
0x50 (6.0);
0x58 (6.1);
0x54 (6.2)
0x70 (5.2);
0x90 (6.0 to 6.3)
ULONG DispatchCode [DISPATCH_LENGTH];
3.10 to 6.2 (x86);
5.2 to 6.3 (x64)
 
0x0298 (6.2);
0x54
0xA0 (6.2 to 6.3);
0x90
PVOID DisconnectData;
6.2 and higher  
0x029C (6.2);
0x58
0xA8 (6.2 to 6.3);
0x98
KTHREAD * volatile ServiceThread;
6.2 and higher  
0x5C 0xA0
INTERRUPT_CONNECTION_DATA *ConnectionData;
10.0 and higher previously 0xA0 and 0xF0
0x60 0xA8
PVOID IntTrackEntry;
10.0 and higher  
0x60 (6.3);
0x68
0xB0
ISRDPCSTATS IsrDpcStats;
6.3 and higher  
0xA0 (6.3) 0xF0 (6.3)
INTERRUPT_CONNECTION_DATA *ConnectionData;
6.3 only next at 0x5C and 0xA0
0xA8 (10.0 to 1903);
0xC8
0xF0 (10.0 to 1903);
0x0110
PVOID RedirectObject;
10.0 and higher  
  0xF8 (6.3 to 1903)
UCHAR Padding [8];
6.3 to 1903  
0xCC 0x0118
PVOID PhysicalDeviceObject;
2004 and higher  

No use is known of the ServiceCount before version 5.0. The name Spare2 is proposed as fitting the sequence frrom Spare1 to Spare3 that is known from later symbol files.

The DispatchCode truly is code. Initialisation of a KINTERRUPT copies code from a template in the kernel. Connection of the KINTERRUPT sets this copy as the interrupt handler whose address is in the IDT entry. Before the WDK for Windows Vista, the NTDDK.H defines the allowed length of this DispatchCode in dwords as DISPATCH_LENGTH. How or why this slipped into the header is not known—the macro is not referenced from any other header—but had its definition continued to be published, its values would be: