NOTE这篇很长。看不完可以收藏,以后需要的时候再看。
.nbpf 是什么
.nbpf(Nebula Plugin File)是 NebulaShell 的二进制插件打包格式。
全称 “Nebula Plugin File”,本质上是一个经过多重加密、多重签名、多层嵌套的 ZIP 容器。
跟普通的 ZIP 或 JAR 不一样——普通 ZIP 一解压就全看见了,.nbpf 从设计之初就把代码保护、防篡改和发布者身份验证作为核心目标。
你看不到里面有什么
一个 .nbpf 文件,用普通的解压软件是打不开的。
不是因为格式特殊,是因为它被加密了。你看到的是一堆二进制乱码。
理论上它内部是一个标准 ZIP 归档,布局是这样的:
- META-INF/ — 元数据和加密信息区
- MANIFEST.MF:完整元数据,加密存储
- SIGNATURE:外层 Ed25519 签名
- SIGNER.PEM:签名者的公钥
- ENCRYPTION:RSA-OAEP 加密的外层 AES 密钥
- INNER_SIGNATURE:中层 RSA-4096 签名
- INNER_ENCRYPTION:RSA-OAEP 加密的中层 AES 密钥
- MODULE_SIGS:内层 HMAC 签名列表
- PLUGIN.MF:明文公开的四个字段(name、version、author、description),只用于索引发现
- NIR/ — 编译后的中间表示目录
- 所有 Python 源码被编译为序列化 code object(NIR),经过两层 AES-256-GCM 加密存储
- RES/ — 资源文件目录
- 非脚本文件(图片、配置等)以明文存储
CAUTION这是内部结构。普通用户看不到,也不需要看到。
五层安全体系
从外到内,一共五层。层层递进,每一层解决不同的问题。
第一层:外层 Ed25519 签名
验证整个包的签名,确保包没有被中间人篡改。验签公钥由包内提供,同时支持本地信任列表校验。这是最外层的完整性检查。
第二层:外层 AES-256-GCM 加密
使用随机生成的 key1 加密 META-INF 区域。key1 本身通过 RSA-OAEP-4096 封装。只有持有对应 RSA 私钥的加载者才能解开。没有私钥,连 META-INF 里的内容都看不到。
第三层:中层 RSA-4096-PSS 签名
对 NIR 密文摘要进行签名,验证插件作者的合法身份。这个签名存储在加密后的 META-INF 中,外部无法读取。只有先解开第二层加密,才能看到这个签名。
第四层:中层 AES-256-GCM 加密
使用 key2 加密 NIR 数据(插件代码的编译结果)。key2 同样通过 RSA-OAEP 封装。两层密钥独立生成,互不依赖。即使有人破解了外层,代码还是加密的。
第五层:内层 HMAC-SHA256 签名
对每个解密后的 NIR 模块逐一签名,防止模块被替换或重排。HMAC 密钥由 key1 和 key2 通过 HKDF-SHA256 派生。这一层确保每个模块的完整性。
每一层用不同的密钥,每一层有独立的作用。外层负责传输安全,中层负责身份验证和代码安全,内层负责模块完整性。
NIR 编译系统
NIR 全称 Nebula Intermediate Representation,是 .nbpf 独有的中间表示层。
插件的所有 .py 源码在打包时,被 NIRCompiler 编译成 Python 原生的 code object,再通过 marshal 序列化成字节流。
编译过程中会执行静态安全检查:
- 拒绝 C 扩展(.so、.pyd 等二进制扩展)
- 禁止导入
os、sys、subprocess等危险模块 - 禁止使用
__import__、exec、eval、compile等动态执行函数
此外,NIRCompiler 还会在 code object 中插入无意义的花指令(junk code),增加逆向分析的难度。
TIPNIR 与平台无关。任何 Python 3.10+ 环境都可以执行编译后的插件,不需要重新编译。
打包流程
打包器做了这些事情:
- 读取 manifest.json
- 编译所有
.py为 NIR - 收集资源文件
- 生成随机 AES 密钥
- 中层加密/签名 NIR
- 内层 HMAC 签名
- 构建 META-INF
- 外层加密/签名
- 写入 ZIP
加载流程
加载器做了这些事情:
- 打开 ZIP
- 外层验签(Ed25519)
- 外层解密(RSA 解密 key1 → AES-GCM 解密 META-INF)
- 中层验签(RSA-4096)
- 中层解密(RSA 解密 key2 → AES-GCM 解密 NIR)
- 内层验签(HMAC)
- 反序列化 NIR
- 在受限沙箱中 exec
- 调用
New()创建插件实例
内存安全
解密完成后,所有密钥立即从内存中擦除。
擦除方式是三次覆写:全0 → 全0xFF → 全0。
不是简单的 del——Python 的 del 只是删除引用,内存里的数据可能还在。三次覆写是真正的清除。
信任模型
.nbpf 没有中心化的证书颁发机构。
系统维护一个本地信任公钥目录。当加载一个未被信任的作者签名的插件时,CLI 会展示:
- 作者名称
- 版本
- 公钥指纹
由用户决定是否信任。一旦信任,公钥被持久化到本地信任目录,下次加载同一作者的插件时自动放行。
NOTE没有中心服务器,没有在线验证。完全的离线、去中心化信任。
完整性验证
CLI 提供了 nebula nbpf verify 命令。
这个命令可以在不加载插件(不解密完整内容)的情况下,验证外层签名和读取公开元数据。
对于插件市场的服务端审核很有价值:服务器可以在不持有任何私钥的情况下,确认包的来源和完整性。
代码保护手段
除了加密和签名,NBPCrypto 模块还实施了一系列抗逆向措施:
- 所有 cryptography 库的导入路径通过字符串拼接动态构造
- 关键常量通过运行时计算,不硬编码
- 解密函数分散在多处,不是集中在一个地方
- 反调试检测:检测
sys.gettrace()和调试环境变量
现状
目前 .nbpf 是 NebulaShell 插件系统的唯一打包与分发格式。
整个实现分布在 oss/core/nbpf/ 目录下:
- 5 个模块
- 约 1300 行 Python 代码
- 测试在
tests/test_nbpf.py中
CAUTION.nbpf 不追求与外部生态的兼容性。它是一个为 NebulaShell 量身定制的封闭包格式。
所以你没法用其它工具打开它。
总结
五层安全体系,从外到内:
| 层级 | 加密/签名 | 作用 |
|---|---|---|
| 1 | Ed25519 | 外层签名,防止篡改 |
| 2 | AES-256-GCM + RSA | 加密 META-INF |
| 3 | RSA-4096-PSS | 中层签名,验证作者 |
| 4 | AES-256-GCM + RSA | 加密 NIR 代码 |
| 5 | HMAC-SHA256 | 内层签名,模块完整性 |
NIR 编译系统做静态检查和花指令注入。
加载完后密钥三次覆写擦除。
没有中心化证书,离线信任。
这篇写完了。
如果你能完整看完——不管看懂没看懂,都说明你对这个项目是真的感兴趣。
谢谢。
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时






