# 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` 的版本列表重打包即可)。