您的位置:首页 > 移动开发

soot实现Android Apps插桩(二)

2016-05-16 17:13 891 查看
译者注:

上篇介绍了在apk中插入System.out.println(“HELLO”)句子。但是,利用soot插桩java/apk,其实都可以插入类、方法等。soot的分析/插桩等都是在Jimple中间语言上进行的,因此,本文介绍利用soot生成一个完整类的过程,希望能够从中受到启发。

首先,我们需要创建一个将methods放入的class,下述步骤是整个创建类文件的必须过程。

加载java.lang.Object和库类

加载java.lang.Object,它是Java class层次的根结点。

在扩展soot框架的代码中,这一步是不需要的;在扩展soot框架时,当用户代码被调用时,类文件的加载已经完成了。

Scene.v().loadClassAndSupport(“java.lang.Object”);


这行代码将使Soot加载java.lang.Object类,并且创建合适的SootClass对象,为它的字段创建SootMethod和SootFields。当然,其他对象都是继承于java.lang.Object。loadClassAndSupprt将加载这个特殊类的传递包,以至于所有需要java.lang.Object类型的类能够自己加载。

这个过程就是Resolution.

因为我们知道HelloWorld程序将使用到标准类库,因此我们必须resolve:

Scene.v().loadClassAndSupport(“java.lang.System”);
这行引用了Scene.v()。Scene是一个程序中所有SootClass的容器,并且提供了很多有用的方法。通过Scene.v()可以得到一个Scene对象。

注释:Soot加载类一般从classfiles/.Jimple两种输入文件,当是前一种时,Soot将加载每个类文件中常量池引用的所有类,当从.Jimple文件中加载时,soot将只加载需要的类型。

创建一个新的SootClass对象

创建HelloWorldSootClass,并且设置它的父类为java.lang.Object

sClass = newSootClass(“HelloWorld”,Modifier.PUBLIC);
这行代码表示创建一个SootClass对象,代表一个public类HelloWorld。

sClass.setSuperClass(Scene.v().getSootClass(“java.lang.Object”));


这是设置新创建的SootClass对象的父类为java.lang.Object。注意:在Scene中getSootClass方法的使用(译者加:getSootClass将String表示的类转换为SootClass)。

Scene.v().addClass(sClass);
这是将新创建的HelloWorld类加到Scene中。一旦类创建它都应该属于Scene。

向SootClasses中添加方法

给HelloWorld穿件一个main()方法

现在,我们已经有一个SootClass,我们需要向SootClass中添加方法。

method=new SootMethod("main",Arrays.asList(newType[]{ArrayType.v(RefType.v("java.lang.String"), 1)}),VoidType.v(),Modifier.PUBLIC|Modifier.STATIC);
我们创建了一个新的publicstatic方法mian,并且它的参数是java.lang.String对象,返回类型为void。

SootMethod的构造携带一个list,因此,我们利用Java方法Arrays.asList去创建一个列表,这个列表来自于利用new Type[]生成的一元数组。在list中,我们put一个数组,并且数组类型是java.lang.String的一元数组类型。其中RefType的调用是返回与java.lang.String类对应的类型。

Types:每个SootClass代表一个Java对象,我们可以通过一个给定类型的对象去初始化class,type和class是紧密相关却又独立的。如果要通过java.lang.String类名得到它的类型,可以通过RefType.v(“java.lang.String”),如果给定一个SootClass对象sc,也可以通过调用sc.getType()得到匹配的类型。

sClass.addMethod(method);


这行代码向类中添加方法。

向方法中添加代码

译者加:上篇就是直接向方法中添加System.out.println(“HELLO”)。如果向某个apk/java中添加类,就需要从本篇的《创建一个新的SootClass对象》开始;如果向某个apk/java中添加方法,则应该从本篇的《向SootClasses中添加方法》开始。

如果一个方法没有包含任何的代码,那么它就是无用的。我们将向main方法中添加一些代码。为了达到目的,我们必须选择一种中间语言(Jimple、Baf、Shample等)添加到代码中。

创建JimpleBody:

在soot中,我们将Body依附于一个SootMethod去关联代码和方法。每个Body它与哪个SootMethod对应,但是一个SootMethod仅有一个active Body对应(可以通过SootMethod.getActiveBody()来获取)。多种Intermediate representations(中间语言)提供了Body的不同类型;Soot有JimpleBody、ShimpleBody、BafBody和GrimpBody。

一个Body有三个重要的属性:Local S链、Trap S链和Unit S链,链就相当于列表结构,提供O(1)的复杂度来插入和删除元素。Locals是Body中的局部变量;Trap表示哪个units捕捉哪种异常;Unit S表示句子本身。

注意:Unit在Jimple表示statemetns,而在Baf中表示instructions。

给main类创建一个Jimple Body,并给Body增加locals/instructions。

JimpleBody body = Jimple.v().newBody(method);
Method.setActiveBody(body);


增加Local

units.add(Jimple.v().newIdentityStmt(arg,Jimple.v().newParameterRef(ArrayType.v(RefType.v(“java.lang.String”),1),0)));

SootMethod声明了它的参数,but these arenot bound to the Local S of the Body.IdentityStmt将第一个参数的值赋给arg,第一个参数类型为string数组。

//insert “tmpRef.println(“Hello world!”)”
{
SootMethod toCall =
Scene.v().getMethod(“<java.io.PrintStream:  void println(java.lang.String)>”);
units.add(Jimple.v().newInvokeStmt(
Jimple.v().newVirtualInvokeExpr(tmpRef,toCall.makeRef(),StringConstant.v(“Hello World!”))));
}

我们通过方法签名<java.io.PrintStream:void println(java.lang.String)>(方法名为println,属于类PrintStream,返回类型为void,并且携带一个String参数,这些数据足以识别一个方法)得到该方法,并且调用它,参数为StringConstant “Hello World!”。

写类文件

为了使程序输出为.class文件,方法的body必须从Jimple转换成Jasmin,并且汇编成字节码。通过JasminOutputStream汇编成字节码。

我们先构造输出流(outputstream),将携带Jasmin源码并输出.class文件。我们可以自定义文件名,也可以让soot自己生成正确的文件名。

String fileName = SourceLocator.v().getFileNameFor(sClass,Options.output_format_class);
OutputStream streamOut = new JasminOutputStream(new FileOutputStream(fileName));
PrintWriter writerOut = new PrintWriter(new OutputStreamWriter(streamOut));


我们先将Jimple转化为Jasmin,并且将Jasmin类输到outputstream。

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);
PrintPriter writerOut = new PrintWriter(new OutputStreamWriter(streamOut));
Printer.v().printTo(sClss,writerOut);
writerOut.flush();
streamOut.close();
我们省略了JaminOutputStream,并且调用了方法的printTo。
下面是完整源码

/* Soot - a J*va Optimization Framework
* Copyright (C) 2008 Eric Bodden
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Map;

import soot.ArrayType;
import soot.Body;
import soot.BodyTransformer;
import soot.G;
import soot.Local;
import soot.PackManager;
import soot.Printer;
import soot.RefType;
import soot.Scene;
import soot.SootClass;
import soot.SootMethod;
import soot.SourceLocator;
import soot.Transform;
import soot.Type;
import soot.VoidType;
import soot.jimple.JasminClass;
import soot.jimple.Jimple;
import soot.jimple.JimpleBody;
import soot.jimple.StringConstant;
import soot.options.Options;
import soot.toolkits.graph.ExceptionalUnitGraph;
import soot.util.Chain;
import soot.util.JasminOutputStream;

public class MyMain {

public static void main(String[] args) {

SootClass sClass;
SootMethod method;

//Resolve dependencies
Scene.v().loadClassAndSupport("java.lang.Object");
Scene.v().loadClassAndSupport("java.lang.System");

//Declare 'public class HelloWorld'
sClass = new SootClass("HelloWorld", Modifier.PUBLIC);

//extends Object
sClass.setSuperclass(Scene.v().getSootClass("java.lang.Object"));
Scene.v().addClass(sClass);

//Create the method,public static void main(String[])
method=new SootMethod("main",Arrays.asList(new Type[]{ArrayType.v(RefType.v("java.lang.String"), 1)}),
VoidType.v(),Modifier.PUBLIC|Modifier.STATIC);

sClass.addMethod(method);

//Create the method body
{
//create emtpy body
JimpleBody body = Jimple.v().newBody(method);
method.setActiveBody(body);
Chain units = body.getUnits();
Local arg,tmpRef;

//Add some locals,java.lang.String l0
arg = Jimple.v().newLocal("l0", ArrayType.v(RefType.v("java.lang.String"), 1));
body.getLocals().add(arg);

//Add locals,java.io.printStream tmpRef
tmpRef = Jimple.v().newLocal("tmpRef", RefType.v("java.io.PrintStream"));
body.getLocals().add(tmpRef);

//add "l0=@parameter0"
units.add(Jimple.v().newIdentityStmt(arg,
Jimple.v().newParameterRef(ArrayType.v(RefType.v("java.lang.String"), 1), 0)));

//add "tmpRef = java.lang.System.out"
units.add(Jimple.v().newAssignStmt(tmpRef, Jimple.v().newStaticFieldRef(
Scene.v().getField("<java.lang.System: java.io.PrintStream out>").makeRef())));

//insert "tmpRef.println("Hello world!")"
{
SootMethod toCall = Scene.v().getMethod("<java.io.PrintStream: void println(java.lang.String)>");
units.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(tmpRef, toCall.makeRef(),StringConstant.v("Hello World!"))));

}

//insert "return"
units.add(Jimple.v().newReturnVoidStmt());
}

try {
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();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}


生成的Jimple表示的文件内容如下

public class HelloWorld extends java.lang.Object
{

public static void main(java.lang.String[])
{
java.lang.String[] l0;
java.io.PrintStream tmpRef;

l0 := @parameter0: java.lang.String[];
tmpRef = <java.lang.System: java.io.PrintStream out>;
virtualinvoke tmpRef.<java.io.PrintStream: void println(java.lang.String)>("Hello World!");
return;
}
}


原文地址

下篇博客将利用soot实现Android Apps插桩(一)(二)中的内容,实现一个较为复杂的插桩过程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: