1. 引言
本文将介绍 SootUp 库。SootUp 是一个用于对 JVM 代码进行静态分析的库,支持原始源代码或编译后的 JVM 字节码。它是 Soot 库的全面重构版本,旨在提供更好的模块化、可测试性、可维护性和易用性。
2. 依赖项
使用 SootUp 前,需要将最新版本(当前为 1.3.0)添加到构建文件中:
<dependency>
<groupId>org.soot-oss</groupId>
<artifactId>sootup.core</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.soot-oss</groupId>
<artifactId>sootup.java.core</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.soot-oss</groupId>
<artifactId>sootup.java.sourcecode</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.soot-oss</groupId>
<artifactId>sootup.java.bytecode</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.soot-oss</groupId>
<artifactId>sootup.jimple.parser</artifactId>
<version>1.3.0</version>
</dependency>
各依赖项的作用:
sootup.core
:核心库sootup.java.core
:Java 核心模块sootup.java.sourcecode
:Java 源码分析模块sootup.java.bytecode
:字节码分析模块sootup.jimple.parser
:Jimple 解析模块
⚠️ 目前没有可用的 BOM 依赖,需要单独管理版本。
3. 什么是 Jimple?
SootUp 支持多种代码格式分析:
- Java 源代码
- 编译字节码
- JVM 内部类
所有输入都会转换为 Jimple 中间表示。Jimple 的设计目标是:
- 简化分析:将字节码的栈式操作转换为变量式操作
- 扁平化结构:将源码的嵌套结构转换为扁平结构
- 可读可写:提供人类可读的中间表示
示例对比:
// 原始 Java 代码
public void demoMethod() {
System.out.println("Inside method.");
}
// 对应的 Jimple 表示
public void demoMethod() {
java.io.PrintStream $stack1;
target.exercise1.DemoClass this;
this := @this: target.exercise1.DemoClass;
$stack1 = <java.lang.System: java.io.PrintStream out>;
virtualinvoke $stack1.<java.io.PrintStream: void println(java.lang.String)>("Inside method.");
return;
}
Jimple 虽然更冗长,但提供了更易分析的统一结构。分析时我们将操作 SootClass
、SootField
、SootMethod
等类型。
4. 分析代码
分析代码需要两个步骤:
- 创建
AnalysisInputLocation
- 构建
JavaView
输入源类型
JVM 内部类:
AnalysisInputLocation inputLocation = new JrtFileSystemAnalysisInputLocation();
源文件分析:
// 单文件 AnalysisInputLocation inputLocation = new OTFCompileAnalysisInputLocation( Path.of("src/test/java/com/baeldung/sootup/AnalyzeUnitTest.java")); // 多文件 AnalysisInputLocation inputLocation = new OTFCompileAnalysisInputLocation(List.of(...)); // 内存字符串 String javaContents = Files.readString(Path.of("...")); AnalysisInputLocation inputLocation = new OTFCompileAnalysisInputLocation("Test.java", javaContents);
字节码分析:
AnalysisInputLocation inputLocation = new JavaClassPathAnalysisInputLocation("target/classes");
创建视图
JavaView view = new JavaView(inputLocation);
5. 访问类
获取类对象
// 获取标识符工厂
IdentifierFactory identifierFactory = view.getIdentifierFactory();
// 创建类签名
ClassType javaClass = identifierFactory.getClassType("com.baeldung.sootup.ClassUnitTest");
// 获取类(安全方式)
Optional<JavaSootClass> sootClass = view.getClass(javaClass);
// 获取类(抛异常方式)
SootClass sootClass = view.getClassOrThrow(javaClass);
类信息检查
assertTrue(classUnitTest.isPublic());
assertTrue(classUnitTest.isConcrete());
assertFalse(classUnitTest.isFinal());
assertFalse(classUnitTest.isEnum());
类关系导航
// 超类
Optional<? extends ClassType> superclass = sootClass.getSuperclass();
// 接口
Set<? extends ClassType> interfaces = sootClass.getInterfaces();
✅ 返回 ClassType
而非 SootClass
,因为类定义可能不在当前视图中。
6. 访问字段和方法
批量获取
Set<? extends SootField> fields = sootClass.getFields();
Set<? extends SootMethod> methods = sootClass.getMethods();
精准获取字段
Optional<? extends SootField> field = sootClass.getField("aField");
精准获取方法
// 无参方法
Optional<? extends SootMethod> method = sootClass.getMethod("someMethod", List.of());
// 带参方法
Optional<? extends SootMethod> method = sootClass.getMethod("anotherMethod",
List.of(identifierFactory.getClassType("java.lang.String")));
获取重载方法
Set<? extends SootMethod> methods = sootClass.getMethodsByName("someMethod");
成员信息检查
assertTrue(sootMethod.isPrivate());
assertFalse(sootMethod.isStatic());
7. 分析方法体
获取方法体
Body methodBody = sootMethod.getBody();
7.1. 访问局部变量
Set<Local> methodLocals = methodBody.getLocals();
⚠️ 局部变量来自 Jimple 表示,可能包含:
- 原始变量(如方法参数)
- 临时变量(如
$stack3
) - 重命名变量(如
I1
代替name
)
示例分析:
private void someMethod(String name) {
var capitals = name.toUpperCase();
System.out.println("Hello, " + capitals);
}
实际局部变量:
this
I1
(参数name
)I2
(变量capitals
)$stack3
(System.out
)$stack4
(字符串拼接结果)
7.2. 访问方法语句图
StmtGraph<?> stmtGraph = methodBody.getStmtGraph();
List<Stmt> stmts = stmtGraph.getStmts();
语句类型解析:
JIdentityStmt
:参数赋值(this
和I1
)JAssignStmt
:变量赋值(3次)I1.toUpperCase()
→I2
System.out
→$stack3
"Hello, " + I2
→$stack4
JInvokeStmt
:方法调用($stack3.println()
)JReturnVoidStmt
:返回语句
✅ 即使简单方法也会生成完整语句图,包含所有操作细节。
8. 总结
SootUp 提供了强大的 JVM 代码静态分析能力:
- 支持多种输入格式(源码/字节码)
- 统一的 Jimple 中间表示
- 完整的类/方法/字段分析
- 详细的语句级分析
下次需要分析 Java 代码时,不妨试试这个工具!