
순수 Go 기반 Java 디컴파일러 및 직렬화 연산기
.war.zip.class 파일의 구조(상수 풀, 필드, 메서드, 버전, 접근 플래그)를 검사합니다.이식성과 임베딩을 위해 설계되었습니다:
javajive 파사드 패키지가 세 가지 기능을 모두 감싸며, 고급 사용을 위해 하위 패키지도 그대로 사용할 수 있습니다.decompile, classinfo, serial 서브커맨드.javac/java로 실제 .class / .jar 및 JDK 직렬화 블롭을 컴파일한 뒤 JavaJive와 대조 검증합니다(HARNESS-WORKFLOW.md 참조).utils / codec / log / go-funk가 최소한의 자체 포함 internal/ 코어로 재구현되었습니다.완전한 38개 JAR 차분 감사에서 고정 기준선 대비 기록된 회귀가 0건입니다: 이전에 컴파일이 깨끗했던 20개 JAR가 모두 유지되고 26개가 이제 완전히 컴파일됩니다. 기준선 결함과 4개의 기존 메서드 스텁은 여전히 남아 있습니다. JAR별 결과와 재현 방법을 참조하세요.
250개의 격리된 의미론적 왕복 테스트가 컴파일, JVM 검증, 스텁, 런타임 동등성을 각각 별도로 기록하며, 재구성된 클래스패스에는 원본 애플리케이션 클래스가 존재하지 않습니다. CI는 JDK 17과 21에서 의미론적 감사를 실행하고, 전체 이력 비교와 OS/Go 및 레이스 매트릭스를 실행합니다. 이러한 검사가 모든 라이브러리 메서드의 동등성을 증명하지는 않습니다. 구현과 남은 경계를 참조하세요.
다음 수치는 2026-09-05에 v0.3.0에 대해 보고된 것입니다. 이는 현재 버전의 인수 결과나 동작 동등성의 증거가 아닙니다.
34개의 실제 jar(18,759개의 평탄화된 단위)에서 디컴파일 → javac --release 8 재컴파일 → 재패키징 → JVM 검증을 통해 측정:
Outer$Inner.java)가 javac 오류 0건으로 재컴파일되고, 34개 jar 전체에서 구문 오류 0건(CI가 강제하는 하드 어서션이므로 렉서 실패 뒤에 타입 오류가 숨을 수 없음).-Xverify:all 클래스별 검증이 종단 간 통과(코덱은 호출 차분 하에서 원본 jar와 바이트 단위로 동일). provenClean에 고정됨.전체 방법론, jar별 표, 재현 명령은 BENCHMARK.md를 참조하세요.
# CLI
go install github.com/yaklang/javajive/cmd/javajive@latest
# Library
go get github.com/yaklang/javajive@latest
또는 소스에서 빌드:
git clone https://github.com/yaklang/javajive
cd javajive
go build -o javajive ./cmd/javajive
Go 1.22+가 필요합니다.
javajive <command> [arguments]
Commands:
decompile decompile .class/.jar/.war/.zip or a directory into Java source
classinfo print the structure of a .class file (version, fields, methods)
serial Java serialization tools (subcommands: tojson, fromjson)
version print the version
help show help
# Single class: prints to stdout by default, or -o to write a file.
javajive decompile Foo.class
javajive decompile Foo.class -o Foo.java
# Archive: defaults to a "<input>.src" directory, or -o to choose one.
javajive decompile app.jar
javajive decompile app.war -o ./app-src
# Directory: recursively decompile .class files (requires -o output dir).
javajive decompile ./classes -o ./src
javajive classinfo Foo.class
class: InvisibleAnnoSeed
super: java/lang/Object
version: 61.0
access: public
constants: 18
fields (0):
methods (2):
<init>()V
run()I
# Serialized binary -> JSON. (-hex: input is a hex string, -: read from stdin)
javajive serial tojson dump.bin
printf 'aced000574000568656c6c6f' | javajive serial tojson -hex -
# JSON -> serialized binary. (default prints hex; with -o writes raw bytes)
javajive serial fromjson dump.json -o out.bin
javajive serial fromjson dump.json # prints hex
통합 파사드 패키지를 사용하세요 — 하나의 임포트로 세 가지 기능을 모두 커버합니다:
import "github.com/yaklang/javajive"
// Decompile a single class, or a whole archive into a directory.
src, err := javajive.Decompile(classBytes)
err = javajive.DecompileArchive("app.jar", "app-src")
// Inspect class structure.
obj, err := javajive.ParseClass(classBytes)
_ = obj.GetClassName()
// Java serialization: binary -> JSON -> binary.
objs, _ := javajive.ParseSerialized(raw) // or ParseSerializedHex(hexStr)
jsonBytes, _ := javajive.SerializedToJSON(objs...)
restored, _ := javajive.SerializedFromJSON(jsonBytes)
out := javajive.MarshalSerialized(restored...)
| Function | Purpose |
|---|---|
Decompile(classBytes) (string, error) | 하나의 .class 바이트를 디컴파일 |
DecompileFile(path) (string, error) | 디스크에서 하나의 .class를 디컴파일 |
DecompileWithResolver(classBytes, resolve) | 클래스 바이트 리졸버로 디컴파일 |
DecompileArchive(src, dst) error | .jar/.war/.zip을 디렉터리로 디컴파일 |
ParseClass(classBytes) (*ClassObject, error) | 하나의 .class 바이트를 파싱 |
ParseClassFile(path) (*ClassObject, error) | 디스크에서 하나의 .class를 파싱 |
ParseSerialized(raw) ([]JavaSerializable, error) | 직렬화 스트림을 파싱 |
ParseSerializedHex(hexStr) ([]JavaSerializable, error) | 16진수로 인코딩된 스트림을 파싱 |
MarshalSerialized(objs...) []byte | 객체를 와이어 포맷으로 재인코딩 |
MarshalSerializedHex(objs...) string | 16진수로 재인코딩 |
SerializedToJSON(objs...) ([]byte, error) | 객체를 JSON으로 변환 |
SerializedFromJSON(raw) ([]JavaSerializable, error) | JSON에서 객체를 재구성 |
고급 사용을 위해 하위 패키지도 내보내집니다:
classparser, classparser/jarwar, serialization.
이식성과 크기를 유지하기 위해 JavaJive는 yaklang 대비 몇 가지 의도적인 절충을 합니다. 전체 매핑과 마이그레이션 가이드는 MIGRATE.md를 참조하세요.
| Area | yaklang (upstream) | JavaJive |
|---|---|---|
| Decompiler ANTLR safety net | ANTLR Java 문법으로 덤프된 소스를 재검증하고, 실패 시 멤버를 스텁으로 저하 | 제거됨(무거운 의존성); 검증은 no-op이며 출력이 직접 방출됨 |
Support layer (utils / codec / log / go-funk) | 공유 모노레포 패키지 | internal/ 아래의 최소한의 자체 포함 재구현 |
yso gadget generator | 포함됨 | 포함되지 않음 |
String literal charset recovery (MatchMIMEType) | 선택적 GBK/GB18030 복구 | 스텁 처리됨(no-op); 대다수 경우 동작 불변 |
서드파티 의존성은 소수의 순수 Go 라이브러리(gobwas/glob, go-viper/mapstructure, samber/lo, tidwall/gjson, segmentio/ksuid, yeka/zip, 그리고 몇 가지 golang.org/x/*)로 제한됩니다.
go test ./... # unit tests + JDK cross-tests (skipped if no JDK)
go test ./... -race # data-race free (linux)
go test ./test/cross/ -v # Java cross-tests only (needs javac/java on PATH)
JDK 교차 테스트는 테스트 시점에 실제 Java 아티팩트를 컴파일하고 JavaJive와 대조 검증합니다. JDK가 없으면 자동으로 t.Skip합니다. 하네스와 CI가 작동하는 방식은 HARNESS-WORKFLOW.md를 참조하세요.
javajive.go unified facade package (import "github.com/yaklang/javajive")
serialization/ Java serialization/deserialization (from yaklang common/yserx)
classparser/ class parser + decompiler (from yaklang common/javaclassparser)
cmd/javajive/ CLI entry point
internal/ trimmed self-contained support layer (log / codec / funk / utils / filesys / ...)
test/cross/ JDK-backed cross-tests (javac/java)
site/ static landing page deployed to GitHub Pages