soot实现Android Apps插桩(二)

译者注:

上篇介绍了在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插桩(一)(二)中的内容,实现一个较为复杂的插桩过程。


你可能感兴趣的:(数据流分析,android分析,android隐私检测,Soot)