Windows内核安全

01.配置双机调试环境

驱动环境配置

首先查看一下自己的SDK版本号:

进入设置选择应用然后选择修改,如下:

进入后选择使用C++的桌面开发,如下:

我们可以看到我们的sdk版本是:10.0.17763.0

然后我们进入官网搜索相关的wdk进行下载,如下:

https://learn.microsoft.com/cs-cz/windows-hardware/drivers/other-wdk-downloads

应该是这个1809,如下:

然后具体匹配我们可以右键查看其属性,如下:

我们可以观察到其也是17763版本的

然后我们双击一路下一步即可,这里我将其放在了虚拟机内:

然后这时候我们如果要创建驱动,我们在Visual Studio里创建项目就会有一个新的选项:Windows Drivers,如下:

上述就是我们的驱动环境配置。


双击调试环境

接下来进入正题,配置一个我们的双机调试环境

本机下载了wdk其会自带一个windbg我直接将其快捷方式发送到桌面,如下:

配置windbg:

然后右键属性:得到其目标如下

1
"C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe"

将目标修改为:如下:

1
"C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe"  -y srv*G:\all_tools\win10_符号包\symbols(Win1021H2)*http://msdl.microsoft.com/download/symbols -b -k com:port=\\.\pipe\com_1,baud=115200,pipe

解释:

1
2
3
4
5
6
目标:
"C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe"
//这一部分指的是符号的位置,帮你解析windows内核里的一些变量名啊,结构等一些东西
-y srv*G:\all_tools\win10_符号包\symbols(Win1021H2)*http://msdl.microsoft.com/download/symbols
//命名管道的一个端口和频率
-b -k com:port=\\.\pipe\com_1,baud=115200,pipe

配置操作系统:

首先配置一个虚拟机系统是win10-21h2

一路下一步

到此选择镜像:如下:

然后配置位置:

然后一路下一步:

内核数量我设置成4个,然后继续下一步:

设置为8GB内存差不多,继续下一步:

然后一路下一步,接下来设置自定义硬件

移除打印机,加入一个串行端口

然后选择使用命名的管道

1
\\.\pipe\com_1

一侧是虚拟机,另一侧是应用程序

最后点击完成,然后开启虚拟机。


首先双击运行,添加一些注册表信息:如下:

然后安装个解压缩工具:

接下来虚拟机内部进行配置:

1
2
3
4
5
6
7
8
9
10
2.配置双机调试
虚拟机内部CMD管理员运行:
bcdedit /dbgsettings serial baudrate:115200 debugport:1
bcdedit /copy {current} /d DebugEntry
bcdedit /displayorder {current} {e386fa5a-e3ba-11ef-93d8-913210d84a86}
bcdedit /debug {e386fa5a-e3ba-11ef-93d8-913210d84a86} ON

关闭强制签名检测
bcdedit.exe -set loadoptions DDISABLE_INTEGRITY_CHECKS
bcdedit /set testsigning on

记得第二句运行完毕之后,复制返回的字符串,并替换掉后面两句的对应位置。

然后接下来就是激活一下这个系统,如下:

然后以管理员身份运行Dbgview:开启监视核心启用详细核心输出

启用详细核心输出

然后关机重启:选择后者DebugEntry

然后双击运行我们刚刚配置好的windbg,如下:

我们会发现它(整个操作系统)已经断下来了。

表现为卡住了,这时候我们直接点击F5让其继续运行。

然后可以自己设置一下各个位置。然后保存一下当前界面:

然后我们可以尝试将其断下来,如下:

然后我们在Command窗口输入:

1
dt _PEB

出现了它的结构,说明符号也加载成功了

至此,调试环境就配置完毕了。

02.第一个驱动程序

C驱动程序:

创建项目:

然后添加新项,如下:

然后我们来调整一下项目属性,如下:

代码如下:

entry.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <ntifs.h>

VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
DbgPrint("DriverUnload!\r\n");
}

//驱动里面的main函数,返回的是一个状态值:NTSTATUS
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath)
{
pDriverObject->DriverUnload = DriverUnload;
DbgPrint("DriverEntry!\r\n");
return STATUS_SUCCESS;
}

然后我们将生成的驱动放入我们的调试环境中,

管理员身份打开Dbgview,INSTDRV

首先在windbg处关闭日志打印:

1
ed nt!kd_fusion_mask 0

然后安装驱动:

点击启动,我们会发现其打印了DriverEntry!

点击停止,我们会发现其打印了DriverUnload!

接下来我们来看C++的第一个驱动代码:

main.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <ntifs.h>

EXTERN_C VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
DbgPrint("DriverUnload!\r\n");
}

//驱动里面的main函数,返回的是一个状态值:NTSTATUS
EXTERN_C NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath)
{
pDriverObject->DriverUnload = DriverUnload;
DbgPrint("DriverEntry!\r\n");
return STATUS_SUCCESS;
}

效果和上面的一致。

03.IRQL中断请求级别

IRQL(Interrupt Request Level,中断请求级别)是Windows操作系统中用来表示系统中断优先级的机制。它定义了不同的中断请求(IRQ)在系统中被处理的优先级,影响着中断的处理顺序和响应的时机。

IRQL的主要概念:

  1. IRQL的作用:IRQL决定了处理器在某一时刻能否响应特定的中断。较高的IRQL优先级会中断较低优先级的任务,从而保证重要任务的及时响应。
  2. 不同IRQL的优先级
    • Passive Level:最低优先级,通常是用户模式(如应用程序执行)使用的IRQL。它不受中断影响,执行时可以中断。
    • Dispatch Level:系统线程可以使用的IRQL,位于Passive Level之上,表示可以响应中断。
    • Device Level:中断处理程序在此级别运行,通常用于设备驱动程序。
    • DPC Level(Deferred Procedure Call):一种延迟执行的机制,可以用来在较低IRQL下处理一些操作,避免阻塞。
    • Interrupt Level:硬件中断处理的最高优先级,通常由硬件生成的IRQ信号引发。
  3. IRQL的影响
    • 中断处理:当IRQL较高时,操作系统会推迟响应较低IRQL的中断,以保证重要任务的及时执行。
    • 阻塞和同步:在IRQL较高时,线程通常不能进行阻塞操作或执行I/O操作,因为系统可能无法中断来进行必要的调度。

举个例子:

  • 用户程序执行时,IRQL通常是最低的,这时候CPU能响应大部分中断。
  • 硬件中断(比如键盘输入、网络卡的接收数据)会将IRQL提升到更高的级别,以便及时处理这些硬件事件。

示例代码:

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <ntifs.h>

/*
IRQL:中断请求级别
中断分发:CPU状态保护 -》 屏蔽相同或更低等级的IRQL的中断 -》 调用合适的ISR -》执行ISR -》 恢复CPU状态
*/

VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
DbgPrint("DriverUnload!\r\n");
}

/*
设备IRQL等级:
x86:3 - 26
x64/arm/arm64:3 - 11

HIGH_LEVEL 最高级IRQL
x64:15
x86:31
屏蔽了所有的中断,在操作链表的时候为了安全所以会设置这个IRQL等级
*/
VOID ShowCurrentIrql(KIRQL kIrql)
{
switch (kIrql)
{
//0 一般代码运行的IRQL等级
case PASSIVE_LEVEL:
{
DbgPrint("IRQL:PASSIVE_LEVEL\r\n");
break;
}
//1 内核APC执行(异步过程调用)
case APC_LEVEL:
{
DbgPrint("IRQL:APC_LEVEL\r\n");
break;
}
//2 调度器是不会被唤醒的,而且不能访问分页内存
case DISPATCH_LEVEL:
{
DbgPrint("IRQL:DISPATCH_LEVEL\r\n");
break;
}
default:
break;
}
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath)
{
pDriverObject->DriverUnload = DriverUnload;
DbgPrint("DriverEntry!\r\n");
ShowCurrentIrql(KeGetCurrentIrql());
//提高IRQL等级
KIRQL kOldIrql;
KeRaiseIrql(APC_LEVEL, &kOldIrql);
ShowCurrentIrql(KeGetCurrentIrql());
//降低IRQL等级
KeLowerIrql(kOldIrql);
ShowCurrentIrql(KeGetCurrentIrql());
KeRaiseIrql(DISPATCH_LEVEL, &kOldIrql);
ShowCurrentIrql(KeGetCurrentIrql());
//降低IRQL等级
KeLowerIrql(kOldIrql);
ShowCurrentIrql(KeGetCurrentIrql());
return STATUS_SUCCESS;
}