这学期选修了程序分析课,作业是基于soot进行一些java程序的静态分析,发现目前不管国内国外对于soot的教程都不太多,要么不太清晰要么太老,打算一边学习一遍记下笔记,方便未来入坑的铁子们少踩坑
建议使用JDK1.8/1.7
,版本过高的JDK似乎与soot存在不兼容问题
这一步很简单网上教程很多,主要在于设置$JAVA_HOME
我的是 MacBook Air (M1 2020): MacOS Monterey version 12.3.1
设置方法很简单
# 修改 .zshrc
sudo vim .zshrc
# 在 .zshrc文件中加上以下这行, 版本号可能不同,请自行修改
export JAVA_HOME="/Library/Java/JavaVirtualMachines/jdk1.8.0_321.jdk/Contents/Home"
# 也有可能是这个路径, 自行check
export JAVA_HOME="/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home"
# 保存 source一下
source ~/.zshrc
然后测试下是否OK
java -version
# java version "1.8.0_321"
# Java(TM) SE Runtime Environment (build 1.8.0_321-b07)
# Java HotSpot(TM) 64-Bit Server VM (build 25.321-b07, mixed mode)
javac -version
# javac 1.8.0_321
echo $JAVA_HOME
# /Library/Java/JavaVirtualMachines/jdk1.8.0_321.jdk/Contents/Home
我没有在win上进行配置,我的舍友在win11上按照这篇指南配置成功
应该和Mac类似, 路径可能有所不同自行确认
首先下载soot的jar包,选择v4.3.0/下的soot-4.3.0-jar-with-dependencies.jar, 其他文件按自己需要下载
为了方便我把下载下来的jar重命名为了soot.jar
并放在了/Applications
目录下, 并且像上面设置JAVA_HOME
一样设置了SOOT_HOME="/Applications/soot.jar"
接着检验下是否安装成功
java -cp $SOOT_HOME soot.Main -h
# General Options:
# -coffi Use the good old Coffi front end for parsing
# Java bytecode (instead of using ASM).
# -jasmin-backend Use the Jasmin back end for generating Java
# bytecode (instead of using ASM).
# -h, -help Display help and exit
# ...
在项目下pom.xml
文件中加入以下
<dependency>
<groupId>org.soot-oss</groupId>
<artifactId>soot</artifactId>
<version>4.3.0</version>
<scope>compile</scope>
</dependency>
进入File -> Project Structure -> Modules
,右侧Module SDK
下面的+ -> 1 JARs or Directories
,找到你下的soot.jar
即可
Jimple
是soot提供的四种IR(中间表示)之一,这里暂时不多做介绍
输入java -cp $SOOT_HOME soot.Main -cp . -pp -f J com.Foo
:
-cp $SOOT_HOME
: 指定soot class path-cp .
: 指定所要分析的项目的path-pp
: 指定soot去自动搜索java的path, 主要是rt.jar
和jce.jar
, soot会去$JAVA_HOME
下找,所以必须先设置好-f J
: 指定输出文件类型, J
就是jimplecom.Foo
: 你要分析的class的名字.jimple
文件啦会比较复杂一些,主要关注setupSoot(...)
函数,其他的部分可以暂时不理解
package com;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import soot.*;
import soot.options.Options;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collections;
public class DemoSoot {
// 省略了一些无关紧要的...
private final static Logger logger = LoggerFactory.getLogger("LVA Logger");
// 主要关注这里
public static void setupSoot(String className) {
// Soot class path
// "./"是我的项目根目录, "src/main/java"下存着当前com.DemoSoote, "src/test/java"下存着我要分析的类Foo
String classesDirMain = "./src/main/java";
String classesDirTest = "./src/test/java";
String jceDir = System.getProperty("java.home") + "/lib/jce.jar";
String jrtDir = System.getProperty("java.home") + "/lib/rt.jar";
String path = jrtDir + File.pathSeparator + jceDir;
path += File.pathSeparator + classesDirMain + File.pathSeparator + classesDirTest;
// Init Scene
Scene.v().setSootClassPath(path);
// Add necessary opts
Options.v().set_process_dir(Collections.singletonList(classesDirMain));
Options.v().set_process_dir(Collections.singletonList(classesDirTest));
// Set Main class
SootClass mainClass = Scene.v().loadClassAndSupport(className);
Scene.v().setMainClass(mainClass);
}
public static void main(String[] args) throws IOException {
// 确保你要分析的文件能够编译,不然会寄掉
String mainClassName = "Foo";
try {
setupSoot(mainClassName);
} catch (CompilationDeathException e) {
logger.error(e.toString());
return;
}
// 启动soot, 相当于命令行使用中的"soot.Main"
soot.Main.main(args);
logger.info("---------soot.Main.main()---------");
// 获取 Foo
SootClass mainClass = Scene.v().getMainClass();
logger.info(String.format("Loading Class: %s ...", mainClass.getName()));
// 获取 Foo中的main函数
String methodSignature = "void main(java.lang.String[])";
SootMethod mainMethod = mainClass.getMethod(methodSignature);
logger.info(String.format("Loading Method: %s ...", mainMethod));
// 通过获取的main函数生成jimpleBody
Body jimpleBody = mainMethod.retrieveActiveBody();
logger.info("Retrieving method body ...");
//将jimple写入文件中, 自行设定你的"rootPath"
String rootPath = "/Users/caohch1/Projects/ProgramAnalysis/HW1/jimples/";
String filePath = rootPath + mainClass.getName() + "-" + mainMethod.getName() + ".jimple";
FileWriter fileWriter = new FileWriter(filePath);
try (BufferedWriter out = new BufferedWriter(fileWriter)) {
out.write(jimpleBody.toString());
} finally {
logger.info(String.format("Writing %s ...", filePath));
}
}
}
还有一种更简单的方式,前提是正确配置了JAVA_HOME路径
String classesDirMain = "./src/main/java/";
String classesDirTest = "./src/test/java/";
String classesDirCurr = "./";
void setupSoot() {
Options.v().set_prepend_classpath(true);
Options.v().set_process_dir(Collections.singletonList(classesDirMain));
Options.v().set_process_dir(Collections.singletonList(classesDirTest)); // 注意,会覆盖掉上一行
Options.v().set_process_dir(Collections.singletonList(classesDirCurr));
}
之后会继续分享soot学习中的笔记,主要可能包括对API的使用和理解,但是我也是刚刚接触soot,有问题之处可以一起讨论,我的邮件: chc2267408610@gmail.com
同时分享几个主要的soot的教程,虽然都有些难啃或者过时