当前位置: 首页 > 工具软件 > Soot > 使用案例 >

soot基础 -- 解析java文件

程皓轩
2023-12-01

问题:

      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(); //关键:启动输出。(不运行此语句不会进行输出)
 类似资料: