前置知识
R3 和 R0
API 从 R3 到 R0
#include <iostream>
#include <windows.h>
void testThread() {
while (true)
{
Sleep(1000);
}
}
HANDLE thread = 0;
int main()
{
thread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)testThread, 0, 0, 0);
if (thread == 0) {
printf("CreateThread failed. GetLastError()=%dn", GetLastError());
system("pause");
return 0;
}
DebugBreak();
SuspendThread(thread);
system("pause");
}
# Child-SP RetAddr Call Site
00 ffff9f8473a21998 fffff807
82aec1d8 nt!KiInsertQueueApc
01 ffff9f8473a219a0 fffff807
82aec104 nt!KiSuspendThread+0x88
02 ffff9f8473a219f0 fffff807
82edc197 nt!KeSuspendThread+0x78
03 ffff9f8473a21a40 fffff807
82edc0f3 nt!PsSuspendThread+0x67
04 ffff9f8473a21aa0 fffff807
82c0f3f5 nt!NtSuspendThread+0x93
05 ffff9f8473a21b00 00007ffa
111907f4 nt!KiSystemServiceCopyEnd+0x25
06 000000fe5a72fbc8 00007ffa
0e994a90 ntdll!NtSuspendThread+0x14
07 000000fe5a72fbd0 00007ff7
b13d1a29 KERNELBASE!SuspendThread+0x10
08 000000fe5a72fc00 00007ff7
b13d2459 SuspendDemo!main+0x89 [C:UsersfrendguosourcereposSuspendDemoSuspendDemoSuspendDemo.cpp @ 37]
09 000000fe5a72fd10 00007ff7
b13d22fe SuspendDemo!invoke_main+0x39 [D:a_work1ssrcvctoolscrtvcstartupsrcstartupexe_common.inl @ 79]
0a 000000fe5a72fd60 00007ff7
b13d21be SuspendDemo!__scrt_common_main_seh+0x12e [D:a_work1ssrcvctoolscrtvcstartupsrcstartupexe_common.inl @ 288]
0b 000000fe5a72fdd0 00007ff7
b13d24ee SuspendDemo!__scrt_common_main+0xe [D:a_work1ssrcvctoolscrtvcstartupsrcstartupexe_common.inl @ 331]
0c 000000fe5a72fe00 00007ffa
0f477604 SuspendDemo!mainCRTStartup+0xe [D:a_work1ssrcvctoolscrtvcstartupsrcstartupexe_main.cpp @ 17]
0d 000000fe5a72fe30 00007ffa
111426a1 KERNEL32!BaseThreadInitThunk+0x14
0e 000000fe5a72fe60 00000000
00000000 ntdll!RtlUserThreadStart+0x21
Nt* 和 Zw* 的区别参考:
SSDT(System Service Descriptor Table)
SSDT表中存放在一组服务函数, 通过该表找到该API在执行体 (Executive)(ntoskrnl.exe
)中导出函数
的位置,最终调用系统功能。
Windows IO 模型
参考自:处理 IRP - Windows drivers | Microsoft Learn
内核模式驱动程序通过一个保护子系统对最终用户进行隐藏,该子系统实现了一个已经熟悉的编程接口,例如Windows或POSIX。设备仅对用户模式代码可见,包括受保护的子系统,但只作为由I/O管理器控制的具名文件对象。具体关系可以看下图:
下面官方有个图,来描述从 subsystem 中打开一个文件的过程:
来自:Example I/O Request - An Overview - Windows drivers | Microsoft Learn
具体到 IO 是怎么做的,可以看下面这个图:
可以看到,整个过程都是使用 IRP 来传输信息的。简化下:
模型中我们需要关注三个概念:
- I/O 请求使用
IRP
从用户空间发送到驱动程序 - I/O 管理器(I/O manager)为所有内核模式驱动程序提供一致的接口。
- 此 I/O 管理器为每个已安装和加载的驱动程序创建一个驱动程序对象( driver object)。
IRP
IRP 在驱动开发过程中不可避免的,除了 DriverEntry,还有我们的 R0 和 R3 通信,也是利用 IRP 来进行的。具体可以参考:
驱动类型
具体分类可以看:
主要开发框架
框架名称 | 解释 | 使用场景 |
Windows Driver Model (WDM) | 用于Windows 98到XP的统一驱动模型 | 通用硬件驱动开发 |
Kernel-Mode Driver Framework (KMDF) | WDF的一部分,用于内核模式驱动开发 | 需要直接与硬件交互的驱动程序 |
User-Mode Driver Framework (UMDF) | WDF的一部分,用于用户模式驱动开发 | 需要在用户模式下运行的驱动程序 |
Network Driver Interface Specification (NDIS) | 网络驱动开发框架 | 网络适配器和相关硬件的驱动开发 |
Windows Display Driver Model (WDDM) | 图形显示驱动模型 | 显示和图形卡驱动开发 |
Miniport Drivers | 与通用端口驱动程序一起用于特定类型的硬件接口 | 特定硬件如SCSI、网络设备的驱动开发 |
Class Drivers and Function Drivers | 类驱动程序用于控制一类设备,功能驱动程序针对特定设备 | 例如,USB类驱动程序可控制所有USB设备,功能驱动程序可针对特定USB摄像头 |
上个手
0. 准备工作
1. 写代码
2. 调试
- 安装驱动
- Windbg 附加
- 符号配置、源配置
- 断点
0