Windows软件调试初探-中断和异常管理

中断描述符表

保护模式下有中断或异常发生时,CPU通过中断描述符表IDT寻找处理函数。

启动时0号处理器称为BSP,其他的称为AP。

在BSP中,IDT最初建立和初始化工作在Windows加载程序WinLoad在实模式下完成。准备好一个内存块后加载程序执行CLI指令关闭中断处理,用LIDT指令将IDT位置和长度信息加载到CPU,加载程序将CPU从实模式切换到保护模式,将执行权移交给NT内核入口函数KiSystemStartup。接下来,啮合处理器初始化函数通过SIDT指令取得IDT信息,对其调整后作为参数传给KiInitializePcr,后者将其记录到PCR和PRCB中。

对于每个AP,KeStartAllProcessors为其建立一个单独的处理器状态区,包括其IDT,再用KiInitProcessor,后者根据启动CPU的IDT为要初始化的AP复制一份。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
3: kd> !pcr
KPCR for Processor 3 at ffff968074fad000: //KPCR线性内存地址
Major 1 Minor 1 //KPCR主版本号和子版本号
NtTib.ExceptionList: ffff968074fbcfb0 //异常处理注册链表
NtTib.StackBase: ffff968074fbb000
NtTib.StackLimit: 000000000009e5b8
NtTib.SubSystemTib: ffff968074fad000
NtTib.Version: 0000000074fad180
NtTib.UserPointer: ffff968074fad870
NtTib.SelfTib: 00000000003ac000

SelfPcr: 0000000000000000 //本结构起始地址
Prcb: ffff968074fad180 //KPRCB地址
Irql: 0000000000000000 //IRQL
IRR: 0000000000000000
IDR: 0000000000000000
InterruptMode: 0000000000000000
IDT: 0000000000000000 //IDT基地址
GDT: 0000000000000000 //GDT基地址
TSS: 0000000000000000 //TSS地址

CurrentThread: ffff968074fb8140 //当前正执行的线程 ETHREAD地址
NextThread: 0000000000000000 //下一个准备执行的线程
IdleThread: ffff968074fb8140 //IDLE线程的ETHREAD地址

DpcQueue: Unable to read nt!_KDPC_DATA.DpcListHead.Flink @ ffff968074fb0240
1: kd> dt nt!_KPCR ffff968074fad000
+0x000 NtTib : _NT_TIB
+0x000 GdtBase : 0xffff9680`74fbcfb0 _KGDTENTRY64
+0x008 TssBase : 0xffff9680`74fbb000 _KTSS64
+0x010 UserRsp : 0x9e5b8
+0x018 Self : 0xffff9680`74fad000 _KPCR
+0x020 CurrentPrcb : 0xffff9680`74fad180 _KPRCB
+0x028 LockArray : 0xffff9680`74fad870 _KSPIN_LOCK_QUEUE
+0x030 Used_Self : 0x00000059`5a425000 Void
+0x038 IdtBase : 0xffff9680`74fba000 _KIDTENTRY64
+0x040 Unused : [2] 0
+0x050 Irql : 0 ''
+0x051 SecondLevelCacheAssociativity : 0xc ''
+0x052 ObsoleteNumber : 0x3 ''
+0x053 Fill0 : 0 ''
+0x054 Unused0 : [3] 0
+0x060 MajorVersion : 1
+0x062 MinorVersion : 1
+0x064 StallScaleFactor : 0x973
+0x068 Unused1 : [3] (null)
+0x080 KernelReserved : [15] 0
+0x0bc SecondLevelCacheSize : 0x2400000
+0x0c0 HalReserved : [16] 0x90321000
+0x100 Unused2 : 0
+0x108 KdVersionBlock : (null)
+0x110 Unused3 : (null)
+0x118 PcrAlign1 : [24] 0
+0x180 Prcb : _KPRCB

IDT每一项为一个门描述符结构,IDT包含3种门描述符:任务门、中断门、陷阱门。

任务门用于任务切换,里面包含用于选择任务状态段TSS的段选择子。中断门用于描述中断处理例程入口。陷阱门用于描述异常处理例程的入口。

列出IDT中每个项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1: kd> !idt -a

Dumping IDT: ffff968074e74000

00: fffff80732bffc00 nt!KiDivideErrorFault
01: fffff80732bfff40 nt!KiDebugTrapOrFault Stack = 0xFFFF968074ECE000
02: fffff80732c00440 nt!KiNmiInterrupt Stack = 0xFFFF968074EE3000
03: fffff80732c00900 nt!KiBreakpointTrap
04: fffff80732c00c40 nt!KiOverflowTrap
05: fffff80732c00f80 nt!KiBoundFault
06: fffff80732c014c0 nt!KiInvalidOpcodeFault
07: fffff80732c019c0 nt!KiNpxNotAvailableFault
08: fffff80732c01cc0 nt!KiDoubleFaultAbort Stack = 0xFFFF968074ED5000
09: fffff80732c01fc0 nt!KiNpxSegmentOverrunAbort
0a: fffff80732c022c0 nt!KiInvalidTssFault
0b: fffff80732c025c0 nt!KiSegmentNotPresentFault
0c: fffff80732c02980 nt!KiStackFault
0d: fffff80732c02cc0 nt!KiGeneralProtectionFault
0e: fffff80732c03000 nt!KiPageFault
0f: fffff80732bf7a48 nt!KiIsrThunk+0x78
10: fffff80732c03640 nt!KiFloatingErrorFault
11: fffff80732c03a00 nt!KiAlignmentFault
12: fffff80732c03d40 nt!KiMcheckAbort Stack = 0xFFFF968074EDC000
13: fffff80732c04840 nt!KiXmmException
14: fffff80732c04c00 nt!KiVirtualizationException
15: fffff80732c05100 nt!KiControlProtectionFault
16: fffff80732bf7a80 nt!KiIsrThunk+0xB0
...

异常的描述与登记

RaiseException产生的异常称为CPU异常或硬件异常,用编程语言的throw关键字称为软件异常。这两类异常的描述和分发是统一的,用EXCEPTION_RECORD结构描述异常:

1
2
3
4
5
6
7
8
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode; //异常代码
DWORD ExceptionFlags; //异常标志
struct _EXCEPTION_RECORD* ExceptionRecord; //相关的另一个异常
PVOID ExceptionAddress; //异常发生地址
DWORD NumberParameters; //参数数组中元素个数
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; //参数数组
} EXCEPTION_RECORD;

太抽象了不学了。