loading...
Featured image of post windows相关知识总结(下)

windows相关知识总结(下)

进程 • DLL • 系统调用 • 驱动 • 内核回调

上篇看这里 -> windows相关知识总结(上)

进程

常见进程

初始化进程

System

进程ID通常为4,是内核级线程的宿主,是运行ntoskrnl.exe的容器

ntoskrnl.exe负责内存管理、进程/线程调度、硬件抽象、系统调用处理等功能)

它是进程树的根,没有父进程,如果看到有用户模式的进程成为它的子进程,需要高度警惕

smss.exe

会话管理器子系统,负责创建新的用户会话,是系统中的第一个真实进程

它会启动csrss.exewinlogon.exe

csrss.exe

客户端/服务器运行时子系统,负责管理窗口、线程和控制台等,每个会话中都会有一个实例

其父进程必须是smss.exe

wininit.exe

Windows初始化进程,在系统引导时启动,会一直保留在后台运行

它的核心任务是启动三个关键进程:services.exelsass.exelsm.exe,任何其他进程都值得审查

其父进程应为空

winlogon.exe

Windows 登录管理器,负责处理用户的交互式登录与注销过程,并加载用户配置文件

在用户成功登录后,它会负责启动userinit.exe

其父进程必须是smss.exe

userinit.exe

用户初始化进程,负责在用户登录后执行登录脚本、恢复网络连接,并启动用户外壳程序(Shell)

它在启动了用户的桌面环境(默认为 explorer.exe)后会立刻退出,因此在正常的进程列表中几乎看不到它

其父进程必须是winlogon.exe

explorer.exe

Windows资源管理器,也是用户登录后启动的第一个进程,是所有用户图形化操作的起点

在一个标准的 Windows 会话中,它有四个功能:

  1. 文件管理器:打开“我的电脑”时看到的那些文件和文件夹窗口
  2. 任务栏与开始菜单:屏幕底部的任务栏、开始按钮、系统托盘区
  3. 桌面图标管理器:桌面图标,以及对它们的点击、拖拽等交互
  4. 桌面背景绘制:桌面壁纸等

恶意软件经常向其注入DLL,以获取用户权限并保持持久化

核心服务进程

services.exe

服务控制管理器,负责启动和管理所有系统服务,是所有通过正常方式启动的svchost.exe的父进程

其父进程必须是wininit.exe

lsass.exe

本地安全机构子系统服务,内存中存放着用户的密码哈希等敏感凭证,系统中必须有且只有一个实例

其父进程必须是wininit.exe

lsm.exe

本地会话管理器服务,处理与用户登录、注销、远程桌面连接和快速用户切换相关的终端服务

其父进程必须是wininit.exe

svchost.exe

服务宿主进程,是 Windows 系统中的一个核心进程,专门用作运行各种系统服务的“容器”或“宿主”

它也是最常见的伪装和注入目标:

  • 父进程:必须是services.exe

  • 文件路径:必须位于C:\Windows\System32\

  • 网络连接:它承载的系统服务中,哪些建立了网络连接,连接到哪里

脚本执行进程

rundll32.exe

用于执行DLL文件中的函数,攻击者常用它来加载恶意DLL,从而绕过基于程序名的检测

分析时应关注其父进程是谁,以及它加载了哪个DLL

powershell.exe

是命令行和脚本引擎,现代无文件攻击的首选工具

任何powershell.exe的出现都值得审查,特别是其命令行参数中是否包含编码(-e)、混淆或下载链接

conhost.exe

控制台窗口主机,为cmd.exe等命令行程序提供交互界面

它的内存中存储了该命令行窗口中所有输入过的命令和输出过的结果

进程权限

系统级核心特权

SeTcbPrivilege

**这是Windows中权限最高、最强大的特权之一。**全称是作为操作系统的一部分(Act as part of the operating system),通常被称为可信计算基(Trusted Computer Base, TCB)特权

拥有此特权的进程被视为系统核心信任的一部分,可以执行几乎任何操作,包括创建安全令牌来冒充任何用户,除了极少数核心系统进程,任何程序都不应拥有它

SeDebugPrivilege

**调试程序特权。**它授予一个进程附加到其他任意进程并检查、修改其内存的能力

攻击者常利用它来从lsass.exe中窃取密码,除了调试器和核心安全软件,任何普通程序(如记事本、浏览器)拥有它都极度可疑

SeLoadDriverPrivilege

**加载和卸载设备驱动程序特权。**它授予一个进程将内核模式驱动程序(.sys文件)加载到Windows内核中或从内核中卸载的能力

这是通往内核模式的入口,恶意软件一旦获得此特权,就可以加载自己的恶意驱动,从而隐藏文件、进程、网络连接,并完全绕过用户态的安全软件

安全与审计特权

SeTakeOwnershipPrivilege

**获取对象所有权特权。**它授予进程获取系统上任何安全对象(如文件、文件夹、注册表项)的所有权的能力,即使该进程原本没有访问该对象的权限

攻击者获得它之后,就可以强行霸占受系统保护的文件或注册表键,然后赋予自己读写权限,进而篡改系统配置或替换核心文件

SeBackupPrivilege

**备份文件和目录特权,可以做到读取一切。**它允许进程在读取文件时,绕过所有常规的文件和目录权限检查(ACL),其初衷是让备份软件可以备份系统上的所有文件

数据窃取类木马和勒索软件经常利用此特权来读取它们本无权访问的敏感用户文档、数据库文件或配置文件,以便进行窃取或加密

SeRestorePrivilege

**还原文件和目录特权,可以做到写入一切。**与SeBackupPrivilege对应,它允许进程在写入文件时,绕过所有权限检查

恶意软件可以利用它来覆盖受保护的系统文件、在系统目录中释放恶意程序,或修改锁定的配置文件以实现持久化

SeSecurityPrivilege

**管理审核和安全日志特权,可以做到删除痕迹。**主要允许进程查看和清空 Windows 安全事件日志

攻击者在完成入侵后,会利用这个特权来清空安全日志,从而抹去自己的登录尝试、账户创建、权限使用等痕迹

常规特权

SeShutdownPrivilege

**关闭系统特权。**授予进程关闭本地计算机的权力

SeImpersonatePrivilege

**身份验证后模拟客户端特权。**通常授予服务类账户,允许一个服务进程模拟连接到它的客户端的安全上下文

攻击者可以诱使一个高权限进程(如SYSTEM)来连接自己,然后利用此特权“模拟”这个高权限进程,从而将自己从一个低权限的服务账户提升到SYSTEM权限

SeChangeNotifyPrivilege

**绕过遍历检查特权。**它允许进程在访问一个对象时,无需检查路径中所有上级目录的权限

这是一个正常无害的特权,Windows 默认会将其授予所有用户,包括最低权限的用户

DLL

什么是DLL

DLL(Dynamic Link Library)文件为动态链接库文件,又称“应用程序拓展”,是软件文件类型

在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库,即DLL文件,放置于系统中,当我们执行某一个程序时,相应的DLL文件就会被调用

一个应用程序可使用多个DLL文件,一个DLL文件也可能被不同的应用程序使用,这样的DLL文件被称为共享DLL文件

DLL文件中存放的是各类程序的函数(子过程)实现过程,当程序需要调用函数时需要先载入DLL,然后取得函数的地址,最后进行调用

使用DLL文件的好处是程序不需要在运行之初加载所有代码,只有在程序需要某个函数的时候才从DLL中取出

DLL的调用方式

1. 静态调用 / 隐式链接

在运行程序前,必须先把所有的DLL都准备好,少一个都不行

  1. 在编译程序的时候,开发者就已经在代码中明确声明了需要用到哪些DLL里的哪些函数
  2. 这些依赖关系被记录在最终生成的可执行文件(.exe)的头部,一个叫做“导入表“(Import Table)的地方
  3. 当运行这个.exe文件时,Windows加载器会首先读取这个表
  4. 加载器会根据导入表的信息,在硬盘上找到所有必需的DLL文件,并将它们加载到该进程的内存空间中
  5. 所有函数地址都链接好之后,程序的主代码才开始执行

2. 动态调用 / 显式链接

在运行程序前,不需要把所有的DLL都准备好,少了哪个就引入哪个

  1. 程序在编译时,并不知道自己会用到哪些DLL
  2. 程序在运行过程中,根据当时的逻辑判断,临时决定需要某个DLL的功能
  3. 程序会调用特定的WindowsAPI函数来手动加载这个DLL
    • LoadLibrary():这个函数用来将指定的DLL文件加载到内存中
    • GetProcAddress():加载成功后,用这个函数从DLL中获取特定函数的地址
  4. 拿到函数地址后,程序就可以像调用自己的函数一样调用它了
  5. 当不再需要时,可以调用FreeLibrary()将其从内存中卸载

常见DLL

核心系统库 (几乎所有进程都会加载)

ntdll.dll

WindowsNT核心库,是用户态程序与系统内核之间最底层的接口,封装了大量的系统调用,例如进程和线程的创建、内存管理、文件I/O以及与内核对象的交互

其他更高层的核心库(如kernel32.dll)最终也需要调用ntdll.dll中的函数来完成实际的工作

kernel32.dll / kernelbase.dll

kernel32.dll是Windows中最核心的用户模式库之一,为应用程序提供了访问操作系统的基础功能,如内存管理、文件I/O和进程线程管理

从Windows7开始,许多kernel32.dll的核心功能被重构并移入了kernelbase.dll,而kernel32.dll本身则更多地作为调用这些新功能的一个“转发层”

user32.dll

负责所有用户图形界面(GUI)的功能设计,如窗口、菜单、按钮、鼠标和键盘输入等,即结构和交互

在早期32-bit 版本的Windows中,用户控件是在ComCtl32中实现的,但是一些控件的显示功能是在User32.dll中实现的,例如在一个窗口中非客户区域(边框和菜单)的绘制就是由User32.dll来完成的

User32.dll是操作系统的一个核心控件,它和操作系统是紧密联系在一起的,不同版本的Windows中User32.dll是不同,因此,应用程序在不同版本的Windows中运行的时候,由于User32.dll的不同,会导致应用程序的界面通常会有微小的不同

gdi32.dll

负责图形化窗口的图形绘制与内容输出,包括像素、线条、字体、位图、画刷等,即内容和外观

comdlg32.dll

通用对话框库(Common Dialog Box Library),这个库为应用程序提供了标准的、预制好的对话框,比如常见的“打开文件”、“保存文件”和“打印”等窗口

user32.dll需要一个“打开文件”对话框时,它不去自己一点点造,而是直接从comdlg32.dll这里拿一个现成的、标准化的来用

advapi32.dll

高级Windows32位应用程序接口,负责处理注册表操作、系统服务管理、账户和安全相关的函数

网络通信库 (判断网络行为的关键)

ws2_32.dll / wsock32.dll

Windows Sockets(套接字)库,是所有 TCP/IP 网络编程的基础

这是一个最重要的网络行为指标

wininet.dll

更高层的互联网协议库,封装了 HTTP、HTTPS 和 FTP 等协议,让程序能更方便地访问网页和传输文件。

dnsapi.dll

负责进行 DNS 域名解析,即将网址(如 www.google.com)转换为 IP 地址

脚本与执行相关库

shell32.dll

提供了核心的 Windows Shell(外壳)功能,如打开文件、显示属性、处理快捷方式等

ole32.dll / oleaut32.dll

负责处理 OLE(对象链接与嵌入)和 COM(组件对象模型)技术

Office 宏病毒和许多漏洞利用(如利用文档中嵌入的恶意对象)都严重依赖这两个库的功能

jscript.dll / vbscript.dll

分别用于解析和执行 JScript 和 VBScript 脚本的引擎

wscript.exemshta.exe等脚本执行进程加载它们时,表明有相应的脚本正在运行

系统调用

什么是系统调用

系统调用是运行在用户模式 (UserMode/Ring3) 的应用程序,向操作系统内核 (KernelMode / Ring0) 请求服务或资源的唯一、规范化的接口

这是操作系统为了保护自身稳定性和安全性而设定的核心机制,应用程序不能随心所欲地直接访问硬件或关键内存,所有这些敏感操作都必须通过系统调用,以一种受控的方式“委托”给内核来完成

系统调用工作过程

准备阶段

用户模式的应用程序(例如notepad.exe)准备调用一个Win32API函数,比如 WriteFileWriteFile 函数本身位于kernel32.dll

中介阶段

kernel32.dll中的WriteFile函数并不会直接执行写文件操作,它是一个包装函数

它会把真正的写文件操作对应的系统调用编号(比如写文件的NtWriteFile 的编号)放入CPU的EAX寄存器中,并将其他参数(如文件句柄、数据缓冲区等)放入其他指定的寄存器

切换阶段

准备好之后,ntdll.dll中的代码会执行一条特殊的CPU指令,例如SYSCALL

特权级转换

SYSCALL指令会使CPU立即从用户模式切换到内核模式,并将控制权交给内核中一个预设好的系统调用总处理程序

派阶段

内核的总处理程序接管控制权后,会查看EAX寄存器中的系统调用编号,然后利用这个编号去SSDT中查找对应的内核函数地址

执行阶段

找到地址后,内核会跳转到该地址(例如内核中的NtWriteFile函数)去执行真正的文件写入操作

返回阶段

内核函数执行完毕后,会将结果返回,CPU 再从内核模式切换回用户模式,应用程序继续执行

SSDT

SSDT 的全称是System Service Dispatch Table(系统服务分派表)

它就是我们在上面流程中提到的那个函数地址查询表,它是一个存放在内核内存中的数组,数组的每一个元素都是一个函数指针,指向了实现具体系统调用的内核函数的内存地址

当系统调用发生时,内核就是通过系统调用编号作为索引,在这个 SSDT 数组中找到并调用正确的内核函数

SSDThook

由于 SSDT 是所有关键操作的必经之路,它成为了内核级 Rootkit 的首要攻击目标

Rootkit(通常是一个恶意的.sys驱动)会获取到 SSDT 在内存中的地址,然后用自己的恶意函数地址,去覆盖表中某个正常系统函数的地址

比如一个文件隐藏Rootkit,可能会用自己的HookedNtQueryDirectoryFile函数地址,替换掉SSDT中原始的 NtQueryDirectoryFile(用于列出目录内容)的地址

此后,当任何程序(包括 explorer.exe)尝试列出目录内容时,系统调用都会被重定向到这个恶意的 HookedNtQueryDirectoryFile函数这个恶意函数会先获取原始的目录列表,然后将其中所有与Rootkit自身相关的恶意文件名都过滤掉,最后再将一个“干净”的列表返回给应用程序,这样,用户就永远无法看到这些恶意文件了

如果一个函数的地址指向的不是官方的 ntoskrnl.exe (或 win32k.sys),而是指向了一个第三方的、可疑的驱动文件,那么就意味着SSDT已被劫持

系统调用的分类

进程与线程管理

NtCreateProcess

创建一个新进程

NtTerminateProcess

终止一个进程

NtCreateThread

创建一个新线程

NtOpenProcess

打开一个已存在进程的句柄

文件与 I/O 操作

NtCreateFile

创建或打开一个文件

NtReadFile

从文件中读取数据

NtWriteFile

向文件中写入数据

NtDeviceIoControlFile

向设备驱动发送控制命令

注册表操作

NtOpenKey

打开一个注册表项

NtQueryValueKey

查询一个注册表键值的数据

NtSetValueKey

设置一个注册表键值的数据

内存管理

NtAllocateVirtualMemory

在进程的虚拟地址空间中分配内存

NtProtectVirtualMemory

修改内存页的保护属性(如可读、可写、可执行)

驱动

什么是驱动

驱动程序(Driver)是一个软件组件,它充当了操作系统内核与物理硬件或虚拟设备之间的通信桥梁

当应用程序需要与硬件(如打印机、网卡、磁盘)交互时,它会向操作系统发出一个通用请求

操作系统内核的I/O管理器接收到这个请求后,不会直接与硬件对话,而是将请求打包成IRP请求转发给相应的驱动程序

驱动程序负责将这个标准化的请求翻译成硬件能够理解的特定指令,并与硬件通信

CPU的特权级别

在了解驱动文件之前,我们得先知道x86架构CPU的四个特权级别

特权级别在x86架构中也被称为保护环(Protection Rings),是CPU硬件层面实现的一种访问控制机制

x86架构定义了四个特权级别,从Ring0到Ring3,数字越小,代表权限越高

Ring 0: 内核态(Kernel Mode)

  • 权限:最高。运行在Ring0的代码可以执行 CPU 的所有指令集,并能直接访问任何内存地址、I/O端口和硬件设备

  • 运行实体: 操作系统内核本身。例如ntoskrnl.exe和绝大多数设备驱动程序(.sys 文件)

  • 这是操作系统的核心,负责管理系统所有资源,包括进程调度、内存管理、I/O 控制等

    如果Ring0的代码崩溃,整个系统将立即蓝屏(BSOD)或宕机(Kernel Panic)

Ring 1 & Ring 2: 准核心态/驱动层(Rarely Used)

  • 权限:介于内核态和用户态之间。它们拥有比Ring3更高的权限(例如可以访问更多的 I/O 端口),但又不像Ring0那样拥有对系统的完全控制权

  • 运行实体: 在理论设计上,Ring1可以用于运行一些准系统级的服务,而Ring2可以用于运行设备驱动程序,从而将驱动与最核心的内核隔离开,增加系统的稳定性(即驱动崩溃不至于让整个内核崩溃)

  • 它几乎从未被主流消费级和服务器级操作系统(如 Windows, Linux, macOS)所使用

    这些操作系统普遍采用了更简单的两级模型,即只使用Ring0和Ring3

    这是因为在Ring1/2和Ring0之间切换上下文的开销,以及复杂的内存管理,并没有带来足够的安全收益来抵消其复杂性

Ring 3: 用户态(User Mode)

  • 权限最低。运行在Ring3的代码受到硬件的严格限制,它不能直接访问硬件,也不能访问属于内核或其他进程的受保护内存空间

  • 运行实体所有的应用程序。例如浏览器 、记事本、游戏等,都运行在Ring3

  • 这是应用程序的沙箱,当一个Ring3的程序需要执行特权操作时(如读取文件),它不能直接去操作硬盘,而是必须通过一个名为系统调用的受控入口,向Ring0的内核提出请求

    内核会对请求进行验证,然后以Ring0的权限代为执行,再将结果返回给Ring1的程序

    这个过程确保了所有对关键资源的访问都在内核的掌控之下

驱动的文件形式

.sys文件

这是最传统、最核心、权限最高的驱动程序形式,是真正的驱动

它们是专门为在操作系统内核(Ring0)中运行而编译的,可以直接与硬件和内核数据结构交互

几乎所有核心的硬件驱动,如磁盘驱动ntfs.sys、网络驱动tcpip.sys等,都是.sys文件

.dll文件

它们有的情况下是驱动组件,但大多数不是传统驱动

为了提高系统的稳定性和安全性,微软引入了用户模式驱动框架 (User-Mode Driver Framework, UMDF)

一些对性能要求不是极致、但对稳定性要求很高的设备(如扫描仪、打印机、传感器等)的驱动,可以被实现为DLL文件

这些驱动DLL运行在权限受限的用户模式(Ring3),并由一个系统进程(通常是svchost.exe)作为“宿主”来加载和运行

驱动程序的分类

按运行模式分类

内核模式驱动程序

这是最常见的类型,运行在操作系统的核心层 (Ring 0),拥有最高权限

它们可以直接与硬件通信,是系统运行的基石

用户模式驱动程序

运行在权限受限的用户模式 (Ring3) 下,安全性更高

如果这类驱动程序崩溃,不会导致整个系统蓝屏

按功能层次分类

总线驱动程序

位于驱动栈的最底层,负责枚举挂载在总线(如 PCI, USB)上的所有设备

功能驱动程序

驱动栈的核心,通常由硬件厂商编写,负责实现设备的具体I/O功能

筛选驱动程序

可以附加在功能驱动程序之上或之下,用于“过滤”或“修改”流经该设备的 I/O 请求

杀毒软件的文件实时监控、磁盘加密软件等,通常就是通过它来实现的

常见的核心系统驱动

内核核心模块

不是传统驱动,但居于内核中心

ntoskrnl.exe

它是操作系统内核本身,是所有内核活动的核心

可以把它理解为驱动管理器最根本的驱动,虽然它实际上并非严格意义上的设备驱动

在系统启动时,ntoskrnl.exe会加载一系列其它核心模块(DLL 和 SYS):

  • hal.dll:硬件抽象层(HAL)
  • kdcom.dll:内核调试通信
  • bootvid.dllci.dll:安全校验、驱动签名
  • 各类驱动程序:acpi.systcpip.sysdisk.sys

它是整个内核态模块的中枢,所有其他的驱动程序都需要在它提供的框架内运行,并由它来管理和调度

内核支持模块

DLL类内核组件,没有设备对象,也不处理IRP,不是传统驱动,但运行在内核态,是 OS 的底层模块

hal.dll

硬件抽象层,屏蔽不同主板和 CPU 的差异,为内核提供统一接口

kdcom.dll

内核调试通信模块,用于系统调试时与调试器通信

ci.dll

负责代码完整性校验与驱动签名验证

bootvid.dll

开机阶段的简易显示输出,属于引导组件

内核设备驱动程序

是真正严格意义上的驱动

tcpip.sys

负责TCP/IP协议栈的核心驱动,处理所有网络数据包的收发。属于网络类驱动的代表

ntfs.sys

NTFS 文件系统的核心驱动,负责硬盘上所有文件的读写和管理

它实现了文件系统层的功能,如读写文件、挂载卷等

acpi.sys

负责高级配置与电源管理接口(ACPI),处理系统电源管理事件,如休眠、唤醒、电源按钮等

它属于系统级别的 ACPI 驱动

partmgr.sys

管理磁盘的分区结构

storport.sys

提供与存储控制器(如 SATA、SCSI 控制器)之间的通信通道

disk.sys

硬盘设备驱动,处理底层磁盘I/O请求

它与partmgr.sysstorport.sys等共同构建块设备访问层

输入与图形类驱动

kbdclass.sys/mouclass.sys

键盘和鼠标的类驱动程序,负责处理来自这些输入设备的通用请求

它不直接与硬件通信,而是接收底层端口驱动(如i8042prt.sys)上传的输入数据

dxgkrnl.sys

DirectX图形内核驱动,是Windows显示驱动模型(WDDM)的核心,负责与GPU交互、调度图形任务

驱动开发支持框架模块

Wdf01000.sys

Windows驱动程序框架(WDF)的一部分,是KMDF驱动的运行时组件,为许多现代驱动提供了统一的驱动模型支持

WdfLdr.sys

WDF加载器,负责驱动初始化

WinUsb.sys

WDF提供的USB驱动支持

驱动栈

什么是驱动栈

当用户在用户层发出一个文件读写请求时,比如ReadFile("D:\\example.txt"),这个请求并不是直接送到硬盘,而是要经过多个驱动程序处理,每个驱动做自己的那一部分

这种结构就叫驱动栈

驱动栈的结构

下面是一个典型的Windows 存储驱动栈:

ntfs.sys(文件系统驱动)         ← 顶层(用户文件访问)
volmgr.sys(卷管理器驱动)
partmgr.sys(分区管理器驱动)
disk.sys(磁盘驱动)
storport.sys(存储端口驱动)
Miniport Driver(存储控制器驱动) ← 底层(控制硬件)

驱动栈工作过程

  1. 应用程序发起 I/O 请求 用户程序调用ReadFile()WriteFile()等API,也就是进行了涉及请求访问硬件或设备的系统调用
  2. 文件系统驱动处理 ntfs.sys解析文件路径、权限等,将文件操作转换为卷层面的块设备请求
  3. 卷管理器驱动处理 volmgr.sys管理逻辑卷,确定具体的物理磁盘分区
  4. 分区管理器驱动处理 partmgr.sys负责管理磁盘分区表,转发请求到正确的物理分区
  5. 磁盘驱动处理 disk.sys负责和物理磁盘设备交互,生成适合硬件的请求
  6. 存储端口驱动处理 storport.sys是与存储控制器通信的中间层,负责协议和请求队列管理
  7. 迷你端口驱动处理 由硬件厂商编写,具体实现与存储控制器硬件的交互
  8. 硬件执行请求 最终请求被硬件执行,数据被读写

这个过程中,每一层都可以处理请求、修改请求、拦截请求,层层检查

其它类型的驱动栈

除了存储,还有很多其他的驱动栈:

USB 驱动栈(简化):

usbhub.sys(USB集线器驱动)      ← 顶层(最高层)
usbccgp.sys(通用USB类驱动)
winusb.sys(用户模式USB驱动)
usbport.sys(USB端口驱动)
Miniport Driver(控制器驱动)   ← 底层(最接近硬件)

网络驱动栈(NDIS):

tcpip.sys(TCP/IP 协议驱动)      ← 顶层(协议层)
ndis.sys(NDIS 中间层驱动)
Miniport Driver(网卡驱动)        ← 底层(硬件驱动)

显示设备驱动栈(Windows显示驱动模型WDDM)

dxgkrnl.sys(DirectX 图形内核驱动)  ← 顶层(图形API接口)
display.sys(显示驱动核心)
Miniport Driver(显卡硬件驱动)        ← 底层(显卡硬件控制)

音频设备驱动栈

audiosrv(音频服务)                 ← 顶层(用户模式服务)
sysaudio.sys(系统音频驱动)
HDAudio.sys(高定义音频驱动)
Miniport Driver(声卡厂商驱动)        ← 底层(硬件控制)

IRP

IRP 是什么

IRP的全称是I/O Request Packet,即I/O 请求包,是内核中用于描述和传递所有I/O请求的核心数据结构

当一个I/O请求产生时,I/O管理器会创建一个IRP对象,这个IRP就像一张填写好的快递单,上面包含了所有与本次请求相关的信息,如操作类型、数据缓冲区地址、目标设备等

之后,I/O管理器会将这张快递单发送给目标设备对应的驱动程序栈,驱动程序通过处理这些 IRP 来完成工作

恶意软件经常通过IRP挂钩的技术来劫持系统功能,它会找到一个正常驱动的IRP处理函数地址,并将其替换为指向自己恶意代码的地址。这样,所有发往正常驱动的IRP都会先被恶意软件截胡,从而实现键盘记录、文件隐藏等功能

IRP在驱动栈中的传递

IRP创建

应用程序调用如ReadFile(),系统调用相关内核服务,内核根据请求生成一个IRP

IRP包含请求类型、缓冲区地址、请求参数等信息

IRP发送到驱动栈顶层驱动

例如,磁盘驱动栈中,顶层可能是文件系统驱动(ntfs.sys

内核将IRP传递给顶层驱动的Dispatch函数

驱动处理IRP

驱动检查IRP内容,执行相应操作(如文件系统解析、数据缓存等)

驱动可以完成请求,或者决定将IRP向下传递

IRP向下传递给下一个驱动

驱动调用IoSkipCurrentIrpStackLocation,跳过当前驱动的IRP堆栈位置

然后调用IoCallDriver把IRP传给下一个驱动,通常是更底层的驱动

依次传递直到最底层驱动

IRP会层层传递,直到最底层的硬件驱动处理它

底层驱动发起对硬件的实际操作,比如读写磁盘、发送网络包

IRP完成

硬件操作完成后,底层驱动调用IoCompleteRequest标记 IRP 完成

IRP 结果逐层向上传递回去,每层驱动可以执行清理或后处理,最终结果反馈给系统和应用程序

应用程序请求
IRP创建
ntfs.sys(文件系统驱动)        ← 顶层
     ↓ 传递IRP
volmgr.sys(卷管理器驱动)
     ↓ 传递IRP
partmgr.sys(分区管理驱动)
     ↓ 传递IRP
disk.sys(磁盘驱动)
     ↓ 传递IRP
storport.sys(存储端口驱动)
     ↓ 传递IRP
Miniport Driver(硬件控制驱动)  ← 底层
     ↓ 硬件操作
硬件设备完成请求
     ↓ 结果返回
Miniport Driver
     ↓ 返回
storport.sys
     ↓ 返回
disk.sys
     ↓ 返回
partmgr.sys
     ↓ 返回
volmgr.sys
     ↓ 返回
ntfs.sys
     ↓ 返回
操作系统

常见IRP主要功能码

每个IRP都包含一个主要功能码,用于定义本次请求的核心操作类型

文件与设备管理

IRP_MJ_CREATE

打开或创建文件、设备句柄

IRP_MJ_CLOSE

关闭文件或设备句柄

IRP_MJ_READ

从文件或设备读取数据

IRP_MJ_WRITE

向文件或设备写入数据

IRP_MJ_QUERY_INFORMATION

查询文件或设备信息(大小、属性等)

IRP_MJ_SET_INFORMATION

设置文件或设备信息(修改属性等)

设备控制

IRP_MJ_DEVICE_CONTROL

向驱动发送自定义控制命令(用户模式到驱动的常用命令)

IRP_MJ_INTERNAL_DEVICE_CONTROL

驱动间内部控制命令,供驱动程序之间通信使用

即插即用(PnP)

IRP_MJ_PNP

即插即用事件通知,如设备启动、停止、移除、资源分配等

电源管理

IRP_MJ_POWER

电源状态管理请求,如休眠、唤醒、电源状态改变等

文件系统控制

IRP_MJ_FILE_SYSTEM_CONTROL

文件系统特定控制请求,如卷挂载、卸载等

系统管理

IRP_MJ_SHUTDOWN

系统关机或重启时通知驱动准备关闭

IRP_MJ_CREATE_MAILSLOT

创建邮件槽(用于进程间通信)

IRP_MJ_QUERY_SECURITY

查询安全描述符

IRP_MJ_SET_SECURITY

设置安全描述符

其他常见功能码

IRP_MJ_QUERY_VOLUME_INFORMATION

查询卷信息

IRP_MJ_SET_VOLUME_INFORMATION

设置卷信息

IRP_MJ_CLEANUP

清理(关闭时,释放资源等)

内核回调

什么是内核回调

内核回调(Kernel Callbacks) 是一种事件驱动的机制

它允许内核模式的驱动程序向操作系统内核注册特定的系统级事件,当这些被注册的事件发生时,内核会暂停其正常操作流程,并逐一回调所有已注册的驱动程序提供的函数,即回调例程

这是内核提供的一个事件通知系统,驱动程序不再需要通过不断轮询的方式来检查某个状态是否改变,而是可以被动地、在事件发生时才被内核精准地唤醒和调用

示例(用户态):

void OnButtonClick() {
    printf("按钮被点击!\n");
}
RegisterButtonCallback(OnButtonClick); // 注册回调

系统中,“按钮被点击”事件发生后,会自动调用注册的OnButtonClick()函数

内核回调工作过程

注册

在驱动程序的初始化阶段(通常是在DriverEntry函数中),驱动程序会调用一个由内核导出的、特定于事件类型的注册函数。例如,要监控进程创建,驱动程序会调用PsSetCreateProcessNotifyRoutine

在调用时,驱动程序会将一个指向自己内部实现的回调函数的地址,作为一个参数传递给内核

内核接收到这个请求后,会将这个函数地址添加到一个与该事件类型相关联的、内部维护的回调函数指针链表中

触发

当一个被订阅的系统事件发生时(例如NtCreateUserProcess系统调用被执行以创建一个新进程),内核在完成其核心操作的某个特定阶段,会暂停下来

调用

内核会找到与该事件对应的回调函数指针链表,然后,它会依次遍历这个链表,并同步地调用每一个已注册的回调函数

内核会将与事件相关的上下文信息(例如,对于进程创建事件,会传递新进程的PID、创建者信息等)作为参数传递给回调函数

处理

驱动程序的回调函数被调用后,便可以在其函数体内执行自定义的逻辑

它可以仅仅记录该事件,也可以进行干预,例如阻止该事件的完成(如果回调类型允许的话)

常见内核回调

进程与线程相关回调

PsSetCreateProcessNotifyRoutine

  • 功能:当进程创建或退出时触发
  • 用途:监控进程行为(如杀毒软件、行为分析等)

PsSetCreateThreadNotifyRoutine

  • 功能:当线程被创建或销毁时触发
  • 用途:检测线程注入、远程线程创建

映像加载相关回调

PsSetLoadImageNotifyRoutine

  • 功能:当模块(如EXE、DLL)被加载到进程时触发
  • 用途:监视DLL是否注入或加载非法模块

注册表操作回调

CmRegisterCallback/CmRegisterCallbackEx

  • 功能:在注册表被访问(读写、删除等)时触发
  • 用途:拦截修改注册表、保护启动项

文件系统监控回调

FsRtlRegisterFileSystemFilterCallbacks

  • 功能:文件系统过滤驱动使用的回调注册接口
  • 用途:监视文件访问、隐藏或加密文件等(如勒索软件防护、杀毒软件)

电源管理与关机回调

IoRegisterShutdownNotification

  • 功能:注册系统关机前通知
  • 用途:清理资源、保存数据、记录日志

PoRegisterPowerSettingCallback

  • 功能:注册电源策略变化回调(如睡眠、唤醒)
  • 用途:管理节能策略或唤醒唤醒设备

蓝屏 (BugCheck) 回调

KeRegisterBugCheckCallback

  • 功能:系统蓝屏前调用,允许驱动记录崩溃状态
  • 用途:记录调试信息、保存崩溃快照

Windows事件通知类回调

ExRegisterCallback

  • 功能:内核模块之间广播通知
  • 用途:驱动之间共享事件信息(如热插拔、电池状态)

网络相关回调

FwpsCalloutRegister

  • 功能:用于网络过滤和处理框架Windows Filtering Platform注册网络流量过滤回调
  • 用途:监控/拦截网络通信(如防火墙、DLP系统)

NdisRegisterProtocolDriver

  • 功能:注册协议驱动的接收/发送回调函数
  • 用途:网络监控、抓包、VPN实现等

安全与对象管理回调

ObRegisterCallbacks

  • 功能:监控对象句柄操作,如进程句柄、线程句柄
  • 用途:防止进程被调试、注入、终止(常用于反作弊或病毒保护)
距离小站第一行代码被置下已经过去
使用 Hugo 构建
主题 StackJimmy 设计
...当然还有kakahuote🤓👆