Kernel PWN
如何理解内核:
操作系统内核(Operation System Kernel)本质上也是一种软件,可以看作是普通应用程式与硬件之间的一层中间层,其主要作用便是调度系统资源、控制 IO 设备、操作网络与文件系统等,并为上层应用提供便捷、抽象的应用接口。操作系统内核实际上是我们抽象出来的一个概念,本质上与用户进程一般无二,都是位于物理内存中的代码 + 数据,不同之处在于当 CPU 执行操作系统内核代码时通常运行在高权限,拥有着完全的硬件访问能力,而 CPU 在执行用户态代码时通常运行在低权限环境,只拥有部分 / 缺失硬件访问能力。
分级保护域:
分级保护域又称为保护环,简称 Rings,是一种将计算机不同资源划分至不同权限的模型:

CPU 的权限主要分为 0-3 四级,基本常用的就是用 Ring0 和 Ring3,对应操作系统内核与用户进程,CPU 在执行用户进程代码时处于 Ring3 下:
现代 CPU 架构在硬件层面就设计了权限等级。
- 用户态 (User Space / Ring 3): 普通程序运行的地方。权限极低,如果代码尝试直接访问硬件或越权读取内存,CPU 会直接触发异常(比如常见的
Segmentation fault)。 - 内核态 (Kernel Space / Ring 0): 内核代码运行的地方。拥有最高权限,可以执行特权指令,掌控所有硬件资源。在异构架构中,这种权限划分同样存在,比如 ARM 的不同异常级别,Exception Levels, 如 EL0 对应用户态,EL1 对应内核态。
状态切换:
- 中断与异常
- 特权级相关指令
现代操作系统的开发者包装出了系统调用(syscall),作为由”用户态 “切换到” 内核态“的入口,从而执行内核代码来完成用户进程所需的一些功能。当用户进程想要请求更高权限的服务时,便需要通过由系统提供的应用接口,使用系统调用以陷入内核态,再由操作系统完成请求。
当发生系统调用,产生异常,外设产生中断等事件时,会发生用户态到内核态的切换,具体的过程为:
- 通过 swapgs 切换 GS 段寄存器,将 GS 寄存器值和一个特定位置的值进行交换,目的是保存 GS 值,同时将该位置的值作为内核执行时的 GS 值使用。
- 将当前栈顶记录在 CPU 独占变量区域里,将 CPU 独占区域里记录的内核栈顶放入 rsp/esp。
- 通过 push 保存各寄存器值
- 通过汇编指令判断是否为
x32_abi。 - 通过系统调用号,跳到全局变量
sys_call_table相应位置继续执行系统调用。
退出时,流程如下:
- 通过
swapgs恢复 GS 值。 - 通过
sysretq或者iretq恢复到用户控件继续执行。如果使用iretq还需要给出用户空间的一些信息(CS, eflags/rflags, esp/rsp 等)。
虚拟内存分布:
虚拟内存分为供用户使用的用户空间和供内核使用的内核空间:可以分为 32 和 64 位不同模式:


进程权限管理:
进程描述符:源码在include/linux/sched.h中,task_struct结构体结构如下:

重要字段结构化表格
| 核心类别 | 字段 (Fields) | 功能说明 (Description) |
|---|---|---|
| 调度相关 | state, sched_class |
调度信息 |
prio, static_prio |
进程的动态优先级与静态优先级。 | |
se, rt_priority |
调度器实体 | |
| 内存管理 (掌控虚拟地址空间) |
mm, active_mm |
mm指向进程虚拟内存空间的描述符。内核线程没有自己的用户空间,借用前一个进程的active_mm |
stack, thread_info |
指向该进程专属的内核栈,以及通常存放在栈底部的线程硬件信息。 | |
| 标识与控制 (进程的身份户口) |
pid, tgid |
pid是内核中每个执行流的唯一 ID。而在多线程程序中,所有线程对外表现为一个整体,共享同一个 tgid |
real_parent, children |
父子关系管理 | |
| 资源管理 | files, fs, signal |
文件,文件系统和信号资源 |
cred, limits |
权限与资源限制 | |
| 统计与架构 | utime, stime |
CPU 时间 |
cpu_context, thread |
当内核执行进程切换时,需要把当前 CPU 寄存器的状态保存在这里,以便下次恢复执行。 | |
| 安全与调试 |
ptrace, seccomp |
ptrace是 GDB 等调试器能够附加和控制该进程的底层支持;seccomp则是一种沙盒安全机制,用于过滤和限制该进程能执行的系统调用。 |
KernelPWN 环境搭建:
配置高级调试器 (GEF)
原生 GDB 调试内核会很吃力,我们需要安装 GEF 插件和相关依赖。
1 | pip3 install capstone unicorn keystone-engine ropper |
下载并编译纯净版内核
我们将亲自编译出一个带有调试符号的内核 (vmlinux)。
下载并解压 Linux 5.9.8 源码:
1 | curl -O -L https://mirrors.tuna.tsinghua.edu.cn/kernel/v5.x/linux-5.9.8.tar.xz |

配置内核编译选项 (极度关键)
保证编译器在编译时保留所有的调试符号,这样以后用 GDB 才能看清里面的变量和源码。
1 | make menuconfig |

在这个界面里,使用键盘的上下方向键移动,回车键进入子菜单,空格键用来勾选(前面会出现 [*] 标志),按 Esc 键两次可以退回上一级。
- 找到目标:使用方向键 ↓ 一直往下按,直到光标停在最后一行
Kernel hacking --->上。 - 进入子菜单:按下
回车键 (Enter)。
接下来,你会看到更深一层的选项,继续执行这几个关键勾选:
路径 I)开启编译调试信息:进入
Compile-time checks and compiler options --->- 找到
Compile the kernel with debug info,按空格键把它变成[*]。
- 找到
路径 II)开启 KGDB 调试器支持
1
2
3退出来按 `Esc`:进入 `Generic Kernel Debugging Instruments --->`
找到 `KGDB: kernel debugger`,按 `空格键` 变成 `[*]`。
- 保存并退出:
- 一直按两下 Esc 退到最外层主界面。
- 再按两下 Esc,系统会弹窗问你 Do you wish to save your new configuration?。
- 选择 < Yes > 回车保存。
修改.config
内核编译脚本在默认情况下会去寻找指定的 .pem 证书文件来为内核模块进行签名。由于你的环境中没有对应的 Debian 官方私钥和证书,如果不置空,编译进行到一半就会报错 No rule to make target ‘debian/certs/debian-uefi-certs.pem’ 并直接中断。
执行静默替换
1 | sed -i 's/CONFIG_SYSTEM_TRUSTED_KEYS=.*/CONFIG_SYSTEM_TRUSTED_KEYS=""/' .config |
在较新的 Kali/Ubuntu 环境下编译 5.9.8 这种稍旧的内核,有时会遇到 BTF (BPF Type Format) 相关的编译错误。为了保险起见,建议你也把下面这一项关掉(改为 n):
1 | sed -i 's/CONFIG_DEBUG_INFO_BTF=y/CONFIG_DEBUG_INFO_BTF=n/' .config |
如果不关掉它,编译时可能需要额外的 pahole 工具,且容易报错中断。

编译内核(需要几十分钟,视电脑性能而定)
1 | sudo apt-get install libelf-dev # 安装可能缺失的依赖 |
编译成功后,你会看到 Setup is… System is… bzImage is ready 的提示。带符号的 vmlinux 就在当前目录下

编译 BusyBox (提供基础命令)
内核只是个引擎,还需要 ls, cd, sh 这些基础命令,由 BusyBox 提供。
- 下载并解压:
1 | wget https://busybox.net/downloads/busybox-1.34.1.tar.bz2 |

配置静态编译:输入 make menuconfig。
- 进入
Settings,勾选Build static binary (no shared libs)。 - (可选)在
Linux System Utilities中取消选中Support mounting NFS...。 - 保存并退出。
我现在的 kernelpwn 环境完整吗
参考资料:https://collectcrop.github.io/blog/2025/02/22/kernel-pwn%E5%88%9D%E6%8E%A2/#kernel-rop
更新: 2026-05-08 01:58:24
原文: https://www.yuque.com/idcm/wnemg9/zawntpmoci7rqq3c