KeEnterCriticalRegion和KeLeaveCriticalRegion配合使用,能禁止用户模式APC和普通内核模式APC的调用,但是不能禁止特殊内核模式的调用(NormalRoutine为空的内核模式APC)

KeEnterGuardedRegion和KeLeaveGuardedRegion能禁止所有APC调用

KeEnterCriticalRegion会调用KeEnterCriticalRegionThread()函数,再看看KeEnterCriticalRegionThread()的内部实现

FORCEINLINE
VOID
KeEnterCriticalRegionThread (
PKTHREAD Thread
) /*++ Routine Description: This function disables kernel APC's for the current thread. N.B. The following code does not require any interlocks. There are
two cases of interest: 1) On an MP system, the thread cannot
be running on two processors as once, and 2) if the thread is
is interrupted to deliver a kernel mode APC which also calls
this routine, the values read and stored will stack and unstack
properly. Arguments: Thread - Supplies a pointer to the current thread. N.B. This must be a pointer to the current thread. Return Value: None. --*/ { ASSERT(Thread == KeGetCurrentThread()); ASSERT((Thread->KernelApcDisable <= ) && (Thread->KernelApcDisable != -)); Thread->KernelApcDisable -= ;
KeMemoryBarrierWithoutFence();
return;
}

KeEnterGuardRegion会调用KeEnterGuardRegionThread()函数,再看看KeEnterGuardRegionThread()的内部实现

FORCEINLINE
VOID
KeEnterGuardedRegionThread (
IN PKTHREAD Thread
) /*++ Routine Description: This function disables special kernel APC's for the current thread. N.B. The following code does not require any interlocks. There are
two cases of interest: 1) On an MP system, the thread cannot
be running on two processors as once, and 2) if the thread is
is interrupted to deliver a kernel mode APC which also calls
this routine, the values read and stored will stack and unstack
properly. Arguments: Thread - Supplies a pointer to the current thread. N.B. This must be a pointer to the current thread. Return Value: None. --*/ { ASSERT(KeGetCurrentIrql() <= APC_LEVEL); ASSERT(Thread == KeGetCurrentThread()); ASSERT((Thread->SpecialApcDisable <= ) && (Thread->SpecialApcDisable != -)); Thread->SpecialApcDisable -= ;
KeMemoryBarrierWithoutFence();
return;
}

注意两者之间的区别

KeEnterCriticalRegionThread中为

Thread->KernelApcDisable -= 1;

KeEnterGuardRegionThread中为

Thread->SpecialApcDisable -= 1;

再看看这里的改变会对APC的派发有什么影响,查看KiDeliverApc函数代码

VOID
KiDeliverApc (
IN KPROCESSOR_MODE PreviousMode,
IN PKEXCEPTION_FRAME ExceptionFrame,
IN PKTRAP_FRAME TrapFrame
) /*++ Routine Description: This function is called from the APC interrupt code and when one or
more of the APC pending flags are set at system exit and the previous
IRQL is zero. All special kernel APC's are delivered first, followed
by normal kernel APC's if one is not already in progress, and finally
if the user APC queue is not empty, the user APC pending flag is set,
and the previous mode is user, then a user APC is delivered. On entry
to this routine IRQL is set to APC_LEVEL. N.B. The exception frame and trap frame addresses are only guaranteed
to be valid if, and only if, the previous mode is user. Arguments: PreviousMode - Supplies the previous processor mode. ExceptionFrame - Supplies a pointer to an exception frame. TrapFrame - Supplies a pointer to a trap frame. Return Value: None. --*/ { PKAPC Apc;
PKKERNEL_ROUTINE KernelRoutine;
KLOCK_QUEUE_HANDLE LockHandle;
PLIST_ENTRY NextEntry;
PVOID NormalContext;
PKNORMAL_ROUTINE NormalRoutine;
PKTRAP_FRAME OldTrapFrame;
PKPROCESS Process;
PVOID SystemArgument1;
PVOID SystemArgument2;
PKTHREAD Thread; //
// If the thread was interrupted in the middle of the SLIST pop code,
// then back up the PC to the start of the SLIST pop.
// if (TrapFrame != NULL) {
KiCheckForSListAddress(TrapFrame);
} //
// Save the current thread trap frame address and set the thread trap
// frame address to the new trap frame. This will prevent a user mode
// exception from being raised within an APC routine.
// Thread = KeGetCurrentThread();
OldTrapFrame = Thread->TrapFrame;
Thread->TrapFrame = TrapFrame; //
// If special APC are not disabled, then attempt to deliver one or more
// APCs.
// Process = Thread->ApcState.Process;
Thread->ApcState.KernelApcPending = FALSE;
if (Thread->SpecialApcDisable == ) { //
// If the kernel APC queue is not empty, then attempt to deliver a
// kernel APC.
//
// N.B. The following test is not synchronized with the APC insertion
// code. However, when an APC is inserted in the kernel queue of
// a running thread an APC interrupt is requested. Therefore, if
// the following test were to falsely return that the kernel APC
// queue was empty, an APC interrupt would immediately cause this
// code to be executed a second time in which case the kernel APC
// queue would found to contain an entry.
// KeMemoryBarrier();
while (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]) == FALSE) { //
// Raise IRQL to dispatcher level, lock the APC queue, and check
// if any kernel mode APC's can be delivered.
//
// If the kernel APC queue is now empty because of the removal of
// one or more entries, then release the APC lock, and attempt to
// deliver a user APC.
// KeAcquireInStackQueuedSpinLock(&Thread->ApcQueueLock, &LockHandle);
NextEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
if (NextEntry == &Thread->ApcState.ApcListHead[KernelMode]) {
KeReleaseInStackQueuedSpinLock(&LockHandle);
break;
} //
// Clear kernel APC pending, get the address of the APC object,
// and determine the type of APC.
//
// N.B. Kernel APC pending must be cleared each time the kernel
// APC queue is found to be non-empty.
// Thread->ApcState.KernelApcPending = FALSE;
Apc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
ReadForWriteAccess(Apc);
KernelRoutine = Apc->KernelRoutine;
NormalRoutine = Apc->NormalRoutine;
NormalContext = Apc->NormalContext;
SystemArgument1 = Apc->SystemArgument1;
SystemArgument2 = Apc->SystemArgument2;
if (NormalRoutine == (PKNORMAL_ROUTINE)NULL) { //
// First entry in the kernel APC queue is a special kernel APC.
// Remove the entry from the APC queue, set its inserted state
// to FALSE, release dispatcher database lock, and call the kernel
// routine. On return raise IRQL to dispatcher level and lock
// dispatcher database lock.
// RemoveEntryList(NextEntry);
Apc->Inserted = FALSE;
KeReleaseInStackQueuedSpinLock(&LockHandle);
(KernelRoutine)(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2); #if DBG if (KeGetCurrentIrql() != LockHandle.OldIrql) {
KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
KeGetCurrentIrql() << | LockHandle.OldIrql << ,
(ULONG_PTR)KernelRoutine,
(ULONG_PTR)Apc,
(ULONG_PTR)NormalRoutine);
} #endif } else { //
// First entry in the kernel APC queue is a normal kernel APC.
// If there is not a normal kernel APC in progress and kernel
// APC's are not disabled, then remove the entry from the APC
// queue, set its inserted state to FALSE, release the APC queue
// lock, call the specified kernel routine, set kernel APC in
// progress, lower the IRQL to zero, and call the normal kernel
// APC routine. On return raise IRQL to dispatcher level, lock
// the APC queue, and clear kernel APC in progress.
// if ((Thread->ApcState.KernelApcInProgress == FALSE) &&
(Thread->KernelApcDisable == )) { RemoveEntryList(NextEntry);
Apc->Inserted = FALSE;
KeReleaseInStackQueuedSpinLock(&LockHandle);
(KernelRoutine)(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2); #if DBG if (KeGetCurrentIrql() != LockHandle.OldIrql) {
KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
KeGetCurrentIrql() << | LockHandle.OldIrql << | ,
(ULONG_PTR)KernelRoutine,
(ULONG_PTR)Apc,
(ULONG_PTR)NormalRoutine);
} #endif if (NormalRoutine != (PKNORMAL_ROUTINE)NULL) {
Thread->ApcState.KernelApcInProgress = TRUE;
KeLowerIrql();
(NormalRoutine)(NormalContext,
SystemArgument1,
SystemArgument2); KeRaiseIrql(APC_LEVEL, &LockHandle.OldIrql);
} Thread->ApcState.KernelApcInProgress = FALSE; } else {
KeReleaseInStackQueuedSpinLock(&LockHandle);
goto CheckProcess;
}
}
} //
// Kernel APC queue is empty. If the previous mode is user, user APC
// pending is set, and the user APC queue is not empty, then remove
// the first entry from the user APC queue, set its inserted state to
// FALSE, clear user APC pending, release the dispatcher database lock,
// and call the specified kernel routine. If the normal routine address
// is not NULL on return from the kernel routine, then initialize the
// user mode APC context and return. Otherwise, check to determine if
// another user mode APC can be processed.
//
// N.B. There is no race condition associated with checking the APC
// queue outside the APC lock. User APCs are always delivered at
// system exit and never interrupt the execution of the thread
// in the kernel.
// if ((PreviousMode == UserMode) &&
(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]) == FALSE) &&
(Thread->ApcState.UserApcPending != FALSE)) { //
// Raise IRQL to dispatcher level, lock the APC queue, and deliver
// a user mode APC.
// KeAcquireInStackQueuedSpinLock(&Thread->ApcQueueLock, &LockHandle); //
// If the user APC queue is now empty because of the removal of
// one or more entries, then release the APC lock and exit.
// Thread->ApcState.UserApcPending = FALSE;
NextEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
if (NextEntry == &Thread->ApcState.ApcListHead[UserMode]) {
KeReleaseInStackQueuedSpinLock(&LockHandle);
goto CheckProcess;
} Apc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
ReadForWriteAccess(Apc);
KernelRoutine = Apc->KernelRoutine;
NormalRoutine = Apc->NormalRoutine;
NormalContext = Apc->NormalContext;
SystemArgument1 = Apc->SystemArgument1;
SystemArgument2 = Apc->SystemArgument2;
RemoveEntryList(NextEntry);
Apc->Inserted = FALSE;
KeReleaseInStackQueuedSpinLock(&LockHandle);
(KernelRoutine)(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2); if (NormalRoutine == (PKNORMAL_ROUTINE)NULL) {
KeTestAlertThread(UserMode); } else {
KiInitializeUserApc(ExceptionFrame,
TrapFrame,
NormalRoutine,
NormalContext,
SystemArgument1,
SystemArgument2);
}
}
} //
// Check if process was attached during the APC routine.
// CheckProcess:
if (Thread->ApcState.Process != Process) {
KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
(ULONG_PTR)Process,
(ULONG_PTR)Thread->ApcState.Process,
(ULONG)Thread->ApcStateIndex,
(ULONG)KeIsExecutingDpc());
} //
// Restore the previous thread trap frame address.
// Thread->TrapFrame = OldTrapFrame;
return;
}

可以看到,这个函数会判断Thread->SpecialApcDisable 和 Thread->KernelApcSidable 的值,如果Thread->SpecialApcDisable 为0,会先派发特殊内核APC,然后判断Thread->KernelApcDisable是否为0,为0 就去进一步的派发普通内核 Apc和 用户Apc

参考:https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/critical-regions-and-guarded-regions

后续再补充代码实现细节.

Critical Regions和Guarded Regions区别的更多相关文章

  1. Windows内核开发-6-内核机制 Kernel Mechanisms

    Windows内核开发-6-内核机制 Kernel Mechanisms 一部分Windows的内核机制对于驱动开发很有帮助,还有一部分对于内核理解和调试也很有帮助. Interrupt Reques ...

  2. [REP]AWS Regions and Availability Zones: the simplest explanation you will ever find around

    When it comes to Amazon Web Services, there are two concepts that are extremely important and spanni ...

  3. Windows Kernel Security Training Courses

    http://www.codemachine.com/courses.html#kerdbg Windows Kernel Internals for Security Researchers Thi ...

  4. C puzzles详解【51-57题】

    第五十一题 Write a C function which does the addition of two integers without using the '+' operator. You ...

  5. mser 最大稳定极值区域(文字区域定位)算法 附完整C代码

    mser 的全称:Maximally Stable Extremal Regions 第一次听说这个算法时,是来自当时部门的一个同事, 提及到他的项目用它来做文字区域的定位,对这个算法做了一些优化. ...

  6. Hbase学习02

    第2章 Apache HBase配置 本章在“入门”一章中进行了扩展,以进一步解释Apache HBase的配置. 请仔细阅读本章,特别是基本先决条件,确保您的HBase测试和部署顺利进行,并防止数据 ...

  7. fasta/fastq格式解读

    1)知识简介--------------------------------------------------------1.1)测序质量值 首先在了解fastq,fasta之前,了解一下什么是质量 ...

  8. [libgdx游戏开发教程]使用Libgdx进行游戏开发(4)-素材管理

    游戏中总是有大量的图像资源,我们通常的做法是把要用的图片做成图片集,这样做的好处就不多说了.直接来看怎么用. 这里我们使用自己的类Assets来管理它们,让这个工具类作为我们的资源管家,从而可以在任何 ...

  9. 目标检测之线段检测---lsd line segment detector

    (1)线段检测应用背景 (2)线段检测原理简介 (3)线段检测实例 a line segment detector (4)hough 变换和 lsd 的区别 --------------------- ...

随机推荐

  1. SQLServer中数据加密方法

    对SQLServer中的数据进行加密,有三种方法, 1.  在程序语言中先对数据进行加密后再把加密后的数据保存在SQLServer数据库中: 2.  利用SQLServer未公开的加密密码函数,在SQ ...

  2. 构建命令maven install 打包不是最新的代码

    问题: 之前一直用的是mvn install 命令来构建项目,但是最近发现最新的代码没有在war包中.之前看的说 mvn install 命令会执行之前的所有阶段,会被编译,测试,打包. 经查最后采用 ...

  3. 热更新(一) 之Lua语法的学习

    热更新 如热更新果需要更换UI显示,或者修改游戏的逻辑,这个时候,如果不使用热更新,就需要重新打包,然后让玩家重新下载(浪费流量和时间,体验不好).热更新可以在不重新下载客户端的情况下,更新游戏的内容 ...

  4. hud 5124 lines(思维 + 离散化)

    http://acm.hdu.edu.cn/showproblem.php?pid=5124 lines   Problem Description: John has several lines. ...

  5. Java笔记(一)GC及类加载

    垃圾回收器(GC)    垃圾回收器:java中有一个线程,专门负责JVM中垃圾内存的释放    垃圾:没有引用的内存节点    垃圾回收的算法    注意:垃圾回收有自己的算法,我们是不能控制垃圾回 ...

  6. springBoot整合MyBatise及简单应用

    springBoot整合MyBatise及简单应用 我采用的是 工具IDEA 框架是springBoot+maven+Mybatise 第一步: pom.xml 引入相关jar包 <?xml v ...

  7. SQL面试题(网络收集)

    1. 用一条SQL 语句 查询出每门课都大于80 分的学生姓名 name   kecheng   fenshu 张三     语文       81 张三     数学       75 李四     ...

  8. [总结帖]Web小白的基础恶补帖

    1. jQuery实现按钮点击跳转网页 <script src="js/jquery/jQuery-2.2.0.min.js" type="text/javascr ...

  9. Linux磁盘分区、挂在

    分区基础知识分区的方式:1) mbr分区:1.最多支持四个主分区2.系统只能安装在主分区3.扩展分区要占一个主分区4.MBR最大只支持2TB,但拥有最好的兼容性2) gtp分区:1.支持无限多个主分区 ...

  10. shell-012:批量创建用户

    # #!/bin/bash # 批量创建用户 # 分析:用命令给用户创建密码的方法有两种 # . 可以直接用echo的方法 # echo "abc-123" |passwd roo ...