# swcenc 2.0 —— Swoole Compiler 同款离线加密器 把**明文 PHP 源码**打包成 **`swoole_loader` 扩展可以直接加载执行**的加密文件。 和官方 Swoole Compiler 是同一类东西:**离线加密器**(源码不出本机)+ 配套 loader。 2.0 相比 1.0 的三件事: | | 1.0 | 2.0 | |---|---|---| | 依赖 | 要装 Python3 + 目标版本 PHP + opcache | **零依赖**:自带 Python + PHP 7.0–8.4 运行时(Linux 还自带 glibc) | | 授权 | 无 | **机器码 + 有效期 + 版本权益**,离线校验(RSA-2048 签名,改一个字就失效) | | 产物控制 | 只能加密 | **绑定域名 / IP / 到期**,产物换个站或过了期就跑不起来 | | 平台 | Linux | **Linux x86_64 + Windows x64** | --- ## 一、30 秒上手 ```sh # 0) 授权:先拿机器码去换授权文件 ./bin/swcenc machine # 机器码: SWC-XXXX-XXXX-XXXX # 把机器码发给签发者,拿到 license.json 后放到 ./license/license.json ./bin/swcenc license # 看授权状态 ./bin/swcenc doctor # 环境自检:能用哪些 (loader, PHP) 组合 # 1) 加密单个文件(PHP 8.3 / loader 3.2) ./bin/swcenc pack app/common.php -o out/common.php --php 8.3 # 2) 整目录加密 + 逐个用真实 loader 验证 ./bin/swcenc batch ./app -o ./app_enc --php 8.3 --verify # 3) 绑定域名与到期(客户最常用的两个开关) ./bin/swcenc batch ./app -o ./app_enc --php 8.3 \ --domains "example.com,*.example.com" --expire 2027-01-01 ``` Windows 上是同一个用法,把 `./bin/swcenc` 换成 `bin\swcenc.exe`: ```bat bin\swcenc.exe machine bin\swcenc.exe pack app\common.php -o out\common.php --php 8.3 --domains "example.com" bin\swcenc.exe doctor ``` * **便携包**:解压后运行 `bin\swcenc.exe`(同目录还有 `bin\swcenc.cmd`,等价)。 * **安装包**:装到 `%LOCALAPPDATA%\swcenc`(用户级,不弹 UAC),桌面与开始菜单会有 「swcenc 加密器」快捷方式;命令行用完整路径 `"%LOCALAPPDATA%\swcenc\bin\swcenc.exe" doctor`。 想让任意目录直接敲 `swcenc`,把 `%LOCALAPPDATA%\swcenc\bin` 自己加进 PATH 即可 (安装器刻意不动你的 PATH)。 不带参数运行就是**交互式菜单**(1 自检 / 2 看授权 / 3 加密单文件 / 4 加密整目录 / 5 机器码)。 装到系统里(可选): ```sh sudo ./install.sh # Linux:装到 /opt/swcenc 并建 /usr/local/bin/swcenc ``` --- ## 二、产物怎么跑起来 产物只有在目标机上装了**对应那一个版本**的 loader 才能执行: ```ini ; php.ini extension=swoole_loader83_nts.so ; Linux ``` 三条硬规则,先看清再动手: | 规则 | 说明 | |---|---| | **单版本** | 产物只能跑在**与加密时相同**的 PHP 主次版本上。8.3 加的密只能在 8.3 跑。 | | **单代数** | loader 3.1 与 3.2 的产物**互不通用**,头块里写着代数。 | | **Pro 独立** | `--pro` 产物只能由 `swoole_loader_pro` 加载;官方 loader 加载会直接段错误。 | `./bin/swcenc versions` 会打印当前包里 loader × PHP 的完整支持矩阵。 --- ## 三、命令与选项 ``` swcenc <命令> [选项] 命令 pack <文件> -o <输出文件> 打包单个文件 batch <目录> -o <输出目录> 批量打包,保持相对路径 info <文件> 查看容器信息 unpack <文件> -o <输出二进制> 解出容器载荷 versions loader × PHP 支持矩阵 caps 各版本能力分层(JSON) doctor 环境自检(含授权状态与内置运行时) machine 打印本机机器码(换授权用) license 查看授权状态 通用选项(pack / batch) --php 7.3 | 8.1 ... 目标 PHP 版本(必须与目标服务器一致) --loader-version 3.1|3.2 写进头块的 loader 代数(默认 3.2) --mode strong|weak strong=字节码载荷(默认);weak=源码随容器下发 --verify 打包后用真实 loader 执行一遍做验证(Linux) -j, --jobs N batch 的并发数 产物绑定 --domains a.com,*.b.com 只允许这些域名运行(Web 请求;CLI 默认放行) --ips 1.2.3.4,... 只允许这些来访 IP --expire 2027-01-01 到期日(当天结束前仍可用;Web 与 CLI 都会拦) --bind-cli CLI 也检查域名(默认不检查,见下) --bind-quiet 绑定不通过时不提示原因,只回 403 加固 / Pro --pro Pro 层(只能配 Pro loader) --swc-strict / --swc-calibrate / --swc-encode-ops 与官方同款的诱饵字节 / 编码态 op 等加固 --guard / --no-guard 运行期反分析自检层(--pro 默认开) --scatter off|light|full|mangle 粉碎性重组打散(--pro 默认 full) 授权 --license <文件> 指定授权文件路径 ``` --- ## 四、产物绑定(域名 / IP / 到期) **为什么需要**:加密只保证「代码不被读」,不保证「谁都能跑」。客户把加密包交付给下线之后, 真正要控制的是「这份产物能在哪个站、到什么时候为止运行」。 ```sh ./bin/swcenc pack app.php -o app.enc.php --php 8.3 \ --domains "customer.com,*.customer.com" \ --ips "1.2.3.4" \ --expire 2027-01-01 ``` 行为(三条都是刻意的设计): 1. **域名 / IP 只拦 Web 请求,CLI 默认放行** —— 否则客户的定时任务、队列 worker 会集体挂掉, 那是售后灾难而不是保护。要连 CLI 一起绑,加 `--bind-cli`。 2. **到期是绝对的**:Web 与 CLI 都会拦。到期就该停。 3. **域名匹配支持通配前缀**:`*.example.com` 匹配 `a.example.com`、`b.a.example.com`, 也匹配 `example.com` 本身。 绑定层与源码一起被编译进载荷(不是可读的配置),命中不允许的环境时输出: ``` This script is not licensed to run here (host evil.com). This script is not licensed to run here (expired on 2027-01-01). ``` > 绑定检查发生在**每次请求**,开销是一次数组比较,可以忽略。 --- ## 五、授权(机器码 / 有效期 / 版本权益) 授权是**离线**的:客户机器不需要联网,签发者也不需要部署授权服务器。 ```sh ./bin/swcenc machine # 客户把这一行发给签发者 ./bin/swcenc license # 授权状态、功能位、可绑定域名 ``` 授权文件(`license/license.json`)由签发者用私钥签名,工具内置公钥校验: * 客户改一个字节 → 签名立刻不匹配(RSA-2048 + SHA-256 / PKCS#1 v1.5); * 授权里写死 `machines`(机器码列表)与 `not_after`(到期日),换机器或过期就用不了; * 版本权益(`edition`)决定哪些功能可用: | edition | strong | batch | pro | bind | decoy | |---|---|---|---|---|---| | trial | ✅ | | | | | | std | ✅ | ✅ | | | | | pro | ✅ | ✅ | ✅ | ✅ | | | enterprise | ✅ | ✅ | ✅ | ✅ | ✅ | 授权文件按这个顺序查找:`--license` 指定 → `SWCENC_LICENSE` → 包内 `license/license.json` → `~/.swcenc/license.json` → `/etc/swcenc/license.json`。 > 机器码由「系统 id + 第一块物理网卡 MAC + CPU 型号」算出(Windows 用 MachineGuid + > 系统卷序列号 + MAC)。**容器里 MAC 每次重建都会变**,所以别把加密器装在会重建的容器里, > 否则会一直要重新授权。 --- ## 六、零依赖是怎么做到的 | 组件 | Linux | Windows | |---|---|---| | 解释器 | 自带 Python 3.12(含标准库) | 自带免安装 Python 3.12 | | PHP 运行时 | 自带 PHP 7.0–8.4(`runtime/php/<版本>/`) | 自带官方 NTS x64 的 `php.exe` + `php_opcache.dll` | | 系统库 | **自带 glibc 与全部依赖 .so**(`runtime/lib/`,用自带的 ld.so 启动) | 自带 VC 运行时(`vcruntime140*.dll`) | 所以客户机上**什么都不用装** —— 实测在一台「没有 glibc、没有 PHP、没有 Python」的 Alpine 容器里,`license` / `doctor` / `pack --verify` 与产物执行全部正常。 ### 发行包里也没有加密器自身的源码 引擎不是以 `.py` 形式交付的: | 平台 | 引擎形态 | 包里 `.py` 数量 | |---|---|---| | Linux | 12 个模块 **Cython 编译成 `.so`**(原生机器码) | **0** | | Windows | 编译成 `.pyc` 字节码 | **0** | **授权校验写在编译产物里**:`ENFORCED_BY_BUILD` 这个常量在打包时被烘焙成 `True`, 所以 `SWCENC_ENFORCE=0 ./bin/swcenc pack ...` 这种绕过尝试**照样被授权拦住** —— "要不要校验授权"只存在于编译出来的机器码里,既改不了也没有源码可改。 `doctor` 会明确告诉你用的是内置运行时还是系统里找到的: ``` ✅ 内置运行时:/opt/swcenc/runtime PHP 10 个版本,Python:自带 —— 客户机不需要装任何东西 ``` --- ## 七、目录结构 ``` swcenc-2.0.0-linux-x86_64/ swcenc-2.0.0-win64/ ├─ bin/swcenc 启动器 ├─ bin/swcenc.exe 启动器(原生,双击出菜单) │ └─ bin/swcenc.cmd 备用启动器 ├─ runtime/ ├─ runtime/ │ ├─ lib/ 自带 glibc + .so │ ├─ php/<版本>/bin/php.exe + php8.dll │ ├─ php/70..84/ │ │ ext/php_opcache.dll │ └─ python/ 自带解释器 │ └─ python/ 免安装解释器 ├─ engine/ 引擎(无 .py:Linux 为 .so,Windows 为 .pyc) │ ├─ pack/ 打包器 + 自检层 + 绑定层 + 授权模块(编译产物) │ ├─ rev/ 逆向数据(op 表 / 诱饵表 / vm 规格,编译产物) │ ├─ loader31/ loader32/ 官方 loader(--verify 与交付给目标机) │ └─ loaderpro31/ loaderpro32/ Pro loader ├─ license/ public_key.pem + 放 license.json 的地方 ├─ examples/ └─ README.md USAGE.md INSTALL.md ``` --- ## 八、常见问题 **Q:`--verify` 报「真实 loader 加载执行通过 ✅」是什么意思?** 产物真的被对应版本的 loader 加载并执行了一遍,输出与原文件一致。这是**最硬的验证**。 **Q:Windows 上为什么不做 `--verify`?** `swoole_loader` 是 Linux 扩展(`.so`),Windows 的 PHP 加载不了它。Windows 上打包会明确 提示跳过验证,请把产物部署到目标服务器验证。**需要逐次验证的 PRO 产物建议在 Linux 机器上打包。** **Q:哪些 PHP 版本实测能跑通?** 发行包里带真实 loader 逐个组合跑过的结果(Alpine 容器,`--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 | —(官方 loader 3.1 没有这几个版本) | ✅ | 含 `--pro`(8.3 实测) | **Q:7.0 / 7.1 / 7.2 为什么不行?** 这是**引擎本身**的现状,不是本工具引入的:用 `swoole_loader70/71/72_nts.so` 加载对应版本 产出的产物会在载入时**段错误**(实测环境:PHP 7.0.33 / 7.1.33 / 7.2.33,带与不带 opcache 都一样;同样的问题在线上服务上一模一样地复现)。所以: * **建议目标 PHP 7.3 及以上** —— 这几个版本是全组合实测通过的; * 确实要交付 7.0–7.2,请先**在你的目标机上用一个最小的 PHP 文件试一次**再批量; * 7.0 / 7.1 上的 `--pro` / `--swc-strict` 另有限制:引擎会 fail-closed(宁可报错也不产出 被弱化的"PRO 产物")。 **Q:Windows 上能用 `--pro` / `--swc-strict` 吗?** 只能配 **PHP 7.0 / 7.1**。7.2+ 的加固层要用一个只在 Linux 上构建的转储扩展,Windows 的 PHP 加载不了它(.so 不是 .dll),所以那两档在 Windows 上会被明确拒绝(不会偷偷降级成弱产物)。 要出加固 / Pro 产物、或者要逐文件 `--verify`,**在 Linux 上打包** —— 那边是全组合支持的。 Windows 上出普通 `strong` 产物、做产物绑定、取机器码、管理授权都没问题。 **Q:产物换目录部署,路径会不会错?** 不会。打包时会抹掉载荷里内嵌的构建路径(`__FILE__` / `__DIR__` 相关), 产物放到哪台机器、哪个目录都能跑。 **Q:为什么引擎目录里看不到 `.py` 文件?** 故意的。引擎已编译(Linux `.so` / Windows `.pyc`),授权校验也在编译产物内。 改动这些文件会让工具无法启动。 **Q:Windows 上装的是 `setup.exe`,不想装怎么办?** 用便携包 `swcenc-2.0.0-win64.zip`,解压后运行 `bin\swcenc.exe`,不写注册表也不进 PATH。 安装包同样可以随时在「程序和功能」里卸载干净。 **Q:`weak` 模式是什么?** 源码随容器下发(有 loader 守卫,但源码以明文存在于文件里)。适合本机调试或 只要「不被随手改」的场景。**正式交付请用默认的 strong。** **Q:同一个项目要同时支持 PHP 7.4 和 8.3 怎么办?** Swoole 是单版本加密,所以分别打包两次(`--php 7.4` / `--php 8.3`), 在目标机上按 PHP 版本选用对应产物。 --- ## 九、安全与合规 * 只对你**拥有合法授权**的源码使用本工具。 * 加密强度取决于目标 loader 与所选加固档位;`--pro` + `--scatter full` + `--guard` 是当前最强的组合。 * 授权私钥不在发行包里;包内 `license/public_key.pem` 只能用于校验,不能签发。