5.7 KiB
5.7 KiB
NetBSD:按需自动加载模块 / 兼容性约束 / 模块路径布局
NetBSD 的 loader 与 Linux 很像,但模块模型不同:**内核可以在需要时自动加载模块**。于是现场出现:
没有 modload
没有明显的启动脚本
但 xyz.kmod 突然进了内核
不一定存在隐藏 loader——可能只是:
操作请求 → 内核 module 子系统按需加载(依赖模块会被递归带上)
这对时间线逆向很关键:模块出现的时间点对应的是某个操作,而不是某个进程。 </CORE RULE>
一、按需加载与自动卸载
- 内核组件用
module_autoload(name, class)定位并加载可选的系统组件(也用于加载其他模块所需的依赖) - 典型触发:第一次挂载某类文件系统时,对应模块被自动带入
- 自动卸载:自动加载的模块会被标记为可自动卸载——一个内核线程在加载后不久(当前实现约 10 秒)尝试卸载,走
MODULE_CMD_AUTOUNLOAD,模块可以返回非零来拒绝 - 若设置了
kern.module.autounload_unsafe,则返回ENOTTY(表示未审计为可安全自动卸载)的模块也可能被自动卸载 noautoload属性(同名.plist里设为 true)使系统拒绝加载该模块- 手工用 modload 加载的模块永远不会被自动卸载
二、兼容性:模块只与特定版本的内核配套
- loader 会检查模块版本与
__NetBSD_Version__的兼容性;不匹配时打印类似module 'X' built for 'N', system 'M'的警告 - 不带 force 标志则加载失败(
EPROGMISMATCH) MODCTL_LOAD_FORCE(modload -f)会强制加载,并记录forced load, system may be unstable——这是官方明确警告过的路径,不兼容模块可能造成数据损坏或崩溃- 同一标志还能强制重新加载被禁用的内建模块:卸载内建模块会把它标记为禁用,之后只有带 force 才能再加载
MODCTL_NO_PROP会跳过定位模块的.plist属性字典(默认会去找同名.plist并合并属性)
所以:
模块 ELF 看起来正常、符号也在、load 就失败
→ 先查 exact kernel version/build 与模块兼容性
→ 而不是先怀疑 loader 或文件损坏
三、模块放在哪:路径不是永久契约
- 传统布局:内核在
/netbsd,调试内核/netbsd.gdb,模块在/stand/<arch>/<version>/modules/ - 自 2025-04-28 起(UPDATING 20250427)新增构建选项
KERNEL_DIR:把内核、其调试内核与内核模块放进同一个以内核命名的目录——
旧: /netbsd(内核)、/netbsd.gdb、/stand/amd64/<版本>/modules
新: /netbsd/kernel、/netbsd/kernel.gdb、/netbsd/modules
- 该选项只针对 i386 与 amd64,且完全是可选的(同一台机器上两种布局都可能有)
- 需要更新过的三级 bootloader(GPT/EFI 引导需更新 EFI 分区上的引导程序);bootloader 会按序尝试
/netbsd/kernel、/onetbsd/kernel、/netbsd.old/kernel - 已知问题:用了
KERNEL_DIR但内核仍以文件形式放在/netbsd时,dmesg会报出错误的kern.module.path
RE/取证含义:硬编码 /stand/... 的工具会开始漏样本;判断"模块在哪"应当读运行时报告(kern.module.path)或按两种布局分别探测。
四、其他现场约束
- MODULAR 内核启动失败的常见原因是模块缺失或 bootloader 无法加载,症状可能表现为
Cannot mount root, error 79一类——不要先当成磁盘/内核损坏 - 模块加载的分寸:模块成为内核的一部分;只有 securelevel ≤ 0 或内核编译时带
INSECURE才允许加载新模块
决策树
拿到 NetBSD 目标
├─ 模块"自己出现了"
│ └─ 先按 module 按需自动加载解释(含依赖递归),对齐到触发它的操作
├─ 模块不见了
│ └─ 查是否被自动卸载(约 10 秒后的尝试)或 noautoload/禁用状态
├─ 模块加载失败但文件看着正常
│ ├─ 查 exact kernel version/build 与兼容性
│ └─ 别直接上 -f/强制加载(官方警告可能不稳定/损坏数据)
├─ 找不到模块文件
│ ├─ 读运行时的模块路径报告
│ └─ 按 /stand/<arch>/<ver>/modules 与 <kernel>/modules 两种布局分别探测
└─ 启动失败
└─ 先查模块缺失/bootloader 能否加载,再谈内核或磁盘
工具与验证
- 模块子系统:modctl 一族的工具(加载/卸载/查询)与模块的
.plist属性 - 路径与布局:运行时的模块路径报告、两种布局的目录探测、bootloader 的查找顺序
- 兼容性:模块版本与内核版本报告的比对
- 验证:能同时说清「模块为何出现/消失(自动加载或卸载 vs 手工)+ 版本兼容状态 + 实际路径布局」
该平台的坑(汇总)
- 找不到显式加载动作就判"模块没加载":可能是按需自动加载(含依赖递归)
- 把自动卸载当"模块被恶意移除":自动加载的模块会被尝试卸载,且模块自己可以拒绝
- 忽略
.plist的noautoload:那会让系统拒绝加载,不是文件损坏 - 加载失败就去改 loader/patch 模块:先核对内核版本与兼容性
- 随手
-f强制加载:官方明确警告不兼容模块可能损坏数据或崩溃 - 以为卸载内建模块后就干净了:它只是被标记禁用,仍需 force 才能再加载
- 硬编码
/stand/<arch>/<version>/modules:新布局把模块放到内核目录下,会漏样本 - 把启动失败直接当内核/磁盘损坏:先查模块缺失与 bootloader 加载能力