文章

CPU中的“模式位”

CPU 中的“模式位”用于区分当前运行环境是内核态(Ring 0)还是用户态(Ring 3),决定程序访问权限和指令执行范围。

CPU中的“模式位”

CPU中的“模式位”

操作系统(OS)处于软件和硬件之间,它管理内存、磁盘、网络、外设,并为用户程序提供接口。但问题来了:

为什么用户程序不能直接操作硬件?为什么一定要通过操作系统?

因为硬件资源是共享的。操作系统必须防止恶意程序(或者有bug的程序)直接操作硬件,否则可能导致数据损坏、隐私泄露甚至系统崩溃。

为了实现这个隔离,处理器使用了一个非常关键的机制:CPU模式控制(mode bit)

CPU的两种运行模式

现代处理器至少支持两种运行模式:

模式权限举例
内核模式可执行所有指令OS内核、驱动、BIOS等
用户模式禁止特权操作(如I/O)普通程序如微信、Chrome等

这种设计是一种“硬件隔离”:即使你用C语言写了个访问硬件端口的程序,CPU也不会让你执行——因为你在用户模式中,执行不了特权指令

模式位(Mode Bit)如何工作?

在x86架构中,控制权限的是某些控制寄存器,但在抽象层面我们可以理解为:

+----------------------------+
|         Mode Bit           |
+----------------------------+
|  0 = 用户模式(User Mode)   |
|  1 = 内核模式(Kernel Mode) |
+----------------------------+

处理器每次执行指令时会检查当前模式位,只有在内核模式下,CPU才允许执行敏感操作

  • 操作磁盘寄存器
  • 修改页表
  • 控制中断
  • 修改系统定时器
  • 重启或关闭系统

例子:用户程序访问显卡失败

假设你有一段C代码:

1
2
unsigned char* vmem = (unsigned char*) 0xB8000;
vmem[0] = 'X';

你试图通过直接写显存来在屏幕上打印字符 'X'。这在裸机环境(如操作系统开发)中有效,但在用户态运行时会出错:

1
Segmentation fault (core dumped)

因为地址 0xB8000 是显卡显存,操作系统不会允许你直接写这个地址,更别说现代系统早已开启虚拟内存,映射规则也不同。

那用户程序怎么访问硬件?

通过系统调用(System Call),也就是“请求内核帮忙做特权操作”。

整个过程大致如下:

用户程序(用户模式,Mode Bit = 0)
        │
        │ 触发系统调用,如 write()
        ▼
CPU触发软中断(如 INT 0x80)
        │
        ▼
CPU自动:
  - 切换到内核栈
  - 设置 Mode Bit = 1(内核模式)
  - 跳转到内核中断处理例程
        │
        ▼
内核执行 write(),将内容写入磁盘/屏幕
        │
        ▼
内核返回:
  - Mode Bit = 0(用户模式)
  - 返回用户程序继续运行

这就是所谓的“陷入(Trap)”机制,CPU在硬件上强制完成模式切换,用户程序不能伪造这一过程

系统调用举例:write()

1
write(fd, "Hello", 5);
  • write() 是 libc 提供的函数,看似普通函数,其实底层执行的是 syscallint 0x80
  • CPU自动切换到内核态执行真正的硬件写入
  • 完成后返回用户态

这是为什么C语言中“标准库函数”并不等于“系统调用”:只有那些最终陷入内核的才算。

硬中断 vs 软中断

类型触发源举例
硬中断硬件设备键盘按键、鼠标移动
软中断程序主动触发系统调用(如 int 0x80

两者都会进入中断处理流程,但:

  • 用户程序无法触发硬中断
  • 用户程序只能通过指定接口请求软中断

为什么需要硬件支持?

你可能会问:我是不是可以直接调用系统调用的汇编指令伪造操作?

不能!因为系统调用入口地址是只读的、隐藏的,而且你的用户程序根本不能修改模式位。

CPU内部电路强制规定:

  • 只有通过受控中断门(Interrupt Gate)才能切入内核
  • 模式位切换只能由 CPU 控制
  • 用户态不能访问内核态栈或页表

内核模式危险吗?

非常危险。内核中的错误不像用户程序那样“Segfault退出”,而是直接导致整个系统崩溃(kernel panic)

因此,驱动程序、内核模块、系统调用实现必须极度谨慎:

1
2
// 错误的驱动代码
*null_ptr = 42; // 会内核崩溃

内核态驱动程序:既强大又危险的系统组件

驱动程序(Device Driver) 是一种特殊的系统软件,它运行在 内核态(Ring 0),负责操作系统与硬件之间的通信,比如磁盘、键盘、显卡、网卡,甚至是虚拟设备(如反作弊模块)都靠驱动控制。

但驱动不仅仅用于硬件控制 —— 很多软件功能,如杀毒、调试器、虚拟机、反作弊系统等,也会以“驱动”的形式直接在内核中运行,从而获得对系统的最高访问权限

Ring 0:驱动程序的特权地位

在现代操作系统(如 Windows、Linux)中,系统执行权限分为多个级别,称为 “环(Ring)”。

Ring 0:内核态(最高权限) → 操作系统内核、驱动程序运行于此
Ring 3:用户态(最低权限) → 应用程序(浏览器、游戏等)运行于此

处于 Ring 0 的驱动程序拥有绝对控制权,可以:

  • 访问任何进程内存(包括用户态程序的数据)
  • 直接调用 CPU 指令、操纵中断表(IDT)或系统服务表(SSDT)
  • 控制硬件 I/O,例如读写磁盘、网络通信
  • 修改内核行为或替换系统函数(Hook)

统一的内核地址空间:风险根源

内核态的驱动程序共享同一个地址空间,这意味着:

所有驱动、公用内核代码都位于同一块高地址内存区域,没有隔离。

简化内存结构(x64 架构):

+--------------------------+
|  内核空间(共享区域)        |
|  - 操作系统核心模块         |
|  - 所有加载的驱动程序       |
+--------------------------+
|  用户空间(每进程独立)      |
|  - 进程 A                 |
|  - 进程 B                 |
+---------------------------+

这使得驱动:

  • 能操作一切,但
  • 一旦出错就影响全局

驱动程序出错可能导致蓝屏

因为驱动运行在核心地址空间,一旦有 bug 或错误操作,比如:

  • 访问了无效内存地址(空指针或越界)
  • 使用了已释放的对象
  • 锁顺序错误导致死锁
  • 非法操作页表或中断向量

Windows 就会立刻触发蓝屏(BSOD)来保护系统。

常见驱动相关蓝屏错误:

  • IRQL_NOT_LESS_OR_EQUAL
  • PAGE_FAULT_IN_NONPAGED_AREA
  • KERNEL_SECURITY_CHECK_FAILURE
  • DRIVER_IRQL_NOT_LESS_OR_EQUAL

驱动程序在安全软件与反作弊中的作用

许多软件会出于“系统控制”的需要,在用户同意下安装自己的驱动模块,典型包括:

1. 杀毒软件(Antivirus)

如 BitDefender、ESET、360、Windows Defender 等会安装内核驱动,以实现:

  • 实时拦截系统调用(监控文件读写、网络连接)
  • 检测 Rootkit、隐藏进程、非法驱动加载
  • 控制防火墙规则
  • 修复关键内核结构(如 SSDT、IDT)
2. 游戏反作弊系统(Anti-Cheat)

如 Vanguard(Valorant)、EasyAntiCheat(EAC)、BattlEye、XIGNCODE 等,使用驱动来:

  • 拦截外挂读取游戏内存(如 NtReadVirtualMemory
  • 阻止 DLL 注入、内联 Hook
  • 扫描内核模块列表,识别未签名驱动
  • 禁止调试器、虚拟机运行
用户启动游戏 → 驱动加载 → 监视所有系统行为
                         ↘ 阻止注入/调试
                          ↘ 扫描非法驱动/模块

为什么它们都要用内核驱动?

  • 因为外挂、病毒本身也常以驱动形式运行在内核态
  • 如果反作弊/杀毒只在用户态运行,无法检测也无法阻止 Ring 0 的行为
  • 所以只能用“以毒攻毒”的方式——同样用驱动进入内核,与其抗衡
本文由作者按照 CC BY 4.0 进行授权