Geoff Chappell, Software Analyst
The KTRAP_FRAME (formally a _KTRAP_FRAME) is a structure in which the kernel saves the state of execution that gets interrupted for diversion to the kernel, whether from external hardware, the processor itself (for a trap or fault) or by software executing an int or syscall instruction. The KTRAP_FRAME is highly specific to the processor architecture. This page concerns itself only with 64-bit Windows for the processor architecture that’s variously named amd64 or x64. The x86 KTRAP_FRAME is presented separately.
Because of the structure’s role in all ways in and out of the kernel, the KTRAP_FRAME may be the best known of all formally undocumented kernel-mode structures. Indeed, this article exists only for the occasional convenience of having the names and offsets for ready reckoning (and, for the historian, of tracking the strikingly few changes).
Though the KTRAP_FRAME structure is not formally documented, the structure’s x64 implementation has a C-language definition in NTDDK.H from every applicable Device Driver Kit (DDK) or Windows Driver Kit (WDK).
The KTRAP_FRAME for 64-bit Windows is 0x0190 bytes in all known versions. Names, types and offsets in the following are from NTDDK.H in various driver kits, checked against the kernel’s symbol files.
Offset (x64) | Definition | Versions |
---|---|---|
0x00 |
ULONG64 P1Home; |
all |
0x08 |
ULONG64 P2Home; |
all |
0x10 |
ULONG64 P3Home; |
all |
0x18 |
ULONG64 P4Home; |
all |
0x20 |
ULONG64 P5; |
all |
0x28 |
KPROCESSOR_MODE PreviousMode; |
all |
0x29 |
KIRQL PreviousIrql; |
all |
0x2A |
UCHAR FaultIndicator; |
all |
0x2B |
UCHAR ExceptionActive; |
all |
0x2C |
ULONG MxCsr; |
all |
0x30 |
ULONG64 Rax; |
all |
0x38 |
ULONG64 Rcx; |
all |
0x40 |
ULONG64 Rdx; |
all |
0x48 |
ULONG64 R8; |
all |
0x50 |
ULONG64 R9; |
all |
0x58 |
ULONG64 R10; |
all |
0x60 |
ULONG64 R11; |
all |
0x68 |
union { ULONG64 GsBase; ULONG64 GsSwap; }; |
all |
0x70 |
M128A Xmm0; |
all |
0x80 |
M128A Xmm1; |
all |
0x90 |
M128A Xmm2; |
all |
0xA0 |
M128A Xmm3; |
all |
0xB0 |
M128A Xmm4; |
all |
0xC0 |
M128A Xmm5; |
all |
0xD0 |
union { ULONG64 FaultAddress; ULONG64 ContextRecord; ULONG64 TimeStamp; }; |
5.2 only |
union { ULONG64 FaultAddress; ULONG64 ContextRecord; ULONG64 TimeStampCKCL; }; |
6.0 to 1607 | |
union { ULONG64 FaultAddress; ULONG64 ContextRecord; }; |
1703 and higher | |
0xD8 |
ULONG64 Dr0; |
all |
0xE0 |
ULONG64 Dr1; |
all |
0xE8 |
ULONG64 Dr2; |
all |
0xF0 |
ULONG64 Dr3; |
all |
0xF8 |
ULONG64 Dr6; |
all |
0x0100 |
ULONG64 Dr7; |
all |
0x0108 |
union { struct { ULONG64 DebugControl; ULONG64 LastBranchToRip; ULONG64 LastBranchFromRip; ULONG64 LastExceptionToRip; ULONG64 LastExceptionFromRip; }; struct { ULONG64 LastBranchControl; ULONG LastBranchMSR; }; }; |
5.2 to 6.3 |
struct { ULONG64 DebugControl; ULONG64 LastBranchToRip; ULONG64 LastBranchFromRip; ULONG64 LastExceptionToRip; ULONG64 LastExceptionFromRip; }; |
10.0 and higher | |
0x0130 |
USHORT SegDs; |
all |
0x0132 |
USHORT SegEs; |
all |
0x0134 |
USHORT SegFs; |
all |
0x0136 |
USHORT SegGs; |
all |
0x0138 |
ULONG64 TrapFrame; |
all |
0x0140 |
ULONG64 Rbx; |
all |
0x0148 |
ULONG64 Rdi; |
all |
0x0150 |
ULONG64 Rsi; |
all |
0x0158 |
ULONG64 Rbp; |
all |
0x0160 |
union { ULONG64 ErrorCode; ULONG64 ExceptionFrame; }; |
5.2 only |
union { ULONG64 ErrorCode; ULONG64 ExceptionFrame; ULONG64 TimeStampKlog; }; |
6.0 to 1607 | |
union { ULONG64 ErrorCode; ULONG64 ExceptionFrame; }; |
1703 and higher | |
0x0168 |
ULONG64 Rip; |
all |
0x0170 |
USHORT SegCs; |
all |
0x0172 |
USHORT Fill1 [3]; |
5.2 only |
UCHAR Fill0; |
6.0 and higher | |
0x0173 |
UCHAR Logging; |
6.0 and higher |
0x0174 |
USHORT Fill1 [2]; |
6.0 and higher |
0x0178 |
ULONG EFlags; |
all |
0x017C |
ULONG Fill2; |
all |
0x0180 |
ULONG64 Rsp; |
all |
0x0188 |
USHORT SegSs; |
all |
0x018A |
USHORT Fill3 [1]; |
5.2 only |
USHORT Fill3; |
6.0 and higher | |
0x018C |
LONG CodePatchCycle; |
5.2 to 6.2 |
ULONG Fill4; |
6.3 and higher |
The KTRAP_FRAME ends—from offset 0x0160 onwards—with items that the processor itself either does or may put on the stack before the kernel sees the interrupt. The structure’s formal layout has always provided that the kernel may squeeze in items of its own where the processor allows 8 bytes though only 16 or 32 bits are meaningful.
Note that the structure does not provide for saving all the general registers. The kernel does not itself use r12 to r15 during entry or exit, and does not save them in anticipation that code deeper into the handling may use them. The understanding is instead that all kernel-mode routines that use r12 to r15 (or xmm6 or xmm7) will do the saving and restoring themselves. In practice, of course, this knowledge is built into the compiler.