Soot可以实现不同文件的转换,比如从class到jimipl,jimple搭配calss,java到class,并且现在也引入了Dexpler能够解析Dalivk文件。因此的它的功能早已不局限于程序分析或者代码优化。你甚至可以修改源代码,然后重新生成的文件,进行插桩等。因此,不难想到Soot可以在环境中可以创建类(你没有听错,当然如果有Java背景的同学可能也听说过一些API提供了创建类的功能,但那都是在字节码的层次上进行的)。Soot也提供了差不多的功能,你可以使用这些API在Scence创建类。这很有用,比如在FlowDroid中作者就通过创建Dummy_Main方法创建了Android的虚main方法,以此来解决Android中Spark进行构建调用图没有主方法的问题(作者在Dummy方法中初始化了组件,因此每个组件都有响应的创建地点)。本篇文章主要介绍如何在Scence中创建一个类。
2、创建一个Class我们准备创建一个Hello类,他继承自万类之祖java.lang.Object。
2.1 加载必要的类因为我们要用到java.lang.Object,而Scence中现在什么也没有,因此我们首先要让类加载进来:
SootClass objectClass = Scene.v().loadClassAndSupport("java.lang.Object");
这时你再去看环境中的类,你发现该类被加载了,但是环境中不止这一个类,这是因为在加载该类的时候,它会引用很多其他的类,这些其他的类也会被加载进Scnece中,并且其他类也可能引用另外一些类,这样递归下去,直到所有用到的类都被加载进Scence中。这个过程叫解析resolve,它会为加载到Scence中的每个类创建一个SootClass对象,保存他们的字段,方法等信息。
因为我们的方法中还需要使用java.lang.System类,因此我们还要把他加载进来:
SootClass sysClass = Scene.v().loadClassAndSupport("java.lang.System");
Scence就是一个容器,里边包含了各种SootClass对象,并且提供了很多工具。它是一个单例类,你可以通过Scence.v()获得它。
这里提醒以下细节:Soot通过解析文件来创建SootClass,可以通过。jimple文件获取或者是classfile文件获取,当通过前者的时候和我描述的是一样的,但是当通过classfile文件时,Soot会加载常量池中的所有的类。(好好学Java虚拟机吧,程序分析还是和语言很相关的)
2.2 创建一个SootClass对象我们创建一个Hello的SootClass类,并指定它的父类为java.lang.Object.
SootClass helloClass = new SootClass("Hello", Modifier.PUBLIC);
helloClass.setSuperclass(Scene.v().getSootClass("java.lang.Object"));
但是环境中还没有这个Hello,我们需要把他添加到Scence中。
Scene.v().addClass(helloClass);
2.3 在Hello中添加方法
我们现在的Hello中没有任何方法,我们准备向他添加一个main方法:
SootMethod method = new SootMethod("main", Arrays.asList(new Type[]{ArrayType.v(RefType.v("java.lang.String"), 1)}),
VoidType.v(), Modifier.PUBLIC | Modifier.STATIC);
这里注意下SootMethod的构造函数的参数就行,方法名,参数类型,返回值类型,限定符
注意这里的Type它用来表示一个对象的类型,在Soot中每个value都是有Type的。
我们接着将创建的main方法添加到类中:
helloClass.addMethod(method);
2.4 为方法添加代码
此时的方法仍是不能用的,因为没有任何代码,我们接下来为method中添加代码。在Soot中,我们会将一个Body和一个method绑定,每个Body都只带自己相关的method。Soot提供了一种几种中间表示,每种中间表示都有相关的Body的数据结构,
一个Body含有三种Chains:Local,Trap,Unit,Chain时一种类似列表的结构,你可以随机访问Chain中任意位置的元素。具体内容我在另一篇文章中有讲.
我们接下来为main创建一个JimpleBody,把用到的Local和执行的指令添加到该Body中。
JimpleBody body = Jimple.v().newBody(method);
method.setActiveBody(body);
我们为main方法创建了body,并让他称为activeBody。
2.5 为Body添加局部变量Local l0 = Jimple.v().newLocal("l0",ArrayType.v(RefType.v("java.lang.String"), 1));
body.getLocals().add(l0);
2.6 添加Unit
body.getUnits().add(Jimple.v().newIdentityStmt(l0,Jimple.v().newParameterRef(ArrayType.v(RefType.v("java.lang.String"),1),0)));
上面这条IdentityStmt语句,是参数给对应的变量赋值,你如果很熟悉Jimple,应该不难。
Local out = Jimple.v().newLocal("out", RefType.v("java.io.PrintStream"));
body.getUnits().add(out)
AssignStmt assignStmt = Jimple.v().newAssignStmt(out,
Jimple.v().newStaticFieldRef(Scene.v().getField("" ).makeRef()));
body.getUnits().add(assignStmt);
SootMethod callMethod = Scene.v().getMethod("" );
body.getUnits().add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(out,callMethod.makeRef(),StringConstant.v("hello,world"))));
下面接着便又向Body中添加了几条语句。
2.7 输出以class文件方式输出:
String fileName = SourceLocator.v().getFileNameFor(sClass, Options.output_format_class);
OutputStream streamOut = new JasminOutputStream(new FileOutputStream(fileName));
PrintWriter writerOut = new PrintWriter(new OutputStreamWriter(streamOut));
JasminClass jasminClass = new soot.jimple.JasminClass(sClass);
jasminClass.print(writerOut);
writerOut.flush();
streamOut.close();
以Jimple的形式输出:
String fileName = SourceLocator.v().getFileNameFor(sClass, Options.output_format_jimple);
OutputStream streamOut = new FileOutputStream(fileName);
PrintWriter writerOut = new PrintWriter(new OutputStreamWriter(streamOut));
Printer.v().printTo(sClass, writerOut);
writerOut.flush();
streamOut.close();
看到上面你有没有感觉到后悔没有学好编译原理啊!
整个代码的实现可以看下官方给的链接。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)