参考文献:
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目录下,运行如下:
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; } }
可以看到,确实将Log.i("test","toast infor!") 这条语句插入到apk中。
接着我们安装插装并签名好的apk到模拟器并运行,打开DDMS,查看LogCat,如下:
可以看到插装了代码的apk能够正常运行,至此,利用soot插装apk便完成了。
讨论:如果原来的Activity代码中并没有导入java.util.Log包的话,插入Log.i()应该就没有作用了。。。。。。