一,IRQL的定义
Interrupt ReQuest Level
     DDK对IRQL的定义是:中断请求级(IRQL)标示中断的优先级。处理器在一个IRQL上执行线程代码,IRQL是帮助决定线程如何被中断的。每个处理器都有自己的中断IRQL。 在同一处理器上,线程只能被更高级别IRQL的线程能中断。也就是说,较高IRQL的中断能够中断其它的任务或具有较低IRQL的中断,所有拥有与当前IRQL相同或更低的IRQL的中断将被屏蔽,直到它在较高IRQL中断服务程序结束后,得到再次执行的机会。

二,常见的IRQL
     Windows大部分时间都运行在软件中断级别中,即0-2级别。当有设备中断来临时,windows会将IRQL提升至硬件中断级别(DIRQL, DEVICE INTERRUPT REQUEST LEVEL),并且运行相应的硬件中断处理函数。当硬件中断结束后,恢复到原来的IRQL。
    
     我们经常遇见的有四种IRQL级别。“Passive”, “APC”, “Dispatch” and “DIRQL”。“DriverEntry”将会在PASSIVE_LEVEL被调用。
     #define PASSIVE_LEVEL 0
     #define APC_LEVEL 1
     #define DISPATCH_LEVEL 2
     #define PROFILE_LEVEL 27
     #define CLOCK1_LEVEL 28
     #define CLOCK2_LEVEL 28
     #define IPI_LEVEL 29
     #define POWER_LEVEL 30
     #define HIGH_LEVEL 31
     
     PASSIVE_LEVEL
     IRQL最低级别,没有被屏蔽的中断,在这个级别上,线程执行用户模式,可以访问分页内存。
     用户模式的代码是运行在最低级别的PASSIVE_LEVEL中,驱动程序的DriverEntry函数,派遣函数,AddDevice函数一般运行在PASSIVE_LEVEL中(驱动程序的StartIO和DPC函数运行在DISPATCH_LEVEL中),它们在必要的时候可以申请进入DISPATCH_LEVEL级别,使用内核函数KeGetCurrentIrql()可以知道系统的当前IRQL。

APC_LEVEL
     在这个级别上,只有APC级别的中断被屏蔽,可以访问分页内存。当有APC发生时,处理器提升到APC级别,这样,就屏蔽掉其它APC,为了和APC执行 一些同步,驱动程序可以手动提升到这个级别。APC级别仅仅比PASSIVE_LEVEL高,这也是在一个线程中插入一个APC可以打断该线程(如果该线程运行在PASSIVE_LEVEL上)运行的原因。

DISPATCH_LEVEL
     DISPATCH_LEVEL是一个重要的区分点,他代表了线程调度器正在运行。一个处理器运行在此IRQL上,代表他可能正在做两件事之一:正在进行线程调度;正在处理一个硬件中断的后半部(不那么重要的部分),这被称为DPC(Deferred Procedure Call:延迟调用)。
     这个级别,DPC(延迟过程) 和更低的中断被屏蔽,不能访问分页内存,所有的被访问的内存不能分页。因为只能处理分页内存,所以在这个级别,能够访问的Api大大减少。
     Windows负责线程调度的组件运行在DISPATCH_LEVEL级别,当前线程运行完时间片后,操作系统自动从PASSIVE_LEVEL提升至DISPATCH_LEVEL级别,从而可以使得线程调度组件可以调度其他的线程。当线程切换完成后,操作系统又从DISPATCH_LEVEL级别恢复到PASSIVE_LEVEL级别。

DIRQL (Device IRQL)
     通常处于高层次的驱动程序不会使用这个IRQL等级,在这个等级上所有的中断都会被忽略。这是IRQL的最高等级。通常使用这个来判断设备的优先级。
一般的,更高级的驱动在这个级别上不处理IRQL,但是几乎所有的中断被屏蔽,这实际上是IRQL的一个范围,这是一个决定某个驱动有更高的优先级的方法。

三,IRQL与线程优先级
     线程优先级的概念不同于IRQL,只有应用程序在PASSIVE_LEVEL运行的时候才有意义。程序员可以设定线程优先级(可以使用API SetThreadPriority)。优先级高代表可以有更多机会在CPU上运行。当线程调度内核组件运行于DISPATCH_LEVEL的时候,所有应用程序的线程都停止,等着被调度。
     一个运行中的线程,它的优先级可能有以下两种类型,每种类型有16个层次:
     1,可变的优先级
     可变的优先级类的数值范围是0到15,大多数的线程都属于这种类型。属于可变优先级的线程总是可抢先的,也就是说,他们共同在相同的IRQL层和其它的线程一起被系统调度,并构成一个循环。
     通常情况下,内核是这样处理一个可变优先级线程的,当线程是一个与用户交互的线程时,它的优先级最高,其它线程
的优先级随着运行时间片的增长而下降,直到到达最初程序设计者定义的基本优先级层次。
     2,实时优先级
     实时优先级类别的范围数值是16到31。这种类型的线程通常用于临界区线程,只能由一个拥有较高优先级的线程产生可抢先的线程。
     
     要注意的是,无论线程的优先级属性是多少,都会被一个软件或硬件中断所抢先。线程的优先级只影响系统线程调度程序的决策。调度程序运行于DISPATCH_LEVEL级,它将基于线程的优先级来决定一个线程何时该运行及这个线程将会获得多少时间片,同时确定其它所有线程的状态。

四,IRQL与内存分页
     在使用内存分页时,可能会导致页故障。因为分页内存随时可能从物理内存交换到磁盘文件,读取不在物理内存中的分页内存时,会引发一个页故障,从而执行这个异常的处理函数。异常处理函数会将磁盘文件的内容重新交换到物理内存中。     
     页故障允许出现在PASSIVE_LEVEL级别的程序中,但如果在DISPATCH_LEVEL或更高的IRQL的程序中会带来系统崩溃。     
     因此,对于等于或高于DISPATCH_LEVEL级别的程序不能使用分页内存。
当程序的中断请求在DISPATCH_LEVEL以上,包括DISPATCH_LEVEL,程序只能使用非分页内存,否则将导致蓝屏死机

#define PAGED_CODE() PAGED_ASSERT(KeGetCurrentIrql() <= APC_LEVEL);

PAGED_CODE() 是DDk提供的宏,在check版本中生效。他会检查这个函数是否低于DISPATCH_LEVEL的终端请求,如果等于或高于这个中断请求级,将会产生这个断言

简单实现代码

  1. #include "IRQL.h"
  2.  
  3. //bp IRQL!DriverEntry
  4. void Sub_1();
  5.  
  6. NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
  7. {
  8. NTSTATUS Status = STATUS_SUCCESS;
  9. PDEVICE_OBJECT DeviceObject = NULL;
  10.  
  11. //KIRQL Irql;
  12. KIRQL NewIrql;
  13. KIRQL OldIrql;
  14.  
  15. int* v1 = NULL;
  16. int* v2 = NULL;
  17.  
  18. DriverObject->DriverUnload = DriverUnload;
  19.  
  20. Sub_1();
  21.  
  22. NewIrql = APC_LEVEL;
  23.  
  24. KeRaiseIrql(NewIrql, &OldIrql);
  25.  
  26. Sub_1();
  27.  
  28. v1 = ExAllocatePool(NonPagedPool, sizeof(int));
  29. v2 = ExAllocatePool(PagedPool, sizeof(int));//DispatchLevel不能用分页内存
  30.  
  31. //APC_LEVEL下的申请内存可以是分页也可以是不分页,
  32.  
  33. /*
  34. 页故障允许出现在PASSIVE_LEVEL级别的程序中,但如果在DISPATCH_LEVEL或更高的 IRQL的程序中会带来系统崩溃。  
  35. */
  36.  
  37. ExFreePool(v1);
  38. ExFreePool(v2);
  39.  
  40. /*
  41. KeGetCurrentIrql
  42. KeRaiseIrql
  43. KeLowerIrql
  44. */
  45. KeLowerIrql(OldIrql);
  46. Sub_1();
  47.  
  48. /*
  49. PASSIVE_LEVEL
  50. APC_LEVEL
  51. DISPATCH_LEVEL
  52.  
  53. // Interrupt Request Level definitions
  54. //
  55.  
  56. #define PASSIVE_LEVEL 0 // Passive release level
  57. #define LOW_LEVEL 0 // Lowest interrupt level
  58. #define APC_LEVEL 1 // APC interrupt level
  59. #define DISPATCH_LEVEL 2 // Dispatcher level
  60. #define CMCI_LEVEL 5 // CMCI handler level
  61.  
  62. #define PROFILE_LEVEL 27 // timer used for profiling.
  63. #define CLOCK1_LEVEL 28 // Interval clock 1 level - Not used on x86
  64. #define CLOCK2_LEVEL 28 // Interval clock 2 level
  65. #define IPI_LEVEL 29 // Interprocessor interrupt level
  66. #define POWER_LEVEL 30 // Power failure level
  67. #define HIGH_LEVEL 31 // Highest interrupt level
  68.  
  69. #define CLOCK_LEVEL (CLOCK2_LEVEL)
  70.  
  71. */
  72.  
  73. return Status;
  74. }
  75.  
  76. void Sub_1()
  77. {
  78. KIRQL Irql;
  79. Irql = KeGetCurrentIrql();//passive_level
  80. switch (Irql)
  81. {
  82. case PASSIVE_LEVEL:
  83. {
  84. DbgPrint("PASSIVE_LEVEL\r\n");
  85. break;
  86.  
  87. }
  88. case APC_LEVEL:
  89. {
  90. DbgPrint("APC_LEVEL\r\n");
  91.  
  92. break;
  93. }
  94. case DISPATCH_LEVEL:
  95. {
  96. DbgPrint("DISPATCH_LEVEL\r\n");
  97.  
  98. break;
  99. }
  100.  
  101. }
  102.  
  103. }
  104.  
  105. VOID DriverUnload(PDRIVER_OBJECT DriverObject)
  106. {
  107. DbgPrint("DriverUnload()\r\n");
  108. }

>=DISPATCH_LEVEL,程序只能使用非分页内存,这里用PAGED_CODE产生断言

  1. #include "IRQL_ApcLevel_Dispatch_Level.h"
  2.  
  3. //bp IRQL_ApcLevel_Dispatch_Level!DriverEntry
  4.  
  5. KIRQL __NewIrql;
  6. KIRQL __OldIrql;
  7.  
  8. void ShowIrql();
  9. void ApcLevel();
  10. void DispatchLevel();
  11.  
  12. NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
  13. {
  14. NTSTATUS Status = STATUS_SUCCESS;
  15. PDEVICE_OBJECT DeviceObject = NULL;
  16.  
  17. DriverObject->DriverUnload = DriverUnload;
  18.  
  19. ApcLevel();
  20.  
  21. __NewIrql = DISPATCH_LEVEL;
  22.  
  23. KeRaiseIrql(__NewIrql, &__OldIrql);
  24. DispatchLevel();
  25.  
  26. /*
  27. PASSIVE_LEVEL
  28. APC_LEVEL
  29. DISPATCH_LEVEL
  30.  
  31. // Interrupt Request Level definitions
  32. //
  33.  
  34. #define PASSIVE_LEVEL 0 // Passive release level
  35. #define LOW_LEVEL 0 // Lowest interrupt level
  36. #define APC_LEVEL 1 // APC interrupt level
  37. #define DISPATCH_LEVEL 2 // Dispatcher level
  38. #define CMCI_LEVEL 5 // CMCI handler level
  39.  
  40. #define PROFILE_LEVEL 27 // timer used for profiling.
  41. #define CLOCK1_LEVEL 28 // Interval clock 1 level - Not used on x86
  42. #define CLOCK2_LEVEL 28 // Interval clock 2 level
  43. #define IPI_LEVEL 29 // Interprocessor interrupt level
  44. #define POWER_LEVEL 30 // Power failure level
  45. #define HIGH_LEVEL 31 // Highest interrupt level
  46.  
  47. #define CLOCK_LEVEL (CLOCK2_LEVEL)
  48.  
  49. */
  50.  
  51. return Status;
  52. }
  53.  
  54. void ApcLevel()
  55. {
  56. DbgPrint("In Apc\r\n");
  57. int* v1 = NULL;
  58. int* v2 = NULL;
  59.  
  60. ShowIrql();
  61.  
  62. __NewIrql = APC_LEVEL;
  63.  
  64. KeRaiseIrql(__NewIrql, &__OldIrql);
  65.  
  66. ShowIrql();
  67.  
  68. v1 = ExAllocatePool(NonPagedPool, sizeof(int));
  69. v2 = ExAllocatePool(PagedPool, sizeof(int));//Dispatch不能用分页内存
  70.  
  71. //APC_LEVEL下的申请内存可以是分页也可以是不分页
  72.  
  73. ExFreePool(v1);
  74. ExFreePool(v2);
  75.  
  76. KeLowerIrql(__OldIrql);
  77. }
  78.  
  79. #pragma PAGEDCODE //使函数加载到分页内存中
  80. //#pragma LOCKEDCODE //使函数加载到未分页内存中
  81. void DispatchLevel()
  82. {
  83. PAGED_CODE();
  84. //#define PAGED_CODE() PAGED_ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
  85.  
  86. //PAGED_CODE() 是DDk提供的宏,在check版本中生效。
  87. //他会检查这个函数是否低于DISPATCH_LEVEL的终端请求,
  88. //如果等于或高于这个中断请求级,将会产生这个断言。
  89.  
  90. DbgPrint("In Dispatch\r\n");
  91. int* v1 = NULL;
  92. int* v2 = NULL;
  93.  
  94. ShowIrql();
  95.  
  96. __NewIrql = DISPATCH_LEVEL;
  97.  
  98. KeRaiseIrql(__NewIrql, &__OldIrql);
  99.  
  100. ShowIrql();
  101.  
  102. v1 = ExAllocatePool(NonPagedPool, sizeof(int));
  103. v2 = ExAllocatePool(PagedPool, sizeof(int));//Dispatch不能用分页内存
  104.  
  105. //APC_LEVEL下的申请内存可以是分页也可以是不分页
  106.  
  107. ExFreePool(v1);
  108. ExFreePool(v2);
  109.  
  110. KeLowerIrql(__OldIrql);
  111. }
  112.  
  113. void ShowIrql()
  114. {
  115. KIRQL Irql;
  116. Irql = KeGetCurrentIrql();//passive_level
  117. switch (Irql)
  118. {
  119. case PASSIVE_LEVEL:
  120. {
  121. DbgPrint("PASSIVE_LEVEL\r\n");
  122. break;
  123.  
  124. }
  125. case APC_LEVEL:
  126. {
  127. DbgPrint("APC_LEVEL\r\n");
  128.  
  129. break;
  130. }
  131. case DISPATCH_LEVEL:
  132. {
  133. DbgPrint("DISPATCH_LEVEL\r\n");
  134.  
  135. break;
  136. }
  137.  
  138. }
  139.  
  140. }
  141.  
  142. VOID DriverUnload(PDRIVER_OBJECT DriverObject)
  143. {
  144. DbgPrint("DriverUnload()\r\n");
  145. }

IRQL Ring0实现的更多相关文章

  1. Win64 驱动内核编程-23.Ring0 InLineHook 和UnHook

    Ring0 InLineHook 和UnHook 如果是要在R0里hook,作者的建议是InLine HOOK,毕竟SSDT HOOK 和 SHADOW SSDT HOOK比较麻烦,不好修改.目前R3 ...

  2. 函数调用关于从Ring3转到Ring0 ESP堆栈变化

    在ring0堆栈获取ring3堆栈方式 第一种方式 [esp+4] == [esp+参数个数*4+4] 如果这里不相等就需要用第二种方式 [[esp+参数个数*4+8]] 这里面的值就是Ring3的堆 ...

  3. ring0

    Intel的x86处理器是通过Ring级别来进行访问控制的,级别共分4层,RING0,RING1,RING2,RING3.Windows只使用其中的两个级别RING0和RING3. RING0层拥有最 ...

  4. 对付ring0 inline hook

    对付ring0 inline hook的基本思路是这样的,自己写一个替换的内核函数,以NtOpenProcess为例,就是MyNtOpenProcess.然后修改SSDT表,让系统服务进入自己的函数M ...

  5. ring0 与 ring3 层之间的交互

    在进行Windows的ring0层开发时,必不可免的要与 ring3 层进行交互.进行数据间的相互传输.可用的方法有DeviceIoCntrol,ReadFile.我平常都是用的DeviceIoCon ...

  6. OD: Ring0 & Kernel

    开发技术讲究封装与模块化,安全技术强调底层安全性.安全技术需要打开封装.追根溯源! <0day 安全:软件漏洞分析技术(第2版)> 第21章 探索 Ring0 笔记 Intel x86 系 ...

  7. Ring3 和Ring0 解释

    这得从CPU指令系统(用于控制CPU完成各种功能的命令)的特权级别说起.在CPU的所有指令中,有一些指令是非常危险的,如果错用,将导致整个系统崩溃.比如:清内存.设置时钟等.如果所有的程序都能使用这些 ...

  8. [原创]浅谈NT下Ring3无驱进入Ring0的方法

    原文链接:浅谈NT下Ring3无驱进入Ring0的方法 (测试环境:Windows 2000 SP4,Windows XP SP2.Windows 2003 未测试) 在NT下无驱进入Ring0是一个 ...

  9. ring0和ring3的区别

    现在探讨内核程序和应用程序之间的本质区别.除了能用WDK编写内核程序和阅读一部分Windows的内核代码之外,我们还需要了解它们的本质是什么,它们和我们熟悉的应用程序有什么区别. Intel的x86处 ...

随机推荐

  1. c# 敏捷2 ForEach ToDictionary ToLookup Except比较

    using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; ...

  2. if else的使用以及如何从键盘获取数值

    if-else的使用 顺序结构 顺序从上到下执行,中间没有判断和跳转 分支结构 根据条件,选择性地执行某段代码 有if-else和switch两种分支语句 循环结构 根据循环,重复性地执行某段代码 有 ...

  3. 使用python查询某目录下所有‘jpg’结尾的图片文件

    调用os模块,先建立一个对目标目录的walk迭代器. 然后再对迭代器进行遍历,判断每个文件是否以'jpg'结尾. 若是,则输出. import os g = os.walk("G:" ...

  4. jsp中的include静态包含与动态包含,

    d动态包含与静态包含的最大区别是 静态包含:<%@ include file = "文件名.jsp"%>,在<body>的标签外定义.在没有编译的时候,两个 ...

  5. LeetCode - Partition Labels

    A string S of lowercase letters is given. We want to partition this string into as many parts as pos ...

  6. node学习笔记之io.sockets

    socket.get和socket.set函数已经失效,代码修改如下所示: 服务器端: var httpd = require('http').createServer(handler); var i ...

  7. notify和notifyAll的区别

    转自:http://www.importnew.com/16453.html 如果某些线程在等待某些条件触发,那当那些条件为真时,你可以用 notify 和 notifyAll 来通知那些等待中的线程 ...

  8. 深入详解美团点评CAT跨语言服务监控(六)消息分析器与报表(一)

    大众点评CAT微服务监控架构对于消息的具体处理,是由消息分析器完成的,消息分析器会轮训读取PeriodTask中队列的消息来处理,一共有12类消息分析器,处理后的结果就是生成各类报表. 消息分析器的构 ...

  9. Python 不可变对象练习

    Python 不可变对象练习 str 是不可变对象,就是对这个对象进行操作不会改变这个对象的数据. 如下: >>> a = 'abc' >>> a.replace( ...

  10. sql server 用户创建与权限管理

    要想成功访问 SQL Server 数据库中的数据, 我们需要两个方面的授权:一.获得准许连接 SQL Server 服务器的权利: 二.获得访问特定数据库中数据的权利(select, update, ...