本文面向 yaklang 维护者,说明如何把仓库内置的 Java 序列化 / 反序列化与 class 解析 / 反编译能力,替换为独立模块 github.com/yaklang/javajive。
javajive 是从 yaklang 抽取并裁剪而来的便携、纯 Go 实现,目标是「一个 import 即可用」,并去掉 yaklang 巨型依赖图(ANTLR、memedit 等)。
⚠️ 迁移前请读完「行为差异」一节。javajive 为便携性做了取舍,并非 1:1 等价替换。
| yaklang 内置(旧) | javajive(新) | 包名 |
|---|---|---|
github.com/yaklang/yaklang/common/yserx |
github.com/yaklang/javajive/serialization |
yserx(不变) |
github.com/yaklang/yaklang/common/javaclassparser |
github.com/yaklang/javajive/classparser |
javaclassparser(不变) |
github.com/yaklang/yaklang/common/javaclassparser/jarwar |
github.com/yaklang/javajive/classparser/jarwar |
jarwar(不变) |
| —(新增统一门面) | github.com/yaklang/javajive |
javajive |
关键点:两个核心包的 包名保持不变(仍是 yserx / javaclassparser),所以迁移在多数情况下只是 改 import 路径,符号、签名、调用方式都不变。
此外 javajive 提供了一个统一门面包 javajive,聚合三类能力,推荐新代码直接用它:
import "github.com/yaklang/javajive"
src, _ := javajive.Decompile(classBytes) // 单类反编译
_ = javajive.DecompileArchive("app.jar", "out-src") // 归档反编译
obj, _ := javajive.ParseClass(classBytes) // class 解析
objs, _ := javajive.ParseSerialized(raw) // 序列化解析
js, _ := javajive.SerializedToJSON(objs...) // -> JSON
back, _ := javajive.SerializedFromJSON(js) // <- JSON
out := javajive.MarshalSerialized(back...) // 重新编码javajive 相对上游做了如下取舍:
-
移除反编译器的 ANTLR 语法安全网
- 上游
javaclassparser在 dump 后会用 ANTLR Java 语法解析器校验生成源码,失败则把成员降级为桩(stub)。 - javajive 删除了
syntax_validate.go对common/yak/java/javasyntax的依赖:EnableDecompileSyntaxValidation默认false,校验函数为 no-op(恒返回nil)。 - 影响:反编译结果直接输出、不再降级。绝大多数类输出相同;极端构造下上游会降级而 javajive 不会。若 yaklang 强依赖该安全网,请保留上游版本,或在 javajive 上层自行接入校验。
- 上游
-
codec.MatchMIMEType降级为 stub- 反编译字符串字面量时的可选「中文字符集(GBK/GB18030)恢复」被关闭(恒返回 nil)。
- 影响:含被错误解码的中文字面量时,javajive 直接走
strconv.Quote常规路径,不做字符集回收。
-
不包含
yso(gadget 生成器)- javajive 只含「序列化 + class 解析 + 反编译」三件套,不含
common/yso。 - 影响:
yso仍需留在 yaklang。但yso依赖yserx/javaclassparser,若 yaklang 把这两个包切到 javajive,则yso的 import 也要一并改到 javajive(见下)。
- javajive 只含「序列化 + class 解析 + 反编译」三件套,不含
-
删除了
yserx/exports.go(脚本引擎导出表)- 上游
yserx.Exports(含"Decompile": jarwar.AutoDecompile等)是 yaklang 脚本引擎的绑定表,javajive 不含它(避免yserx反向耦合jarwar)。 - 影响:脚本引擎的
yserx库导出需由 yaklang 侧维护(保留这份exports.go,把其中的函数指向 javajive 的同名导出即可)。
- 上游
-
不复制重型回归测试
- 依赖
java2ssa/javasyntax的javaclassparser/tests/未迁移;javajive 自带更轻量的单元测试与 Java 交叉测试。
- 依赖
改动最小、风险最低:保留 yaklang 现有的包路径,把实现重导出(re-export)到 javajive。其余调用方一行不改。
在 common/yserx/yserx.go(或新文件)里:
package yserx
import jjser "github.com/yaklang/javajive/serialization"
// 类型别名:保持下游 *yserx.JavaObject 等用法不变
type (
JavaSerializable = jjser.JavaSerializable
JavaObject = jjser.JavaObject
// ……按需补齐你下游真正用到的类型
)
// 函数转发
var (
ParseJavaSerialized = jjser.ParseJavaSerialized
ParseHexJavaSerialized = jjser.ParseHexJavaSerialized
MarshalJavaObjects = jjser.MarshalJavaObjects
ToJson = jjser.ToJson
FromJson = jjser.FromJson
NewJavaString = jjser.NewJavaString
// ……
)注意:类型别名只能对导出类型逐个声明;若下游用到大量内部类型,方案 A 的样板代码会变多,这时方案 B 更干净。
yserx.Exports这份脚本导出表请继续留在 yaklang 维护。
直接删除 common/yserx、common/javaclassparser 两个目录,加 javajive 依赖,然后机械重写所有 import:
cd /path/to/yaklang
# 1) 引入依赖
go get github.com/yaklang/javajive@latest
# 2) 删除内置实现(先确认没有方案 A 的薄壳)
rm -rf common/yserx common/javaclassparser
# 3) 全仓重写 import(注意顺序:先长后短)
grep -rl 'common/javaclassparser' --include='*.go' . | xargs sed -i '' \
-e 's#github.com/yaklang/yaklang/common/javaclassparser#github.com/yaklang/javajive/classparser#g'
grep -rl 'common/yserx' --include='*.go' . | xargs sed -i '' \
-e 's#github.com/yaklang/yaklang/common/yserx#github.com/yaklang/javajive/serialization#g'
# 4) 处理脚本导出表:把 yserx.Exports 这份文件迁回 yaklang 侧(指向 javajive 的同名导出)
# 5) 收敛 & 验证
go mod tidy
go build ./...
go test ./common/yso/... ./common/yakgrpc/... ./common/yak/yakurl/...包名不变,所以 yserx.XXX / javaclassparser.XXX 的调用点 不需要改,只改 import 行。
迁移时重点验证以下 yaklang 内部消费方(它们直接 import 了这两个包):
common/yso/*:gadget 生成器,依赖yserx+javaclassparser。改 import 即可;注意它构造序列化对象,方案 A 的类型别名需覆盖其用到的全部类型。common/yakgrpc/grpc_yso.go/grpc_facades.go/grpc_codec.go:gRPC 处理器。common/yak/yakurl/java_decompiler/{fs,list}.go:反编译 URL 资源处理。直接用classparser/jarwar,迁移最直接。- 脚本引擎(yaklib)中注册
yserx.Exports的位置:改为引用 yaklang 侧保留的导出表。 common/yserx/cmd/dumper.go:示例 cmd,未迁移,可删除或自行保留。
迁移完成后建议运行:
go build ./...
go vet ./...
go test ./common/yso/... ./common/yakgrpc/... ./common/yak/yakurl/java_decompiler/...并重点回归:
- 反编译若干真实 jar/war(对比迁移前后输出;注意「行为差异 1」可能带来的降级差异)。
yso生成的 gadget 能正常序列化 / 反序列化。- 脚本引擎中
yserx.*/Decompile库函数可用。
- 你强依赖反编译器的 ANTLR 语法安全网(降级为桩的行为)。
- 你需要字符串字面量的中文字符集恢复。
以上场景请继续使用 yaklang 内置实现,或在 javajive 之上自行补回相应能力。