# swcenc 2.0.0 发布说明
**交付日期**:2026-10-08
**标题**:Swoole 官方同款离线加密器 —— 自带运行时(零依赖)+ 授权绑定 + 产物域名/到期绑定
---
## 一、这次交付了什么
| 交付物 | 位置 | 说明 |
|---|---|---|
| **Windows 安装包** | `dist/swcenc-2.0.0-win64-setup.exe`(43MB) | 双击安装:桌面/开始菜单快捷方式、卸载项、装完可直接跑自检 |
| Windows 免安装包 | `dist/swcenc-2.0.0-win64.zip`(72MB,解压 145MB) | 解压即用,运行 `bin\swcenc.exe` |
| Linux 发行包 | `dist/swcenc-2.0.0-linux-x86_64.tar.gz`(204MB,解压 511MB) | 自带 Python + PHP 7.0–8.4 + glibc 与全部依赖库 |
| 对外下载页 | | 两个包 + sha256,静态直出 |
| 授权签发工具 | `tools/licgen.py` + `license/private_key.pem` | **只在签发者手里**,不进发行包 |
| 引擎侧新能力 | 线上引擎 `engine/pack/` | 授权模块 / 绑定层 / 内置运行时探测 |
下载校验(与本地一致,已实测回下载比对):
```
linux 5b2cc51a3d0ac7f350dd7c804658479313da93a0f8eb99bd7cb42921186869f7
win setup.exe e4d16047738b9c6693c9d73a040243c0a774cabbb03db29f3159d1e3899eb403
win portable bd7184f40fb010970cb08d58a7a9f967112085cc84b8a18b6a41ad0eb244cca0
```
---
## 一之二、发行包里**没有源码**(本轮新增)
两条硬要求,都已落实:
| 要求 | Linux | Windows |
|---|---|---|
| 不发 Python 源码 | 引擎 12 个模块全部 **Cython 编译成 `.so`**(原生机器码),包里 `.py` 数 = 0 | 引擎编译成 **`.pyc`** 字节码(无 `.py`),再打进安装包与便携包 |
| **授权校验写进编译产物** | `swc_license` 编译进 `.so`,且 `ENFORCED_BY_BUILD` 在**构建时烘焙为 True** | 同样烘焙后再编译成 `.pyc` |
烘焙的效果(已实测):
`SWCENC_ENFORCE=0 ./bin/swcenc pack ...` —— **照样被授权拦住**。
也就是说"要不要校验授权"这件事只存在于编译产物里,客户既改不了(没有源码),也绕不过(环境变量被构建常量压过)。
线上服务用的是同一份引擎源码,其 `ENFORCED_BY_BUILD` 保持 `False`,不受影响。
构建流程:`tools/build_dist_engine.py`(复制源码到临时目录 → 烘焙 → Cython/字节码编译 → 自检),
`build2.sh` 调用它。**编译产物自身会先跑一次 `versions` 自检**,编译过了但导入就炸的情况不会流到发行包。
---
## 一之三、发行包完整性修复(本轮实测抓出来的两个真问题)
两个都是"**能出产物、但少了一层保护**"的静默失效,靠 Alpine 里真跑一遍才暴露:
| # | 现象 | 根因 | 修法 |
|---|---|---|---|
| 1 | `--pro` 在发行包里直接报 `No module named 'parse_oparray'`(普通 strong 却正常) | 构建脚本算 rev 的**源码目录**时把临时工作目录当成了引擎目录(`/../rev` = `/tmp/rev`),文件不存在又被 `isfile` 静默跳过 —— 发行包里从来没有 `rev/parse_oparray` | 改成显式传 rev 源码目录;并把"**逐个模块真的 import 一遍**"加进构建自检(少一个模块直接构建失败) |
| 2 | `--pro`(默认 `--scatter full`)**打散层完全没生效**,日志里只有一句不起眼的"scatter 跳过" | `swc_scatter.php` / `swc_symbols.php` 要 `require __DIR__.'/vendor/autoload.php'`(nikic/php-parser),而发行包**没带 vendor/** | 把 `vendor/`(1.9MB,273 个文件,BSD-3)打进两个平台包;`build2.sh` 的资产拷贝与构建体检都加上它;引擎侧的跳过提示改成显眼的 `⚠ scatter 未生效(缺 vendor/php-parser?…)` |
**修复前后(同一个包、同一条命令、Alpine 里实测,用 `SWCENC_LAYER_REPORT` 读结构化层清单):**
```
修复前 --scatter full -> scatter: {}
修复后 --scatter full -> scatter: {mode: full, strings: 4, renamed: 0, seed: 603ce30d, ...}
--pro -> scatter: {...full...} + pro: {ops_permuted: 41/41} + guard: on
--pro --php 8.3 --verify -> [验证] 真实 loader 加载执行通过
```
第 1 条还顺手把引擎的 `probe_strict()` 修准了:Windows 上 `rev/opdump` 里是 Linux 的 `.so`,
php.exe 根本加载不了,以前只判断"文件在不在",于是会拿着它去 dump 再报一句
"opdump 没有产出数据"。现在 Windows 直接判定"无转储扩展",能力矩阵如实反映、
`--swc-strict/--pro` 报的是真正的原因。
> 这两条是**发行包**的问题,线上 `swcapi.xueyuanpie.com` 一直是好的(源码 + 完整的 rev/vendor 都在),
> 所以线上行为没有任何变化 —— 复检 `act=versions` 仍返回全部 10 个 PHP 版本。
### 实测支持矩阵(发行包 + 真实 loader,逐个组合跑 `--verify`)
| 目标 PHP | loader 3.1 | loader 3.2 |
|---|---|---|
| 7.0 / 7.1 / 7.2 | ❌ 段错误 | ❌ 段错误 |
| 7.3 / 7.4 | ✅ | ✅(`--swc-strict` 通过) |
| 8.0 / 8.1 | ✅ | ✅ |
| 8.2 / 8.3 / 8.4 | —(官方 3.1 无这些版本) | ✅(`--pro` 在 8.3 实测通过) |
### 已知限制(引擎本身,线上与发行包一致)
* **PHP 7.0 / 7.1 / 7.2 的产物会在加载时段错误**(实测 PHP 7.0.33 / 7.1.33 / 7.2.33,
loader 3.1 与 3.2 都一样;带/不带 opcache 一样;**线上 `swcapi` 用同一份引擎复现完全相同**)。
所以:目标请用 **7.3+**;确实要交付 7.0–7.2,请先在目标机上用小文件试一次。
这是引擎的既有问题,与本次(Swoole/SG 解密、离线加密器)的改动无关,本轮未动它。
* `--swc-strict` / `--pro` 在 **7.0 / 7.1** 上会 fail-closed 直接报错
(引擎拒绝产出被弱化的"PRO 产物",而不是静默降级)。
* Windows 上 `--pro` / `--swc-strict` 只支持 PHP 7.0/7.1(7.2+ 需要 Linux 专有的转储扩展),
见 `USAGE.md` 的能力对照表。
---
## 二、三个核心能力
### 1. 零依赖(自带运行时)
| 组件 | Linux | Windows |
|---|---|---|
| 解释器 | 自带 CPython 3.12(只带标准库,引擎只用标准库) | 自带免安装 Python 3.12 |
| PHP | 自带 7.0–8.4 的 `php` + 各版本 `opcache.so` | 自带官方 NTS x64 的 `php.exe` + `php8.dll`(7.x 为 `php7.dll`) + `ext/php_opcache.dll` |
| 系统库 | **自带 glibc + 63 个依赖 .so**,用自带 `ld-linux` 启动 | 自带 `vcruntime140*.dll`(VC14 族二进制兼容 VS2015–2022) |
**实测证据**:把一个「没有 glibc、没有 PHP、没有 Python」的 `alpine:3.20` 容器里跑完整流程 ——
`doctor` / `license` / `pack --verify`(PHP 8.3 与 7.4 各一次)/ 产物在官方 loader 下执行,全部通过。
为什么这么打包:引擎(连 `swcopdump*.so` 转储扩展)本来就是针对这套 PHP 构建验证过的,
换成自编译 PHP 等于把 ABI / opcache file_cache 兼容性重新赌一次;闭包方案的依赖是**自带**的,
零依赖目标一样达成,而且不会引入新的兼容性风险。
### 2. 授权绑定(机器码 / 有效期 / 版本权益)
* 签名:RSA-2048 + SHA-256 + PKCS#1 v1.5,**纯标准库实现**(工具侧无需任何第三方库)
* 校验顺序(报错都能直接指导客户下一步):签名 → 有效期 → 机器码 → 功能位
* 机器码:Linux = 系统 id + 首块物理网卡 MAC + CPU 型号;Windows = MachineGuid + 系统卷序列号 + MAC
* 版本权益:`trial / std / pro / enterprise` 决定 `strong / batch / pro / bind / decoy` 功能位
实测的四类拒绝(都给了可执行的中文提示):
```
篡改内容 → 授权文件签名校验失败:内容被改动过,或不是本产品签发的文件…
过期 → 授权已于 2024-02-01 过期(过期测试)。请凭本机机器码 SWC-… 联系签发者续期。
换机器 → 该授权不适用于本机:授权绑定的是 SWC-0000-1111-2222,本机是 SWC-655E-0CC9-0961…
功能位不足 → 当前授权不含「Pro 虚拟机层(--pro)」(标准版)。…请联系签发者升级授权。
```
### 3. 产物绑定(域名 / IP / 到期)
```sh
./bin/swcenc batch ./app -o ./app_enc --php 8.3 \
--domains "customer.com,*.customer.com" --ips "1.2.3.4" --expire 2027-01-01
```
行为(三条都是刻意设计,避免售后灾难):
1. 域名 / IP **只拦 Web**,CLI 默认放行(否则客户 cron / 队列会集体挂掉);要一并绑加 `--bind-cli`
2. **到期是绝对的**:Web 与 CLI 都拦
3. 域名支持通配前缀 `*.example.com`,且同时匹配 apex 域
实测(`php -S` + 真 loader):
```
Host: example.com → 200 42
Host: sub.example.com → 200 42 (通配命中)
Host: evil.com → 403 This script is not licensed to run here (host evil.com).
过期产物(CLI 直跑) → This script is not licensed to run here (expired on 2020-01-01).
```
绑定层与源码一起被编译进载荷,不是可读配置。
---
## 三、怎么签发授权
```sh
cd /root/swcenc
python3 tools/licgen.py machine # 签发者本机机器码
python3 tools/licgen.py sign \
--customer "某某科技" --edition pro \
--machines SWC-XXXX-XXXX-XXXX \
--domains "customer.com,*.customer.com" \
--months 12 --out license-xxx.json
python3 tools/licgen.py show license-xxx.json # 校验(含机器码/有效期)
```
把 `license-xxx.json` 改名成 `license.json` 交给客户,放进发行包 `license/` 目录即可。
* 私钥:`license/private_key.pem`(0600,**不要外发、不要进包**)
* 换密钥 = 所有老授权失效,所以换之前要给所有客户重签
* 客户机换机器 / 授权到期 → 拿新机器码重签一份即可
---
## 四、验证清单(本次实际跑过的)
| 验证 | 结果 |
|---|---|
| **编译版(.so)引擎**在无 glibc/PHP/Python 容器里跑通 | ✅ `license` / `doctor` / `pack --verify` / 产物执行 全通过 |
| 环境变量绕过授权 | ✅ `SWCENC_ENFORCE=0` 仍被拦(烘焙生效) |
| 发行包源码体检 | ✅ Linux:`engine/**/*.py` = 0,`.so` = 12;Windows:`.py` = 0,`.pyc` = 12 |
| Windows 安装包载荷 | ✅ 用 7z 解出 207 个文件,含 `bin/swcenc.exe`、`.pyc`、两代 loader、10 个 PHP;无 `.py`、无私钥、无 license.json |
| 无 glibc/PHP/Python 容器里 `doctor` | ✅ 内置运行时被识别,10 个 PHP 版本全绿 |
| 同一容器里 `pack --verify`(PHP 8.3 / loader 3.2) | ✅ 真实 loader 加载执行通过 |
| 同一容器里 PHP 7.4 / loader 3.1 | ✅ 通过 |
| 产物在容器里用官方 loader 执行 | ✅ 输出正确 |
| 未授权打包 | ✅ 退出码 3 并给出机器码 |
| 篡改 / 过期 / 他机 / 功能位不足 | ✅ 四类全部正确拒绝 |
| 域名绑定(通配/非法/CLI) | ✅ 200 / 200 / 403 / CLI 放行 |
| 到期绑定(CLI 也拦) | ✅ 拒绝并给出到期日 |
| **线上服务回归**(网关 → swcapi → 引擎 → 产物) | ✅ 加密成功、产物正常 |
| 公开下载 URL + sha256 回下载比对 | ✅ 一致 |
Windows 包:**结构层面已验证**(`swcenc.exe` 为 PE32+ 控制台程序、安装包为 NSIS PE、
载荷 207 个文件齐全、`.py` = 0),但本机没有 Windows 也没有 Wine,**没有真机执行过**。
Windows 侧三个需要你在真机上确认的点:
1. `swcenc.exe doctor` —— 应打印内置运行时(10 个 PHP 版本)与授权状态;
2. 菜单里选 3) 加密单个文件 —— 应能出产物;
3. 装完在「程序和功能」里能看到 swcenc,卸载后目录与快捷方式都清掉。
---
## 五、已知限制 / 待办
1. **Windows 未真机验证**(本机既无 Windows 也无 Wine,只做了结构校验:PE32+ 启动器、
NSIS 安装包、载荷 210 个文件齐全、`.py` = 0)。第一台 Windows 机器上请按顺序确认:
① 双击安装 → 桌面出现「swcenc 加密器」;② `"%LOCALAPPDATA%\swcenc\bin\swcenc.exe" doctor`
应列出内置的 10 个 PHP 版本与授权状态;③ 菜单选 3) 加密单个文件(PHP 8.3)应出产物并自报
`[完成]`;④ 卸载后目录与快捷方式清干净。
`--verify` 在 Windows 上按设计跳过(loader 是 Linux 扩展),
`--pro` / `--swc-strict` 在 Windows 上只支持 PHP 7.0/7.1。
**需要逐次验证或 Pro 层的产物,请在 Linux 机器上打包。**
2. **容器里机器码不稳定**:机器码含网卡 MAC,容器重建即变。建议装在固定物理机 / VPS。
3. `swcapi.xueyuanpie.com` 的 `data/version_matrix.json` 缓存被写空过一次(引擎文件权限问题导致
引擎读不进来)。已修:新增模块的属主/权限已对齐 `www:www 644`,并加固了缓存逻辑 ——
引擎临时不可用时**沿用上一次的好数据**,不再写空矩阵(否则客户会看到
「loader 3.2 不支持 PHP 8.3」)。
4. **`cj.xueyuanpie.com` 的 PHP 应用本身在 500**(与本次交付无关,静态文件不受影响)。
5. 包体积较大(Linux 505MB 解压)源于自带 10 个 PHP 版本 + glibc 闭包。若客户只要一两个
PHP 版本,可以出一个精简包(改 `runtime/build_runtime_linux.py` 的版本列表重打包即可)。