package dev.navids.soottutorial.android; import soot.*; import soot.jimple.*; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; public class AndroidLogger { private final static String USER_HOME = System.getProperty("user.home"); private static String androidJar = USER_HOME + "/Library/Android/sdk/platforms"; static String androidDemoPath = System.getProperty("user.dir") + File.separator + "demo" + File.separator + "Android"; static String apkPath = androidDemoPath + File.separator + "/calc.apk"; static String outputPath = androidDemoPath + File.separator + "/Instrumented"; public static void main(String[] args){ if(System.getenv().containsKey("ANDROID_HOME")) androidJar = System.getenv("ANDROID_HOME")+ File.separator+"platforms"; // Clean the outputPath final File[] files = (new File(outputPath)).listFiles(); if (files != null && files.length > 0) { Arrays.asList(files).forEach(File::delete); } // Initialize Soot InstrumentUtil.setupSoot(androidJar, apkPath, outputPath); // Add a transformation pack in order to add the statement "System.out.println() at the beginning of each Application method PackManager.v().getPack("jtp").add(new Transform("jtp.myLogger", new BodyTransformer() { @Override protected void internalTransform(Body b, String phaseName, Map options) { // First we filter out Android framework methods if(AndroidUtil.isAndroidMethod(b.getMethod())) return; JimpleBody body = (JimpleBody) b; UnitPatchingChain units = b.getUnits(); List generatedUnits = new ArrayList<>(); // The message that we want to log String content = String.format("%s Beginning of method %s", InstrumentUtil.TAG, body.getMethod().getSignature()); // In order to call "System.out.println" we need to create a local containing "System.out" value Local psLocal = InstrumentUtil.generateNewLocal(body, RefType.v("java.io.PrintStream")); // Now we assign "System.out" to psLocal SootField sysOutField = Scene.v().getField(""); AssignStmt sysOutAssignStmt = Jimple.v().newAssignStmt(psLocal, Jimple.v().newStaticFieldRef(sysOutField.makeRef())); generatedUnits.add(sysOutAssignStmt); // Create println method call and provide its parameter SootMethod printlnMethod = Scene.v().grabMethod(""); Value printlnParamter = StringConstant.v(content); InvokeStmt printlnMethodCallStmt = Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(psLocal, printlnMethod.makeRef(), printlnParamter)); generatedUnits.add(printlnMethodCallStmt); // Insert the generated statement before the first non-identity stmt units.insertBefore(generatedUnits, body.getFirstNonIdentityStmt()); // Validate the body to ensure that our code injection does not introduce any problem (at least statically) b.validate(); } })); // Run Soot packs (note that our transformer pack is added to the phase "jtp") PackManager.v().runPacks(); // Write the result of packs in outputPath PackManager.v().writeOutput(); } }