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

KeEnterGuardedRegion和KeLeaveGuardedRegion能禁止所有APC调用

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

  1. FORCEINLINE
  2. VOID
  3. KeEnterCriticalRegionThread (
  4. PKTHREAD Thread
  5. )
  6.  
  7. /*++
  8.  
  9. Routine Description:
  10.  
  11. This function disables kernel APC's for the current thread.
  12.  
  13. N.B. The following code does not require any interlocks. There are
  14. two cases of interest: 1) On an MP system, the thread cannot
  15. be running on two processors as once, and 2) if the thread is
  16. is interrupted to deliver a kernel mode APC which also calls
  17. this routine, the values read and stored will stack and unstack
  18. properly.
  19.  
  20. Arguments:
  21.  
  22. Thread - Supplies a pointer to the current thread.
  23.  
  24. N.B. This must be a pointer to the current thread.
  25.  
  26. Return Value:
  27.  
  28. None.
  29.  
  30. --*/
  31.  
  32. {
  33.  
  34. ASSERT(Thread == KeGetCurrentThread());
  35.  
  36. ASSERT((Thread->KernelApcDisable <= ) && (Thread->KernelApcDisable != -));
  37.  
  38. Thread->KernelApcDisable -= ;
  39. KeMemoryBarrierWithoutFence();
  40. return;
  41. }

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

  1. FORCEINLINE
  2. VOID
  3. KeEnterGuardedRegionThread (
  4. IN PKTHREAD Thread
  5. )
  6.  
  7. /*++
  8.  
  9. Routine Description:
  10.  
  11. This function disables special kernel APC's for the current thread.
  12.  
  13. N.B. The following code does not require any interlocks. There are
  14. two cases of interest: 1) On an MP system, the thread cannot
  15. be running on two processors as once, and 2) if the thread is
  16. is interrupted to deliver a kernel mode APC which also calls
  17. this routine, the values read and stored will stack and unstack
  18. properly.
  19.  
  20. Arguments:
  21.  
  22. Thread - Supplies a pointer to the current thread.
  23.  
  24. N.B. This must be a pointer to the current thread.
  25.  
  26. Return Value:
  27.  
  28. None.
  29.  
  30. --*/
  31.  
  32. {
  33.  
  34. ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
  35.  
  36. ASSERT(Thread == KeGetCurrentThread());
  37.  
  38. ASSERT((Thread->SpecialApcDisable <= ) && (Thread->SpecialApcDisable != -));
  39.  
  40. Thread->SpecialApcDisable -= ;
  41. KeMemoryBarrierWithoutFence();
  42. return;
  43. }

注意两者之间的区别

KeEnterCriticalRegionThread中为

  1. Thread->KernelApcDisable -= 1;

KeEnterGuardRegionThread中为

  1. Thread->SpecialApcDisable -= 1;

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

  1. VOID
  2. KiDeliverApc (
  3. IN KPROCESSOR_MODE PreviousMode,
  4. IN PKEXCEPTION_FRAME ExceptionFrame,
  5. IN PKTRAP_FRAME TrapFrame
  6. )
  7.  
  8. /*++
  9.  
  10. Routine Description:
  11.  
  12. This function is called from the APC interrupt code and when one or
  13. more of the APC pending flags are set at system exit and the previous
  14. IRQL is zero. All special kernel APC's are delivered first, followed
  15. by normal kernel APC's if one is not already in progress, and finally
  16. if the user APC queue is not empty, the user APC pending flag is set,
  17. and the previous mode is user, then a user APC is delivered. On entry
  18. to this routine IRQL is set to APC_LEVEL.
  19.  
  20. N.B. The exception frame and trap frame addresses are only guaranteed
  21. to be valid if, and only if, the previous mode is user.
  22.  
  23. Arguments:
  24.  
  25. PreviousMode - Supplies the previous processor mode.
  26.  
  27. ExceptionFrame - Supplies a pointer to an exception frame.
  28.  
  29. TrapFrame - Supplies a pointer to a trap frame.
  30.  
  31. Return Value:
  32.  
  33. None.
  34.  
  35. --*/
  36.  
  37. {
  38.  
  39. PKAPC Apc;
  40. PKKERNEL_ROUTINE KernelRoutine;
  41. KLOCK_QUEUE_HANDLE LockHandle;
  42. PLIST_ENTRY NextEntry;
  43. PVOID NormalContext;
  44. PKNORMAL_ROUTINE NormalRoutine;
  45. PKTRAP_FRAME OldTrapFrame;
  46. PKPROCESS Process;
  47. PVOID SystemArgument1;
  48. PVOID SystemArgument2;
  49. PKTHREAD Thread;
  50.  
  51. //
  52. // If the thread was interrupted in the middle of the SLIST pop code,
  53. // then back up the PC to the start of the SLIST pop.
  54. //
  55.  
  56. if (TrapFrame != NULL) {
  57. KiCheckForSListAddress(TrapFrame);
  58. }
  59.  
  60. //
  61. // Save the current thread trap frame address and set the thread trap
  62. // frame address to the new trap frame. This will prevent a user mode
  63. // exception from being raised within an APC routine.
  64. //
  65.  
  66. Thread = KeGetCurrentThread();
  67. OldTrapFrame = Thread->TrapFrame;
  68. Thread->TrapFrame = TrapFrame;
  69.  
  70. //
  71. // If special APC are not disabled, then attempt to deliver one or more
  72. // APCs.
  73. //
  74.  
  75. Process = Thread->ApcState.Process;
  76. Thread->ApcState.KernelApcPending = FALSE;
  77. if (Thread->SpecialApcDisable == ) {
  78.  
  79. //
  80. // If the kernel APC queue is not empty, then attempt to deliver a
  81. // kernel APC.
  82. //
  83. // N.B. The following test is not synchronized with the APC insertion
  84. // code. However, when an APC is inserted in the kernel queue of
  85. // a running thread an APC interrupt is requested. Therefore, if
  86. // the following test were to falsely return that the kernel APC
  87. // queue was empty, an APC interrupt would immediately cause this
  88. // code to be executed a second time in which case the kernel APC
  89. // queue would found to contain an entry.
  90. //
  91.  
  92. KeMemoryBarrier();
  93. while (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]) == FALSE) {
  94.  
  95. //
  96. // Raise IRQL to dispatcher level, lock the APC queue, and check
  97. // if any kernel mode APC's can be delivered.
  98. //
  99. // If the kernel APC queue is now empty because of the removal of
  100. // one or more entries, then release the APC lock, and attempt to
  101. // deliver a user APC.
  102. //
  103.  
  104. KeAcquireInStackQueuedSpinLock(&Thread->ApcQueueLock, &LockHandle);
  105. NextEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
  106. if (NextEntry == &Thread->ApcState.ApcListHead[KernelMode]) {
  107. KeReleaseInStackQueuedSpinLock(&LockHandle);
  108. break;
  109. }
  110.  
  111. //
  112. // Clear kernel APC pending, get the address of the APC object,
  113. // and determine the type of APC.
  114. //
  115. // N.B. Kernel APC pending must be cleared each time the kernel
  116. // APC queue is found to be non-empty.
  117. //
  118.  
  119. Thread->ApcState.KernelApcPending = FALSE;
  120. Apc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
  121. ReadForWriteAccess(Apc);
  122. KernelRoutine = Apc->KernelRoutine;
  123. NormalRoutine = Apc->NormalRoutine;
  124. NormalContext = Apc->NormalContext;
  125. SystemArgument1 = Apc->SystemArgument1;
  126. SystemArgument2 = Apc->SystemArgument2;
  127. if (NormalRoutine == (PKNORMAL_ROUTINE)NULL) {
  128.  
  129. //
  130. // First entry in the kernel APC queue is a special kernel APC.
  131. // Remove the entry from the APC queue, set its inserted state
  132. // to FALSE, release dispatcher database lock, and call the kernel
  133. // routine. On return raise IRQL to dispatcher level and lock
  134. // dispatcher database lock.
  135. //
  136.  
  137. RemoveEntryList(NextEntry);
  138. Apc->Inserted = FALSE;
  139. KeReleaseInStackQueuedSpinLock(&LockHandle);
  140. (KernelRoutine)(Apc,
  141. &NormalRoutine,
  142. &NormalContext,
  143. &SystemArgument1,
  144. &SystemArgument2);
  145.  
  146. #if DBG
  147.  
  148. if (KeGetCurrentIrql() != LockHandle.OldIrql) {
  149. KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
  150. KeGetCurrentIrql() << | LockHandle.OldIrql << ,
  151. (ULONG_PTR)KernelRoutine,
  152. (ULONG_PTR)Apc,
  153. (ULONG_PTR)NormalRoutine);
  154. }
  155.  
  156. #endif
  157.  
  158. } else {
  159.  
  160. //
  161. // First entry in the kernel APC queue is a normal kernel APC.
  162. // If there is not a normal kernel APC in progress and kernel
  163. // APC's are not disabled, then remove the entry from the APC
  164. // queue, set its inserted state to FALSE, release the APC queue
  165. // lock, call the specified kernel routine, set kernel APC in
  166. // progress, lower the IRQL to zero, and call the normal kernel
  167. // APC routine. On return raise IRQL to dispatcher level, lock
  168. // the APC queue, and clear kernel APC in progress.
  169. //
  170.  
  171. if ((Thread->ApcState.KernelApcInProgress == FALSE) &&
  172. (Thread->KernelApcDisable == )) {
  173.  
  174. RemoveEntryList(NextEntry);
  175. Apc->Inserted = FALSE;
  176. KeReleaseInStackQueuedSpinLock(&LockHandle);
  177. (KernelRoutine)(Apc,
  178. &NormalRoutine,
  179. &NormalContext,
  180. &SystemArgument1,
  181. &SystemArgument2);
  182.  
  183. #if DBG
  184.  
  185. if (KeGetCurrentIrql() != LockHandle.OldIrql) {
  186. KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
  187. KeGetCurrentIrql() << | LockHandle.OldIrql << | ,
  188. (ULONG_PTR)KernelRoutine,
  189. (ULONG_PTR)Apc,
  190. (ULONG_PTR)NormalRoutine);
  191. }
  192.  
  193. #endif
  194.  
  195. if (NormalRoutine != (PKNORMAL_ROUTINE)NULL) {
  196. Thread->ApcState.KernelApcInProgress = TRUE;
  197. KeLowerIrql();
  198. (NormalRoutine)(NormalContext,
  199. SystemArgument1,
  200. SystemArgument2);
  201.  
  202. KeRaiseIrql(APC_LEVEL, &LockHandle.OldIrql);
  203. }
  204.  
  205. Thread->ApcState.KernelApcInProgress = FALSE;
  206.  
  207. } else {
  208. KeReleaseInStackQueuedSpinLock(&LockHandle);
  209. goto CheckProcess;
  210. }
  211. }
  212. }
  213.  
  214. //
  215. // Kernel APC queue is empty. If the previous mode is user, user APC
  216. // pending is set, and the user APC queue is not empty, then remove
  217. // the first entry from the user APC queue, set its inserted state to
  218. // FALSE, clear user APC pending, release the dispatcher database lock,
  219. // and call the specified kernel routine. If the normal routine address
  220. // is not NULL on return from the kernel routine, then initialize the
  221. // user mode APC context and return. Otherwise, check to determine if
  222. // another user mode APC can be processed.
  223. //
  224. // N.B. There is no race condition associated with checking the APC
  225. // queue outside the APC lock. User APCs are always delivered at
  226. // system exit and never interrupt the execution of the thread
  227. // in the kernel.
  228. //
  229.  
  230. if ((PreviousMode == UserMode) &&
  231. (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]) == FALSE) &&
  232. (Thread->ApcState.UserApcPending != FALSE)) {
  233.  
  234. //
  235. // Raise IRQL to dispatcher level, lock the APC queue, and deliver
  236. // a user mode APC.
  237. //
  238.  
  239. KeAcquireInStackQueuedSpinLock(&Thread->ApcQueueLock, &LockHandle);
  240.  
  241. //
  242. // If the user APC queue is now empty because of the removal of
  243. // one or more entries, then release the APC lock and exit.
  244. //
  245.  
  246. Thread->ApcState.UserApcPending = FALSE;
  247. NextEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
  248. if (NextEntry == &Thread->ApcState.ApcListHead[UserMode]) {
  249. KeReleaseInStackQueuedSpinLock(&LockHandle);
  250. goto CheckProcess;
  251. }
  252.  
  253. Apc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
  254. ReadForWriteAccess(Apc);
  255. KernelRoutine = Apc->KernelRoutine;
  256. NormalRoutine = Apc->NormalRoutine;
  257. NormalContext = Apc->NormalContext;
  258. SystemArgument1 = Apc->SystemArgument1;
  259. SystemArgument2 = Apc->SystemArgument2;
  260. RemoveEntryList(NextEntry);
  261. Apc->Inserted = FALSE;
  262. KeReleaseInStackQueuedSpinLock(&LockHandle);
  263. (KernelRoutine)(Apc,
  264. &NormalRoutine,
  265. &NormalContext,
  266. &SystemArgument1,
  267. &SystemArgument2);
  268.  
  269. if (NormalRoutine == (PKNORMAL_ROUTINE)NULL) {
  270. KeTestAlertThread(UserMode);
  271.  
  272. } else {
  273. KiInitializeUserApc(ExceptionFrame,
  274. TrapFrame,
  275. NormalRoutine,
  276. NormalContext,
  277. SystemArgument1,
  278. SystemArgument2);
  279. }
  280. }
  281. }
  282.  
  283. //
  284. // Check if process was attached during the APC routine.
  285. //
  286.  
  287. CheckProcess:
  288. if (Thread->ApcState.Process != Process) {
  289. KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
  290. (ULONG_PTR)Process,
  291. (ULONG_PTR)Thread->ApcState.Process,
  292. (ULONG)Thread->ApcStateIndex,
  293. (ULONG)KeIsExecutingDpc());
  294. }
  295.  
  296. //
  297. // Restore the previous thread trap frame address.
  298. //
  299.  
  300. Thread->TrapFrame = OldTrapFrame;
  301. return;
  302. }
  303.  

可以看到,这个函数会判断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. long polling

    Regular http: client 发出请求到server server 计算 response server 响应 response 给 client Polling: A client re ...

  2. python 中为什么不需要重载

    函数重载主要是为了解决两个问题. (1)可变参数类型. (2) 可变参数个数. 另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两 ...

  3. django 定制管理页面外观 模板文件不生效的解决方法

    问题描述:大概过程跟下面描述的一样,简单来说就是照着例子学习的时候定制管理页面外观,按照文档要求拷贝了base_site.html文件到templates目录下,并且按照要求修改了settings.p ...

  4. androidstudio提示adb错误:cannot parse version string:kg01的解决方法

    打开adb.exe的文件目录,同时按下shift和鼠标右键,打开cmd后运行一下这个命令adb kill-server

  5. 浅谈 温故知新——HTML5!

    古人有云:温故而知新.活到老,学到老,作为一枚前端的程序猿,不停的学习能够让我们对我们的技术有一个更加丰富的认识.这几天,项目已经完成,但我发现自己的知识体系存在一些短板,特别是在H5方面,所以我又回 ...

  6. C#——Socket

    最近公司让我搞个socket小程序(服务端). 主要的功能:客户端发字符串到服务端,服务端接收后在界面上显示. 参照了网上许多代码,自己从中修改整理代码. public Form4() { Initi ...

  7. WP8.1StoreApp(WP8.1RT)---添加推送功能和获取系统信息

    添加推送通知 1:Package.appxmanifest中的声明添加后台任务的推送通知权限 2:var channel = await PushNotificationChannelManager. ...

  8. django系列3.3--view视图函数, render, reverse(未完待续)

    1.view视图函数 urls分发之后所用到的处理函数 2.render 用于渲染页面 在views.py中常用 from django.shortcuts import render, HttpRe ...

  9. 十一、linux文件系统权限详解

    对于文件系统权限,我们前面已经讲解了一部分,这里就不在重复了. 1.修改文件权限有两种,一种是数字.一种是字母 (chmod 的修改只能是属主或者root) 数字: 修改目录权限和目录内所有文件的权限 ...

  10. 九、基础正则表达式BRE

    1.重要性:简单的说正则表达式就是处理一套字符串的规则和方法,以行为单位对字符串进行处理. 运维工作中,会有大量的访问日志,错误日志,大数据学习正则表达式是不可少的. 2.linux正则表达式,主要是 ...