Joe1sn's Cabinet

windows内核驱动 5-中断级与自旋锁

顺便复习操作系统了

WinOS 相关原理

中断级

如果有时间的话可以试着玩一玩这个游戏:https://github.com/plbrault/youre-the-os
在线:https://plbrault.github.io/youre-the-os/
或许在游玩的过程中你会自己总结出一套操作系统进程调度的一套方法,便于其他方法的理解

  • 调度方式:抢占式

  • 最小执行单元:纤程->线程->进程

  • 中断级( Irql ) 0->2级别越来越高,高级别可以打断低级别

    • 0:Pass level

    • 1:Apc level

    • 2: Dpc level

      ISR延迟调用,硬件中断后,不那么紧急的任务放在DPC队列中

      DPC访问换页内存,页面换到磁盘中pagefile.sys,引起换页缺页中断,如果换页中断无法打断DPC,然后就会访问无效地址,造成BSOD,所以DPC中最好不要使用换页内存,也即要使用nonpagedpool,而不要使用paged pool

    • hardware(io…)

    • 强制打断:ipicall


自旋锁

有一间厕所,A进去后就锁上了厕所的门。B和C的其他就只有在门外排队等待

应用场景:

  1. 操作危险数据:全局变量等(可以参考SQL)
  2. 可重入代码

编程相关

中断级

获取自身中断级别:KeGetCurrentIrql()

升级到DPC:KeRaiseIrqlToDpcLevel

降级:KeLowerIrql

1
2
3
4
5
6
DbgPrint("Current Irql %d\nNow Try Raise to DPC\n", KeGetCurrentIrql());
KIRQL irql = KeGetCurrentIrql();
irql = KeRaiseIrqlToDpcLevel();
DbgPrint("After, Current Irql %d\nNow Try decrease to Pass\n", KeGetCurrentIrql());
KeLowerIrql(irql);
DbgPrint("End, Current Irql %d\n", KeGetCurrentIrql());

image-20240319120411828

将线程保持在DPC会导致蓝屏

应用:希望函数在该线程执行中:1.不被中断,2:相关目标不被执行或者修改等
MSDN中有每个内核函数的中断级

创建DPC线程

1
2
3
4
5
6
7
8
VOID DpcRoutineFunc() {
DbgPrint("Current Irql: %d\n", KeGetCurrentIrql());
}
....
DpcRoutineFunc(NULL);
KDPC kDPC = { 0 };
KeInitializeDpc(&kDPC, (PKDEFERRED_ROUTINE)DpcRoutineFunc, NULL);
KeInsertQueueDpc(&kDPC, NULL, NULL);

image-20240319132207124

DPC中调用ZwOpenFile之类的会BSOD

自旋锁

一般的自旋锁都是全局变量

1
2
3
4
5
6
KeInitializeSpinLock(&kSpinLock);
DbgPrint("SpinLock init\n");
KeAcquireSpinLock(&kSpinLock, &irql);
DbgPrint("SpinLock Locked\n");
KeReleaseSpinLock(&kSpinLock, irql);
DbgPrint("SpinLock Delocked\n");

image-20240319130611722

image-20240319130947718

加锁这个函数会会提升到DPC中,但是DPC不能访问可分页内存,例如Ring3下的PCB中的LDR

视频给出了一个技巧

image-20240319131322729