/ Windows / 48浏览

Windows 驱动入门

前置知识

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 fffff80782aec1d8     nt!KiInsertQueueApc
01 ffff9f8473a219a0 fffff80782aec104     nt!KiSuspendThread+0x88
02 ffff9f8473a219f0 fffff80782edc197     nt!KeSuspendThread+0x78
03 ffff9f8473a21a40 fffff80782edc0f3     nt!PsSuspendThread+0x67
04 ffff9f8473a21aa0 fffff80782c0f3f5     nt!NtSuspendThread+0x93
05 ffff9f8473a21b00 00007ffa111907f4     nt!KiSystemServiceCopyEnd+0x25
06 000000fe5a72fbc8 00007ffa0e994a90     ntdll!NtSuspendThread+0x14
07 000000fe5a72fbd0 00007ff7b13d1a29     KERNELBASE!SuspendThread+0x10
08 000000fe5a72fc00 00007ff7b13d2459     SuspendDemo!main+0x89 [C:UsersfrendguosourcereposSuspendDemoSuspendDemoSuspendDemo.cpp @ 37] 
09 000000fe5a72fd10 00007ff7b13d22fe     SuspendDemo!invoke_main+0x39 [D:a_work1ssrcvctoolscrtvcstartupsrcstartupexe_common.inl @ 79] 
0a 000000fe5a72fd60 00007ff7b13d21be     SuspendDemo!__scrt_common_main_seh+0x12e [D:a_work1ssrcvctoolscrtvcstartupsrcstartupexe_common.inl @ 288] 
0b 000000fe5a72fdd0 00007ff7b13d24ee     SuspendDemo!__scrt_common_main+0xe [D:a_work1ssrcvctoolscrtvcstartupsrcstartupexe_common.inl @ 331] 
0c 000000fe5a72fe00 00007ffa0f477604     SuspendDemo!mainCRTStartup+0xe [D:a_work1ssrcvctoolscrtvcstartupsrcstartupexe_main.cpp @ 17] 
0d 000000fe5a72fe30 00007ffa111426a1     KERNEL32!BaseThreadInitThunk+0x14
0e 000000fe5a72fe60 0000000000000000     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 来传输信息的。简化下:

模型中我们需要关注三个概念:

  1. I/O 请求使用 IRP 从用户空间发送到驱动程序
  2. I/O 管理器(I/O manager)为所有内核模式驱动程序提供一致的接口。
  3. 此 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 附加
  • 符号配置、源配置
  • 断点

3. 签名

参考文档

  1. 通过驱动接触win内核-编程技术-看雪-安全社区|安全招聘|kanxue.com
  2. https://bbs.kanxue.com/thread-275999.htm
  3. https://learn.microsoft.com/zh-cn/windows-hardware/drivers/kernel/handling-irps
如何通过 C++ 实时监听 ETW 事件
如何通过 C++ 实时监听 ETW 事件
【译】调查并确定 Windows 运行速度变慢问题
【译】调查并确定 Windows 运行速度变慢问题
【译】丢失的 WPA 文档 —— 磁盘使用
【译】丢失的 WPA 文档 —— 磁盘使用
【译】丢失的 WPA 文档 —— CPU 调度
【译】丢失的 WPA 文档 —— CPU 调度
【译】丢失的 WPA 文档 —— CPU 采样
【译】丢失的 WPA 文档 —— CPU 采样
如何通过 PDH(Performance Data Helper) 获取性能计数器的值

0

  1. This post has no comment yet

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注