利用Soot插装apk实战

参考文献:

1. 使用keytool与jarsigner(这两个都是JDK自带的工具)给插装代码后的apk签名:点击打开链接

2.在安卓模拟器上安装插装并签名了的apk:点击打开链接

3.利用Soot插装apk: Instrumenting_Android_and_Java_Applications_as_Easy_as_abc

  这里主要讲下步骤

1. 给插装好的apk签名;

   生成私钥

  keytool:

 打开windows命令行工具,输入:keytool -genkey -v -keyalg DSA -keysize 1024 -sigalg SHA1withDSA  -validity 20000  -keystore MyDevel.keystore -alias devel   -keypass MyDevel -storepass MyDevel

  用私钥进行签名

  jarsigner:

  打开windows命令行工具,输入:jarsigner  -verbose -sigalg SHA1withDSA -digestalg SHA1  -keystore myDevel.keystore -storepass MyDevel test.apk devel


  详细用法可看开头给的链接

2. 在安卓模拟器上安装apk

  未签名的apk是无法在模拟器上安装的,当我们往apk里插了代码后,我们便需要重新对插装了代码的apk进行上面这个步骤的签名,否则安装时会报错,提示未给定证书。

  先启动模拟器,进入platform-tools目录下,运行如下:

利用Soot插装apk实战_第1张图片


3. 使用soot插装apk应用程序

 首先编写一个需要被插装代码的apk,为了方便起见,编写的apk非常简单,只有一个Activity,未插装前代码如下:

package com.example.instrumentapk;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.widget.Toast;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		//Toast toast=Toast.makeText(MainActivity.this,"toast message", Toast.LENGTH_LONG);
		//toast.show();
		//Log.i("test", "log infor");		
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}
我们插装的目的是在函数onCreate函数里的setContentView()函数之后插入log消息,即插入Log.i("test","log infor!")语句。

我们新建一个Java项目,导入soot-trunk.jar包(nightly-build版本),编写代码如下:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;



import soot.Body;
import soot.BodyTransformer;
import soot.G;
import soot.Pack;
import soot.PackManager;
import soot.Scene;
import soot.SootClass;
import soot.SootMethod;
import soot.Transform;
import soot.Unit;
import soot.jimple.InvokeStmt;
import soot.jimple.Jimple;
import soot.jimple.StaticInvokeExpr;
import soot.jimple.Stmt;
import soot.jimple.StringConstant;
import soot.options.Options;


public class InstrumentApk {
    public final static String androidJar="D:\\Eclipse\\adt-bundle-windows-x86-20130917\\sdk\\platforms\\";
	public final static String APK="D:\\Eclipse\\SootTest\\InstrumentApk.apk";
    public static void initsoot(String[] args){
		//G.reset();
		Options.v().set_allow_phantom_refs(true);//设置允许伪类(Phantom class),指的是soot为那些在其classpath找不到的类建立的模型
	    Options.v().set_prepend_classpath(true);//prepend the VM's classpath to Soot's own classpath
		Options.v().set_output_format(Options.output_format_dex);//设置soot的输出格式
	    Options.v().set_android_jars(androidJar);//设置android jar包路径
	    Options.v().set_src_prec(Options.src_prec_apk);
	    Options.v().set_process_dir(Collections.singletonList(APK));
	    
	    //Options.v().set_soot_classpath("");
	    Scene.v().loadNecessaryClasses();
	    //call soot.Main
	    //soot.Main.main(args);
	}
	public static void main(String[] args) {
		initsoot(args);
		PackManager.v().getPack("jtp").add(new Transform("jtp.MyTransform", new MyTransform()));//添加自己的BodyTransformer
		PackManager.v().runPacks();
		PackManager.v().writeOutput();
		
	}

}

// custom bodyTransformer
class MyTransform extends BodyTransformer{
	@Override
	protected void internalTransform(Body arg0, String arg1,
			Map<String, String> arg2) {
		Iterator<Unit> unitsIterator=arg0.getUnits().snapshotIterator();//获取Body里所有的units,一个Body对应Java里一个方法的方法体,Unit代表里面的语句
		while(unitsIterator.hasNext()){
			Stmt stmt=(Stmt)unitsIterator.next();//将Unit强制转换为Stmt,Stmt为Jimple里每条语句的表示
			if(stmt.containsInvokeExpr()){//如果是一条调用语句
				String declaringClass=stmt.getInvokeExpr().getMethod().getDeclaringClass().getName();//获取这个方法所属的类
				String methodName=stmt.getInvokeExpr().getMethod().getName();//获取这个方法的名称
				if(declaringClass.equals("android.app.Activity")&&methodName.equals("setContentView")){
					List<Unit> toastUnits=makeToast(arg0, "toast infor!");
					arg0.getUnits().insertAfter(toastUnits, stmt);//在这条语句之后插入Toast消息
					break;
				}
			}
		}
		
	}
	private List<Unit> makeToast(Body body,String toast){
		List<Unit> unitsList=new ArrayList<Unit>();
		//插入语句Log.i("test",toast);
		SootClass logClass=Scene.v().getSootClass("android.util.Log");//获取android.util.Log类
		SootMethod sootMethod=logClass.getMethod("int i(java.lang.String,java.lang.String)");
		StaticInvokeExpr staticInvokeExpr=Jimple.v().newStaticInvokeExpr(sootMethod.makeRef(),StringConstant.v("test"),StringConstant.v(toast));
		InvokeStmt invokeStmt=Jimple.v().newInvokeStmt(staticInvokeExpr);
		unitsList.add(invokeStmt);
		return unitsList;
	}
}

运行之后,其会在sootOutput目录下生成一个同名字的apk,本例中即为InstrumentApk.apk,记得要用开头的方法给这个apk签名,我们用 dex2jar 反编译这个apk,用jd-gui 查看它代码究竟有没有插入成功,如下:


利用Soot插装apk实战_第2张图片

可以看到,确实将Log.i("test","toast infor!") 这条语句插入到apk中。

接着我们安装插装并签名好的apk到模拟器并运行,打开DDMS,查看LogCat,如下:


可以看到插装了代码的apk能够正常运行,至此,利用soot插装apk便完成了。

讨论:如果原来的Activity代码中并没有导入java.util.Log包的话,插入Log.i()应该就没有作用了。。。。。。

你可能感兴趣的:(反编译,数据流分析,Android漏洞分析,android分析,Soot)