问题:
soot如何接受java文件,并且将其解析出来?
这里主要说明两种解析方式,通过调用soot接口的方式,以及以类似于命令行的方式向soot传递参数的方式。
两种方式本质上是相同的,看读者更容易接受哪一种。
一、通过soot提供的接口来实现soot的使用。
1.首先我构建了一个测试类:
public class TestMain {
public static void main(String[] args) {
C(1);
}
public static void A(){
System.out.println("inside A");
}
public static void B(){
System.out.println("inside B");
}
public static void C(int i ){
if(i > 1){
A();
}else{
B();
}
}
}
2.接下来我要用soot来识别出这个java类。
- 路径的设置十分重要,路径不正确可能导致无法识别。
- Options是全局的参数设置,并且是只有一个(单例)。
public class TestFileInput {
public static final String path = "test/javaTest";
public static void main(String args[]) {
initial(path);
SootClass appclass = Scene.v().loadClassAndSupport("TestMain");//若无法找到,则生成一个。
System.out.println("the main class is :" + appclass.getName());
//获取类中的相关的方法
Iterator<SootMethod> methodIt = appclass.getMethods().iterator();
while(methodIt.hasNext()){
System.out.println("the function member is : " + methodIt.next().getName());
}
}
private static void initial(String apkPath) {
soot.G.reset();
Options.v().set_allow_phantom_refs(true);
Options.v().set_prepend_classpath(true);
Options.v().set_validate(true);
Options.v().set_output_format(Options.output_format_jimple);
Options.v().set_src_prec(Options.src_prec_java);
Options.v().set_process_dir(Collections.singletonList(apkPath));//路径应为文件夹
Options.v().set_keep_line_number(true);
// Options.v().set_whole_program(true);
Options.v().set_no_bodies_for_excluded(true);
Options.v().set_app(true);
// Scene.v().setMainClass(appclass); // how to make it work ?
Scene.v().addBasicClass("java.io.PrintStream", SootClass.SIGNATURES);
Scene.v().addBasicClass("java.lang.System", SootClass.SIGNATURES);
Scene.v().addBasicClass("java.lang.Thread", SootClass.SIGNATURES);
Scene.v().loadNecessaryClasses();
}
}
3.结果如下:
the main class is :TestMain
the function member is : main
the function member is : A
the function member is : B
the function member is : C
the function member is : <init>
二、 以类似于命令行的方式向soot传递参数
这次我们通过构建查看call graph(函数调用关系图)相关的一些内容。
1.待检测的类如下:
- 该文件所在的路径: 项目 / test / testCallGraph / testers
- Call graph的生成是必须得有入口函数(main)的。
- 注意从main 开始追踪调用的结构。
package testers;
public class CallGraphs
{
public static void main(String[] args) {
doStuff();
}
public static void doStuff() {
new A().foo();
}
}
class A
{
public void foo() {
bar();
}
public void bar() {
}
}
2.通过soot解析上面这个类的call graph.
代码如下:
soot.Main.main(args) 相当于命令行处理。main会将传递的数组进行解析,对于命令解析成soot可以识别的东西。
- 命令参数以类似于数组的方式传递给main().
public class CallGraphExample
{
public static void main(String[] args) {
List<String> argsList = new ArrayList<String>(Arrays.asList(args));
//相当于传入命令行参数。cmd上操作命令。
argsList.addAll(Arrays.asList(new String[]{
"-w",
// "-v",
"-process-path",
"test/testCallGraph",
"-main-class",
"testers.CallGraphs",//main-class
"testers.CallGraphs",//argument classes
"testers.A" //
}));
PackManager.v().getPack("wjtp").add(new Transform("wjtp.myTrans", new SceneTransformer() {
@Override
protected void internalTransform(String phaseName, Map options) {
CHATransformer.v().transform();
SootClass a = Scene.v().getSootClass("testers.A");
SootMethod src = Scene.v().getMainClass().getMethodByName("doStuff");
CallGraph cg = Scene.v().getCallGraph();
Iterator<MethodOrMethodContext> targets = new Targets(cg.edgesOutOf(src));
while (targets.hasNext()) {
SootMethod tgt = (SootMethod)targets.next();
System.out.println(src + " may call " + tgt);//这里说的是可能,并不是一定就正确。
}
}
}));
args = argsList.toArray(new String[0]);
soot.Main.main(args);
}
}
分析的结果
- 只关注doStuff()所可能调用的方法。(这里说可能,是因为call graph的构造,并不是绝对的准确的事情)
<testers.CallGraphs: void doStuff()> may call <java.lang.Object: void <clinit>()>
<testers.CallGraphs: void doStuff()> may call <testers.A: void foo()>
<testers.CallGraphs: void doStuff()> may call <testers.A: void <init>()>
三、Soot输出中间代码
- 【Main.java】在Soot中有个Main.java类,这个类主要是负责解析命令行的,在这里面可以找到命令 与 程序类的方法 之间的对应关系(尤其注意Main中的run()方法)。
- 【PackManager】PackMananger是负责进行阶段处理的,它控制着各种执行的运行,不启动则不会运行相应的阶段。(将类的加载与类的分析运行相区别,见文章:soot -- 常见参数配置)
soot.G.reset();
Options.v().set_allow_phantom_refs(true);
Options.v().set_prepend_classpath(true);
Options.v().set_validate(true);
Options.v().set_output_format(Options.output_format_jimple);//1.输出的形式
Options.v().set_output_dir("sootOutput"); //2.输出的文件目录
Options.v().set_src_prec(Options.src_prec_java);
Options.v().set_process_dir(Collections.singletonList(apkPath));
Options.v().set_whole_program(true);
Options.v().set_no_bodies_for_excluded(true);
Scene.v().addBasicClass("java.io.PrintStream", SootClass.SIGNATURES);
Scene.v().addBasicClass("java.lang.System", SootClass.SIGNATURES);
Scene.v().addBasicClass("java.lang.Thread", SootClass.SIGNATURES);
Scene.v().loadNecessaryClasses();
PackManager.v().writeOutput(); //关键:启动输出。(不运行此语句不会进行输出)