package de.ecspride;
import java.io.File;
import java.net.URISyntaxException;
import java.util.Collections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import soot.Scene;
import soot.SootClass;
import soot.options.Options;
/**
* @author Siegfried Rasthofer
*/
public class Settings {
// log
Logger log = LoggerFactory.getLogger(Settings.class);
//singleton
public static Settings instance = new Settings();
//bin folder for the java classes that will be added to the apk
public final String bin = getCodeLocation();
//information about all events (method signatrues) we do care
public static String eventInformationFile = "./files/eventInformation.xml";
// Java classes that will be added to the apk
public final static String javaPackageToAdd = "de.ecspride.javaclasses";
public final static String REMOTE_SERVICE_CONNECTION_JAVA = "de.ecspride.javaclasses.RemoteServiceConnection";
public final static String INSTRUMENTATION_HELPER_JAVA = "de.ecspride.javaclasses.InstrumentationHelper";
public final static String EVENT_PEP_JAVA = "de.ecspride.javaclasses.EventPEP";
public final static String INCOMING_HANDLER_JAVA = "de.ecspride.javaclasses.IncomingHandler";
public final static String SANITIZER = "de.ecspride.javaclasses.Sanitizer";
public final static String WAIT_PDP_ACTIVITY = "de.ecspride.javaclasses.WaitPDPActivity";
public final static String WAIT_PDP_ACTIVITY1 = "de.ecspride.javaclasses.WaitPDPActivity$1";
public final static String WAIT_PDP_ACTIVITY11 = "de.ecspride.javaclasses.WaitPDPActivity$1$1";
public final static String WAIT_PDP_ACTIVITY2 = "de.ecspride.javaclasses.WaitPDPActivity$2";
public final static String WAIT_PDP_ACTIVITY21 = "de.ecspride.javaclasses.WaitPDPActivity$2$1";
public final static String[] class2AddList = {
REMOTE_SERVICE_CONNECTION_JAVA,
INSTRUMENTATION_HELPER_JAVA,
EVENT_PEP_JAVA,
INCOMING_HANDLER_JAVA,
SANITIZER,
WAIT_PDP_ACTIVITY,
WAIT_PDP_ACTIVITY1,
WAIT_PDP_ACTIVITY2,
WAIT_PDP_ACTIVITY11,
WAIT_PDP_ACTIVITY21
};
public final String INSTRUMENTATION_HELPER_INITIALIZE_METHOD = "initializeEventPEP";
//source categories that should be considered by the analysis (all the corresponding methods in the InstrumentationOptions.sourceFile are considered)
public final String sourceCategories = "HARDWARE_INFO|UNIQUE_IDENTIFIER|LOCATION_INFORMATION|NETWORK_INFORMATION|ACCOUNT_INFORMATION|"
+ "EMAIL_INFORMATION|FILE_INFORMATION|BLUETOOTH_INFORMATION|VOIP_INFORMATION|DATABASE_INFORMATION|PHONE_INFORMATION|INTER_APP_COMMUNICATION";
//sink categories that should be considered by the analysis (all the corresponding methods in the InstrumentationOptions.sinkFile are considered)
public final String sinkCategories = "LOG|NETWORK|SMS_MMS|VIDEO|INTER_APP_COMMUNICATION";
//absolute path to the file that contains source information
public String sourceFile;
//absolute path to the file that contains sink information
public String sinkFile;
//root-folder for different Android sdks (e.g., /AndroidSDK for /AndroidSDK/android-3/android.jar, /AndroidSDK/android-4/android.jar, ...)
public String androidPlatforms;
//the concrete Android jar file (e.g., /AndroidSDK/android-18/android.jar)
public String androidJar;
//the input apkFile
public String apkFile = null;
//this file contains information about the taint-wrapper
public String taintWrapperFile;
public static boolean jimpleOutput = false;
public static String sootOutput = null;
public static String pdpClass = null;
//initialize soot
public void initialiseSoot(){
if(apkFile == null)
throw new RuntimeException("There has to be an apk for the analysis");
Options.v().set_allow_phantom_refs(true);
Options.v().set_validate(true);
Options.v().set_prepend_classpath(true);
//no cfg needed
Options.v().set_whole_program(false);
//input format
Options.v().set_src_prec(Options.src_prec_apk);
//the output is an unsigned apk file
Options.v().set_output_format(Options.output_format_dex);
Options.v().set_process_dir(Collections.singletonList(apkFile));
if (instance.androidJar != null)
Options.v().set_force_android_jar(instance.androidJar);
else if (instance.androidPlatforms != null)
Options.v().set_android_jars(instance.androidPlatforms);
else
throw new RuntimeException("Neither single Android JAR not platform "
+ "directory specified");
//the bin folder has to be added to the classpath in order to
//use the Java part for the instrumentation (JavaClassForInstrumentation)
Options.v().set_soot_classpath(instance.bin
+ File.pathSeparator + instance.androidJar);
initialiseInstrumentationClasses();
Scene.v().loadNecessaryClasses();
addInstrumentedClassToApplicationClass();
}
private String getCodeLocation() {
try {
String path = Settings.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
System.out.println("Code loaded from " + path);
return path;
} catch (URISyntaxException e) {
return "./bin/";
}
}
/**
* Add all java-classes to the basic classes with the BODIES settings, since they will be
* set as application class in addInstrumentedClassToApplicationClass()
*/
public void initialiseInstrumentationClasses() {
for (String class2Add: class2AddList) {
Scene.v().addBasicClass(class2Add, SootClass.BODIES);
}
}
/**
* Set java-classes to application class in order to be added to the apk.
*/
public void addInstrumentedClassToApplicationClass(){
for (String class2Add: class2AddList) {
Scene.v().getSootClass(class2Add).setApplicationClass();
}
}
/**
* Get the path of the apk to analyze.
* @return: apk path
*/
public String getApkPath(){
return apkFile;
}
//prints out the different options for the instrumentation
public void printHelp(){
StringBuilder output = new StringBuilder();
output.append("\nUsage: de.ecspride.Main\n"
+ "-sourceFile </path/to/CategorizesSouceList> \n"
+ "-sinkFile </path/to/CategorizedSinkList> \n"
+ "-apkFile </path/to/file.apk> \n"
+ "-androidPlatforms </path/to/root/folder/of/jars> (e.g., AndroidSDK for /AndroidSDK/android-3/android.jar, /AndroidSDK/android-4/android.jar, ...)\n"
+ "-androidJar </path/to/android.jar>\n"
/* + "-instrumentationType [hybrid|complete]\n" */
+ "-taintWrapper </path/to/taintWrapper.txt>\n"
+ "-eventInformationFile </path/to/taintWrapper.txt>\n"
+ "-j enable Jimple output\n"
+ "-o output directory\n"
+ "-pdp a.b.c:a.b.c.PDPClass\n"
+ "\n"
+ "\n"
+ "Debug: to enable debug/warning/... output level define slf4j parameters such as: \n"
+ " -Dorg.slf4j.simpleLogger.defaultLogLevel=DEBUG\n"
+ "See http://www.slf4j.org/api/org/apache/log4j/Level.html for the available levels."
+"\n"
);
System.out.println(output.toString());
}
//parses the user's command line input and saves it into the corresponding data structure
public void parseCommandLineArgs(String[] args){
for(int i = 0; i < args.length; i++){
if(args[i].equals("-help")){
printHelp();
System.exit(0);
}
else if(args[i].equals("-androidPlatforms")){
++i;
correctArgumentCheck(args[i]);
androidPlatforms = args[i];
}
else if(args[i].equals("-androidJar")){
++i;
correctArgumentCheck(args[i]);
androidJar = args[i];
}
else if(args[i].equals("-sourceFile")){
++i;
correctArgumentCheck(args[i]);
sourceFile = args[i];
}
else if(args[i].equals("-sinkFile")){
++i;
correctArgumentCheck(args[i]);
sinkFile = args[i];
}
else if(args[i].equals("-taintWrapper")){
++i;
correctArgumentCheck(args[i]);
taintWrapperFile = args[i];
}
else if(args[i].equals("-apkFile")){
++i;
correctArgumentCheck(args[i]);
apkFile = args[i];
} else if (args[i].equals("-j")) {
jimpleOutput = true;
log.info("Jimple output enabled.");
} else if (args[i].equals("-o")) {
sootOutput = args[++i];
File f = new File(sootOutput);
if (!f.exists())
f.mkdirs();
log.info("Soot output: "+ sootOutput);
} else if (args[i].equals("-pdp")) {
pdpClass = args[++i];
log.info("PDP target class: "+ pdpClass);
} else if (args[i].equals("-eventInformationFile")) {
eventInformationFile = args[++i];
log.info("Event information file from: "+ eventInformationFile);
} else {
System.err.println("unknown option '"+ args[i] +"'");
printHelp();
System.exit(0);
}
}
//check for correct settings
if(sourceFile == null || sinkFile == null || apkFile == null){
System.err.println("Error: command line arguments are not correct!"
+ "\nInstrumentation stopped...");
printHelp();
System.exit(-1);
}
if (sootOutput == null) {
System.err.println("Error: no output directory specified.");
printHelp();
System.exit(-1);
}
if (pdpClass != null && !pdpClass.contains(":")) {
System.err.println("Error: wrong format for option -pdp '"+ pdpClass +"'");
printHelp();
System.exit(-1);
}
}
//simple checks whether the option starts with a "-" or not
private void correctArgumentCheck(String argument){
if(argument.startsWith("-"))
throw new RuntimeException("Ooops, wrong command line argument: " + argument);
}
//reset all Settings
public void reset(){
instance = new Settings();
}
public static boolean mustOutputJimple() {
return jimpleOutput;
}
/**
* The dummy main class is from the FlowDroid project which basically emulates the Android operating system with its
* lifecycle. This is an external class which is used during the analysis, but is not needed to be included in the
* output file. Therefore, it is set to "Library Class".
*/
public void setDummyMainToLibraryClass(){
Scene.v().getSootClass("dummyMainClass").setLibraryClass();
}
}