Burp Suite 2026.8 许可机制分析
样本是 PortSwigger 官网的 Professional / Community 2026.8 Desktop JAR(2026-08-24 发布)。Community 和 Professional 从 2026.4 起已经合成一份安装器、一份 JAR,差别不在文件,而在启动之后内存里那一枚「我是不是 Pro」的开关。
结论先放前面:
许可 blob 本身有 SHA256withRSA 和服务端激活;「现在是不是 Professional」最终落成一次引用比较。 会改 JVM 字节码的人,打的是后者,不是去还原 PortSwigger 私钥。这和 Typora「激活事务有 RSA、启动只看本地字典」是同一类结构,只是 Burp 多了一层隐藏 ClassLoader 和心跳。
下面按「先看清楚这摊,再讲网上的老加载器为什么有的还能跟、有的已经对不上号」写。

样本
从 官方 Releases 下 Desktop JAR:
| 项 | 值 |
|---|---|
| 文件 | burpsuite_desktop_v2026.8.jar |
| 大小 | 729 488 519 字节(大半是内嵌 Chromium) |
| SHA-256 | 888f0588cf82a3b70d508dcbf07591184a26249e38cda8ea8b5a68f4b5a30789 |
| Main-Class | burp.StartBurp |
| Implementation-Version | 53343 |
| Java class major | 65(Java 21) |
| 保护 | Zelix KlassMaster ZKM25.0.0(常量池水印) |
核对哈希和官网一致之后再拆。包里 59 104 个条目,burp/ 下两万多个 class,名字几乎全是 Zxxx 这种 ZKM 名。明文搜 license 基本搜不到业务字符串,那是字符串加密,不是没有许可逻辑。
先建立一个心智模型
很多人一上来就想「序列号算法怎么算」。对 Burp 这件事,更先要问的是:
程序用什么来区分 Community 和 Professional?
Montoya API 里有公开枚举 BurpSuiteEdition:COMMUNITY / PROFESSIONAL / ENTERPRISE。功能闸(Intruder 全速、Scanner、Discover、Desktop REST API)读的是这个枚举,不是你有没有一份「长得像官方的 license 文本」。
枚举从哪来?2026.8 里大致是这条链:
1 | |

所以有两层:
- 内容层:key 像不像、签过没有、build 对不对、激活响应能不能过隐藏解析器。这一层有 RSA,客户端没有私钥。
- 开关层:内存里有没有非空的
Zeq_。有就是 Pro。这一层是布尔。
网上从 2019 用到 2026 的加载器,打的几乎都是第 2 层的上游(让解析「成功」,或者直接改数学库让自签也算成功),很少有人去算官方私钥。
官方激活长什么样
文档写得很清楚,JAR 里也内嵌了同一份 HTML。
标准路径:账号页下 license key → 粘贴 → Next → 客户端 POST 到:
1 | |
离线走 Manual activation:Copy URL → 浏览器打开官方激活页 → Copy request → 粘到网页 → 把 Activation response 贴回客户端。手工激活把协议口完整露在 UI 里,所以后来的 keygen 根本不用打真服务器,把 request/response 当本地 mock 就行。
本地不落明文 ~/.BurpSuite/*.key,而是 Preferences.userNodeForPackage(burp.StartBurp)。ZKM 解开后能看到这几个键:
| 键 | 含义 |
|---|---|
license + 运行模式序号 |
当前 license 原文 |
expired_license |
过期 license |
use_community_edition |
强制走 Community |
| 对 license 做摘要后再存 | 激活响应 |
只改 Preferences、不提供能过隐藏解析器的 blob,启动仍会进「需要激活 / 无效许可」。
启动失败的退出枚举也值得看一眼,因为它把后患写在名字上:
INVALID_LICENSE/LICENSE_EXPIREDLICENSE_WAS_NUKED(远程撤销)HEARTBEAT_FAILED/HEARTBEAT_FORBIDDEN
心跳明文是 PUT /api/heartbeat,Content-Type: application/json。FORBIDDEN 会关进程。老加载器不管这个;2026 年的 agent README 才开始 mock HTTP,说明 PortSwigger 后来把失败路径接到了杀进程。
保护栈:ZKM 不够,后面还有 ClassLoader
ZKM 25 做了三件常见的事:类名搅乱、字符串加密、部分控制流。解密方法是每类一份 a(int, int)。把 StartBurp 的调用对子拿出来反射执行,就能解出 Activate.ashx、burp.testing、Prefs 键这些明文。
真正藏校验算法的是三层自定义 ClassLoader:
burp.Zwa8:解析 license key(Zfqu.Zt用Class.forName调进来)burp.Zyc7:造 activation requestburp.Zp74:解析 activation response
它们自己很小(8~11 KB),真正干活的 class 是运行时 defineClass 进去的,还带 MD5 形常量做完整性。静态在 JAR 里搜 burpr0x! 或 4096-bit 模数,2026.8 明文是 0 命中——不是没了,是加密进隐藏类了。
另外还有一组 Zv1 / Z_g 的 SHA256withRSA,模数是 1024/2048。对过公开 keygen 里的 PKCS8:对不上。那是给写文件流验签的(更新包之类),不是 license keygen 的那把。
网上的加载器在打什么
公开能看到源码的,是反编译出来的 BurpLoaderKeygen 一族。用户侧仪式从 Java 8 到 2026 几乎没变:
-javaagent:loader.jar起官方 JAR- keygen 出一段文本,贴进 Burp
- 选 Manual activation
- request / response 互贴
Keygen 造的不是官网合法许可:NUL 分隔字段(名字、full、过期约 2099)→ 自己的 RSA 签名 → DES 信封(历史上密钥是 burpr0x!)→ Base64。
Loader 四刀,故意不看 ZKM 类名:
| 刀 | 认什么 | 干什么 |
|---|---|---|
| 换模 | BigInteger.oddModPow 吃到硬编码的 4096-bit 官方 n |
换成破解者自己的 n,自签就能过 |
| 拆解析 | burp/* 里 >110 KB、描述符 ([Ljava/lang/Object;Ljava/lang/Object;)V、指令 >2 万 |
整段方法清掉,改成 DES 解码 |
| 跳完整性 | 同上大类里第二次 new Exception |
条件跳改成 GOTO |
| Bounty | OkHttp 回包 | 顺带伪造 Burp Bounty 的 LicenseSpring JSON |
更早一代(scz / surferxyz,Burp 1.x~2.0)是改 BigInteger.compareTo + -Xbootclasspath/p。Java 9 砍了这条启动参数,Burp 2.1 也不再明文放那两组 n,哲学没变:数学比较动手脚,不破私钥。

2026.8 上,老启发式已经对不齐
用公开 loader 的体积规则扫本包:burp/* 大于 110 KB、方法是 (Object[], Object)V、指令超过 2 万——命中 0 个类。
包里确实有一堆 ~217 KB 的巨型 class,但描述符对不上。许可解析在不到 12 KB 的 ClassLoader 里,描述符是 (Object[], Object)Z,代码只有几百到两千字节。体积启发式是为「算法直接摊在一个超大方法里」准备的,2026.8 把算法藏进 defineClass 之后,这刀空了。
这就是为什么「把网上 2025 的 loader 挂上去」不一定还能 Pro:不是许可模型变了,是认入口的指纹变了。
开关层本身没变。Zn2m.ZW 仍然是:
- 许可存储还没就绪 →
null Zeq_为空 → Community 品牌- 否则 → Pro 品牌
Zvqq.ZV 看到 Pro 品牌就返回 PROFESSIONAL。直接补这一层,不依赖 110 KB 启发式,也不依赖那组 4096-bit n 还在不在 oddModPow 里。
验证:标题不够,要看功能面
改开关之后,只看窗口写不写 Professional 不够。对照做了三组:

有加载器、有显示
- 窗口:
Burp Suite Professional v2026.8 - Help → License:Professional +「Manage license key」(Community 是升级页)
- Dashboard:「All issues found by the scanner」
- Intruder 菜单:Do active scan / Do passive scan / Scan defined insertion points
- View:Discover、Collaborator
- REST
127.0.0.1:1337:这套 Desktop API 本身就是 Pro 的;openapi.json里有POST /scan - 扫描器知识库 184 条(含 SQL 注入、命令注入)
POST /v0.1/scan→ 201 Created,任务进引擎(新项目默认 paused,那是暂停不是拒绝)
去掉加载器再开
- 窗口只叫
Burp Suite,停在选 Community / Professional 的向导 - 没有 1337、没有 8080
- 采两分钟 一条 TCP 都没有——心跳客户端还没挂上
- 关掉这个窗,进程退出
心跳
有加载器时如果把心跳发送 nop 掉,后期同样采不到 PUT /api/heartbeat。那不是服务器认了假许可,是客户端根本没把心跳打出去。无许可对象时心跳调度也不会启动。要验证「假许可能不能过心跳」,得放真 PUT,看会不会 HEARTBEAT_FORBIDDEN。
一句话:功能面能开,靠的是运行时闸;不是官方许可,去掉加载器立刻回去。
和 Typora 比一眼
| Typora 1.14.9 | Burp 2026.8 | |
|---|---|---|
| 激活事务 | 服务端 RSA | SHA256withRSA + Activate.ashx |
| 每次启动 | 本地 AES 字典有没有 email/license | 内存里有没有 Zeq_ |
| 客户端私钥 | 没有 | 没有 |
| 额外对抗 | Hardened Runtime(但又放了 DYLD) | ZKM + 隐藏 ClassLoader + 心跳杀进程 |
| 开关能不能补 | 能 | 能 |
两边都不是「需要私钥才能用」。Burp 只是把开关藏得更深、杀进程更积极。
厂商如果真想加固
ZKM 换名对加载器几乎无效,人家按体积和描述符认。要加成本,得动结构:
- 不要用「
Zeq_非空 / 引用等于Zs56.Zu」这种单点开关。把 SKU 散到 Scanner、Intruder、项目保存等多处带签名的能力票。 - 心跳不要只在一个
ScheduledExecutorService里;和功能调用耦合并校验服务端挑战。 - 隐藏 ClassLoader 的 MD5 常量可以和类一起改。完整性应绑到启动后的服务端挑战,而不是只比本地哈希。
- 激活 TLS 钉扎到 PortSwigger 证书,避免自定义
SSLSocketFactory把信任链放太宽。 - 手工激活口是产品需求,但响应解析如果只靠客户端公钥,加载器仍然可以换模或抽掉解析函数。
参考
- 官方发布页与激活文档:https://portswigger.net/burp/releases/professional-community-2026-8
- 青衣十三楼飞花堂 / scz:旧版
burp-loader-keygen与BigInteger.compareTo - 公开反编译的 BurpLoaderKeygen(
bigint_patch/burp_patch1/burp_patch2)
本文是对官方 2026.8 Desktop JAR 的许可架构还原和对照实验,不提供即用加载器或密钥材料。