package de.ecspride.pep;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import soot.ArrayType;
import soot.Body;
import soot.BooleanType;
import soot.ByteType;
import soot.CharType;
import soot.DoubleType;
import soot.FloatType;
import soot.IdentityUnit;
import soot.IntType;
import soot.Local;
import soot.LongType;
import soot.PrimType;
import soot.RefType;
import soot.Scene;
import soot.ShortType;
import soot.SootClass;
import soot.SootField;
import soot.SootMethod;
import soot.Type;
import soot.Unit;
import soot.Value;
import soot.javaToJimple.LocalGenerator;
import soot.jimple.ArrayRef;
import soot.jimple.AssignStmt;
import soot.jimple.DefinitionStmt;
import soot.jimple.IdentityStmt;
import soot.jimple.IfStmt;
import soot.jimple.IntConstant;
import soot.jimple.InterfaceInvokeExpr;
import soot.jimple.InvokeExpr;
import soot.jimple.InvokeStmt;
import soot.jimple.Jimple;
import soot.jimple.NewArrayExpr;
import soot.jimple.NewExpr;
import soot.jimple.NopStmt;
import soot.jimple.NullConstant;
import soot.jimple.ParameterRef;
import soot.jimple.SpecialInvokeExpr;
import soot.jimple.StaticInvokeExpr;
import soot.jimple.Stmt;
import soot.jimple.StringConstant;
import soot.jimple.infoflow.android.data.AndroidMethod;
import soot.jimple.infoflow.android.data.AndroidMethod.CATEGORY;
import soot.jimple.infoflow.entryPointCreators.AndroidEntryPointCreator;
import soot.jimple.infoflow.handlers.ResultsAvailableHandler;
import soot.jimple.infoflow.results.InfoflowResults;
import soot.jimple.infoflow.results.ResultSinkInfo;
import soot.jimple.infoflow.results.ResultSourceInfo;
import soot.jimple.infoflow.solver.cfg.IInfoflowCFG;
import soot.jimple.infoflow.source.data.SourceSinkDefinition;
import soot.jimple.toolkits.ide.icfg.BiDiInterproceduralCFG;
import soot.util.MultiMap;
import de.ecspride.Main;
import de.ecspride.Settings;
import de.ecspride.events.EventInformation;
import de.ecspride.events.Pair;
import de.ecspride.instrumentation.Instrumentation;
import de.ecspride.util.UpdateManifestAndCodeForWaitPDP;
import de.ecspride.util.Util;
/**
* Responsible for instrumenting statements which will trigger the PDP and waits for a
* response from it. The response could be either: statement execution is allowed or
* statement execution is not allowed.
* @author Siegfried Rasthofer
*/
public class PolicyEnforcementPoint implements ResultsAvailableHandler{
private static Logger log = LoggerFactory.getLogger(PolicyEnforcementPoint.class);
/**
* key: method-signature indicating the statement which needs some instrumentation
* value: event-information about the event which will be triggered at the method-signature
*/
private final Map<String, EventInformation> allEventInformation;
private final Set<SourceSinkDefinition> sources;
private final Set<SourceSinkDefinition> sinks;
private final AndroidEntryPointCreator entryPointCreator;
private int sourceSinkConnectionCounter = 0;
private InfoflowResults results = null;
private final String unknownCategory = "UNKNOWN_BUNDLE_DATA";
public PolicyEnforcementPoint(Map<String, EventInformation> eventInformation,
Set<SourceSinkDefinition> sources,
Set<SourceSinkDefinition> sinks,
AndroidEntryPointCreator entryPointCreator){
this.allEventInformation = eventInformation;
this.sources = sources;
this.sinks = sinks;
this.entryPointCreator = entryPointCreator;
}
@Override
public void onResultsAvailable(IInfoflowCFG cfg, InfoflowResults results) {
log.info("FlowDroid has finished. Duration: " + (System.currentTimeMillis() - Main.startTime) +" ms.");
Main.startTime = System.currentTimeMillis();
Settings.instance.setDummyMainToLibraryClass();
this.results = results;
if (log.isDebugEnabled()) {
log.debug("");
log.debug("InfoFlow Results");
MultiMap<ResultSinkInfo, ResultSourceInfo> r = results.getResults();
for (ResultSinkInfo k : r.keySet()) {
log.debug("ResultSinkInfo: "+ k);
for (ResultSourceInfo rsi: r.get(k)) {
log.debug(" source: "+ rsi);
}
}
log.debug("");
}
log.info("Starting bytecode instrumentation.");
log.info("Adding code to initialize PEPs.");
Util.initializePePInAllPossibleClasses(Settings.instance.getApkPath());
log.info("Build code for new 'WaitPDPActivity"); // building the code has to be done here (not in the Main class, otherwise Jimple validation will fail
String mainActivityClass = UpdateManifestAndCodeForWaitPDP.getMainActivityName(Settings.instance.getApkPath());
String packageName = UpdateManifestAndCodeForWaitPDP.getApplicationPackageName(Settings.instance.getApkPath());
UpdateManifestAndCodeForWaitPDP.updateWaitPDPActivity(packageName, mainActivityClass);
// update packagename in field of WaitPDP class
SootClass sc = Scene.v().getSootClass(Settings.INSTRUMENTATION_HELPER_JAVA);
SootField sf1 = sc.getFieldByName("applicationPackageName");
Util.changeConstantStringInField(sf1, packageName);
log.info("Adding Policy Enforcement Points (PEPs).");
doAccessControlChecks(cfg);
log.info("Instrumentation is done.");
if (Settings.mustOutputJimple()) {
log.info("-------- Dumping Jimple bodies.");
Main.dumpJimple();
log.info("--------");
}
}
/**
* For a concrete method (declared in an application class), find statements
* containing an invoke expression for which the method is one of the method
* in 'allEventInformation' (i.e., getLine1Number(), ...).
*
* @param cfg
*/
private void doAccessControlChecks(BiDiInterproceduralCFG<Unit, SootMethod> cfg){
for(SootClass sc : Scene.v().getApplicationClasses()){
for(SootMethod sm : sc.getMethods()){
if(sm.isConcrete()){
Body body = sm.retrieveActiveBody();
// only instrument application methods (i.e., not methods declared in PEP helper classes
// or in a Java library classes or in an Android classes, ...)
if(!isInstrumentationNecessary(sm)) {
continue;
}
// important to use snapshotIterator here
Iterator<Unit> i = body.getUnits().snapshotIterator();
log.debug("method: "+ sm);
while(i.hasNext()){
Stmt s = (Stmt) i.next();
// only instrument invoke expressions
if (!s.containsInvokeExpr()) {
continue;
}
InvokeExpr invExpr = s.getInvokeExpr();
String methodSignature = invExpr.getMethod().getSignature();
if(!allEventInformation.containsKey(methodSignature)) {
continue;
}
log.debug("statement "+ s +" matches "+ methodSignature +".");
ResultSinkInfo sink = null;
outer:
for (ResultSinkInfo key : results.getResults().keySet()) {
// iterate over all the arguments of the invoke expression
// and check if an argument is a tainted sink. If one is
// set variable 'sink' to the ResultSinkInfo key.
for (Value v : invExpr.getArgs()) {
Value pathValue = key.getAccessPath().getPlainValue();
if (v == pathValue) {
sink = key;
log.debug("found a sink: "+ pathValue);
break outer;
}
}
}
if(sink != null){
log.debug("instrument with data flow information )" + s + ")");
instrumentSourceToSinkConnections(cfg, sink, s instanceof AssignStmt);
instrumentWithNoDataFlowInformation(methodSignature, s, invExpr, body, s instanceof AssignStmt);
} else {
log.debug("instrument without data flow information (" + s + ")");
instrumentWithNoDataFlowInformation(methodSignature, s, invExpr, body, s instanceof AssignStmt);
}
} // loop on statements
}
}
}
}
private List<Unit> instrumentIntentAddings(BiDiInterproceduralCFG<Unit, SootMethod> cfg,
Unit unit, InvokeExpr sinkExpr, Set<ResultSourceInfo> sourceInfo){
if(isMethodInterComponentSink(sinkExpr.getMethod())){
SootMethod method = cfg.getMethodOf(unit);
Body body = null;
if(method.hasActiveBody())
body = method.retrieveActiveBody();
else
throw new RuntimeException("No body found!");
Set<String> sourceCategories = getDataIdList(sourceInfo);
final String hashSetType = "java.util.HashSet";
List<Unit> generated = new ArrayList<Unit>();
//HashSet initialization
Local hashSetLocal = generateFreshLocal(body, RefType.v(hashSetType));
NewExpr newExpr = Jimple.v().newNewExpr(RefType.v(hashSetType));
AssignStmt assignStmt = Jimple.v().newAssignStmt(hashSetLocal, newExpr);
generated.add(assignStmt);
//constructor call
SpecialInvokeExpr constructorCall = Jimple.v().newSpecialInvokeExpr(hashSetLocal, Scene.v().getMethod("<java.util.HashSet: void <init>()>").makeRef());
InvokeStmt constructorCallStmt = Jimple.v().newInvokeStmt(constructorCall);
generated.add(constructorCallStmt);
//add categories to HashSet
for(String cat : sourceCategories){
InterfaceInvokeExpr addCall = Jimple.v().newInterfaceInvokeExpr(hashSetLocal, Scene.v().getMethod("<java.util.Set: boolean add(java.lang.Object)>").makeRef(), StringConstant.v(cat));
InvokeStmt addCallStmt = Jimple.v().newInvokeStmt(addCall);
generated.add(addCallStmt);
}
//get Intent
Value intent = sinkExpr.getArg(0);
List<Object> args = new ArrayList<Object>();
args.add(RefType.v("android.content.Intent"));
args.add(intent);
args.add(RefType.v(hashSetType));
args.add(hashSetLocal);
StaticInvokeExpr sie = Instrumentation.createJimpleStaticInvokeExpr(
Settings.INSTRUMENTATION_HELPER_JAVA,
"addTaintInformationToIntent",
args);
InvokeStmt invStmt = Jimple.v().newInvokeStmt(sie);
generated.add(invStmt);
return generated;
}
return Collections.emptyList();
}
/**
*
* @param cfg
* @param sink
* @param assignmentStatement
*/
private void instrumentSourceToSinkConnections(BiDiInterproceduralCFG<Unit, SootMethod> cfg, ResultSinkInfo sink, boolean assignmentStatement){
sourceSinkConnectionCounter += 1;
// loop through the sinks
for (ResultSinkInfo key : results.getResults().keySet()) {
log.debug("compare: "+ key);
log.debug(" to: "+ sink);
// if the current sink is the sink at the unit tagged with 'sink'
if(key.equals(sink)){
// loop through the sources
for(ResultSourceInfo si : results.getResults().get(key)){
Stmt stmt = si.getSource();
SootMethod sm = cfg.getMethodOf(stmt);
Body body = sm.retrieveActiveBody();
// Source instrumentation. The three type categories for the source are:
// - callback
// - ICC source method (i.e., Intent.getExtras())
// - not a callback and not an ICC source method (i.e., getLine1Number())
//
if (isInterComponentSourceCallback(si, cfg)) {
throw new RuntimeException("Callbacks as sources are not supported right now");
} else if (isInterComponentSourceNoCallback(si, cfg)) {
//only invoke expression are treated here
if (stmt.containsInvokeExpr()) {
//only statements that return a android.os.Bundle are currently supported
if (stmt instanceof DefinitionStmt) {
DefinitionStmt defStmt = (DefinitionStmt)stmt;
Value leftValue = defStmt.getLeftOp();
if (leftValue.getType().equals(RefType.v("android.os.Bundle"))) {
List<Object> args = new ArrayList<Object>();
args.add(IntType.v());
args.add(IntConstant.v(sourceSinkConnectionCounter));
args.add(RefType.v("android.os.Bundle"));
args.add(leftValue);
InvokeExpr invExpr = Instrumentation.createJimpleStaticInvokeExpr(
Settings.INSTRUMENTATION_HELPER_JAVA,
"registerNewSourceSinkConnection",
args);
InvokeStmt invStmt = Jimple.v().newInvokeStmt(invExpr);
Unit instrumentationPoint = null;
if (stmt instanceof IdentityStmt) {
instrumentationPoint = getLastIdentityStmt(body);
} else {
instrumentationPoint = stmt;
}
body.getUnits().insertAfter(invStmt, instrumentationPoint);
log.debug("insert a: "+ invStmt);
} else {
System.err.println("We do only support android.os.Bundle right now!");
}
}
}
} else {
String sourceCat = getSourceCategory(si);
if(sourceCat != null){
List<Object> args = new ArrayList<Object>();
args.add(IntType.v());
args.add(IntConstant.v(sourceSinkConnectionCounter));
args.add(RefType.v("java.lang.String"));
args.add(StringConstant.v(sourceCat));
InvokeExpr invExpr = Instrumentation.createJimpleStaticInvokeExpr(
Settings.INSTRUMENTATION_HELPER_JAVA,
"registerNewSourceSinkConnection",
args);
InvokeStmt invStmt = Jimple.v().newInvokeStmt(invExpr);
Unit instrumentationPoint = null;
if(stmt instanceof IdentityStmt)
instrumentationPoint = getLastIdentityStmt(body);
else
instrumentationPoint = stmt;
body.getUnits().insertAfter(invStmt, instrumentationPoint);
log.debug("insert b: "+ invStmt);
}
}
// sink instrumentation
if(sink.getSink().containsInvokeExpr()){
Body bodyOfSink = cfg.getMethodOf(key.getSink()).getActiveBody();
InvokeExpr invExpr = sink.getSink().getInvokeExpr();
List<Unit> generated = new ArrayList<Unit>();
generated.addAll(instrumentIntentAddings(cfg, stmt, invExpr, results.getResults().get(key)));
EventInformation sinkEventInfo = allEventInformation.get(invExpr.getMethod().getSignature());
// TODO: write code to handle cases where Intent is given as a parameter to a component method
// such as Service.onStartCommand(Intent i, ...)
if (!si.getSource().containsInvokeExpr()) {
System.out.println("WARNING: skipping source with no invoke expression: "+ si.getSource());
continue; // next source
}
EventInformation sourceEventInfo = allEventInformation.get(si.getSource().getInvokeExpr().getMethod().getSignature());
generated.addAll(generatePolicyEnforcementPoint(key.getSink(), invExpr,
bodyOfSink, sourceSinkConnectionCounter, assignmentStatement));
log.debug("body with data flow:\n"+body);
for (Unit u: generated) {
log.debug("gen: "+ u);
}
if(sinkEventInfo.isInstrumentAfterStatement())
bodyOfSink.getUnits().insertAfter(generated, key.getSink());
else
bodyOfSink.getUnits().insertBefore(generated, key.getSink());
}
else
throw new RuntimeException("Double-Check the assumption");
} // loop through the sources
} // if the sink at the unit is the current sink
} // loop through the sinks
}
/**
* Add Policy Enforcement Point (PEP) for Unit 'unit'.
* @param methodSignature
* @param unit
* @param invExpr
* @param body
* @param assignmentStatement
*/
private void instrumentWithNoDataFlowInformation(String methodSignature, Unit unit, InvokeExpr invExpr, Body body, boolean assignmentStatement){
log.debug("add PEP without dataflow information for unit "+ unit);
EventInformation eventInfo = allEventInformation.get(methodSignature);
List<Unit> generated = generatePolicyEnforcementPoint(unit, invExpr, body, -1, assignmentStatement);
log.debug("body no data flow:\n"+body);
for (Unit u: generated) {
log.debug("gen: "+ u);
}
if(eventInfo.isInstrumentAfterStatement()) {
body.getUnits().insertAfter(generated, unit);
} else {
body.getUnits().insertBefore(generated, unit);
}
}
/**
* Generate Policy Enforcement Point (PEP) for Unit 'unit'.
* @param unit
* @param invExpr
* @param body
* @param dataFlowAvailable
* @param assignmentStatement
* @return
*/
private List<Unit> generatePolicyEnforcementPoint(Unit unit, InvokeExpr invExpr, Body body, int dataFlowAvailable, boolean assignmentStatement){
log.debug("Dataflow available: "+ dataFlowAvailable);
List<Unit> generated = new ArrayList<Unit>(); // store all new units that are generated
String methodSignature = invExpr.getMethod().getSignature();
EventInformation eventInfo = allEventInformation.get(methodSignature);
String eventName = eventInfo.getEventName();
Set<Pair<Integer, String>> allParameterInformation = eventInfo.getParameterInformation();
// This list containts types and parameters that are used to build the
// invoke expression to "isStmtExecutionAllowed':
//
// java.lang.String "eventName"
// IntType "dataFlowAvailable"
// java.lang.Object[] "parameters"
//
List<Object> parameterForHelperMethod = new ArrayList<Object>();
List<Object> categories = new ArrayList<Object>();
// add event name information
Type eventNameType = RefType.v("java.lang.String");
parameterForHelperMethod.add(eventNameType);
StringConstant eventNameConstant = StringConstant.v(eventName);
parameterForHelperMethod.add(eventNameConstant);
// add information about dataflow availability
parameterForHelperMethod.add(IntType.v());
parameterForHelperMethod.add(IntConstant.v(dataFlowAvailable));
// add information about parameters
parameterForHelperMethod.add(getParameterArrayType());
List<Value> paramValues = new ArrayList<Value>();
for(Pair<Integer, String> parameterInfo : allParameterInformation){
paramValues.add(StringConstant.v("param" + parameterInfo.getLeft() + "value"));
paramValues.add(invExpr.getArg(parameterInfo.getLeft()));
}
Pair<Value, List<Unit>> arrayRefAndInstrumentation = generateParameterArray(paramValues, body);
generated.addAll(arrayRefAndInstrumentation.getRight());
parameterForHelperMethod.add(arrayRefAndInstrumentation.getLeft());
// Generate PEP call to the PDP. Store the result send by the PDP to 'resultPDPLocal'
// Pseudo code looks like this:
//
// resultPDPLocal = isStmtExecutionAllowed(eventName, dataFlowAvailable, parameters);
//
StaticInvokeExpr sie = Instrumentation.createJimpleStaticInvokeExpr(
Settings.INSTRUMENTATION_HELPER_JAVA,
"isStmtExecutionAllowed",
parameterForHelperMethod
);
Local resultPDPLocal = generateFreshLocal(body, soot.IntType.v());
AssignStmt asssCondition = Jimple.v().newAssignStmt(resultPDPLocal, sie);
generated.add(asssCondition);
for (Unit u: generated) {
System.out.println("isStmt gen: "+ u);
}
if(assignmentStatement){
// If the method call before which the PEP in inserted is an assignment statement of
// the form "resultPDPLocal = originalCallThatIsChecked()", generate a new assignment
// statement that stores a default value to "resultPDPLocal" if the PDP does not
// allow the call of method originalCallThatIsChecked().
//
// Pseudo-code:
//
// if(resultPDPLocal == 0) goto dummyLabel:
// result = originalCallThatIsChecked();
// goto dummyLabel2:
// dummyLabel:
// result = dummyValue (i.e., 0 for IntType, false for BooleanType, ...)
// dummyLabel2:
// nop
//
if(unit instanceof DefinitionStmt){
DefinitionStmt defStmt = (DefinitionStmt)unit;
Value pepCondition = Jimple.v().newEqExpr(resultPDPLocal, IntConstant.v(0));
// insert nop
Unit label2Nop = Jimple.v().newNopStmt();
body.getUnits().insertAfter(label2Nop, unit);
// insert result = dummyValue
Unit dummyStatement = createCorrectDummyAssignment((Local)defStmt.getLeftOp());
body.getUnits().insertAfter(dummyStatement, unit);
log.debug("insert c: "+ dummyStatement);
// insert goto dummyLabel2:
body.getUnits().insertAfter(Jimple.v().newGotoStmt(label2Nop), unit);
IfStmt ifStmt = Jimple.v().newIfStmt(pepCondition, dummyStatement);
generated.add(ifStmt);
} else {
throw new RuntimeException("error: expected DefinitionStmt got "+ unit +" -> "+ unit.getClass());
}
} else {
// If the method call before which the PEP in inserted is a call statement of
// the form "originalCallThatIsChecked()", generate a new nop statement
// to jump to if the PDP does not allow the call of method originalCallThatIsChecked().
//
// Pseudo-code:
//
// if(resultPDPLocal == 0) goto nopLabel:
// result = originalCallThatIsChecked();
// nopLabel:
// nop
//
Value pepCondition = Jimple.v().newEqExpr(resultPDPLocal, IntConstant.v(0));
NopStmt nopStmt = Jimple.v().newNopStmt();
body.getUnits().insertAfter(nopStmt, unit);
log.debug("insert d: "+ nopStmt);
IfStmt ifStmt = Jimple.v().newIfStmt(pepCondition, nopStmt);
generated.add(ifStmt);
}
return generated;
}
/**
*
* @param parameter
* @param body
* @return
*/
private Pair<Value, List<Unit>> generateParameterArray(List<Value> parameter, Body body){
List<Unit> generated = new ArrayList<Unit>();
NewArrayExpr arrayExpr = Jimple.v().newNewArrayExpr(RefType.v("java.lang.Object"), IntConstant.v(parameter.size()));
Value newArrayLocal = generateFreshLocal(body, getParameterArrayType());
Unit newAssignStmt = Jimple.v().newAssignStmt(newArrayLocal, arrayExpr);
generated.add(newAssignStmt);
for(int i = 0; i < parameter.size(); i++){
Value index = IntConstant.v(i);
ArrayRef leftSide = Jimple.v().newArrayRef(newArrayLocal, index);
Value rightSide = generateCorrectObject(body, parameter.get(i), generated);
Unit parameterInArray = Jimple.v().newAssignStmt(leftSide, rightSide);
generated.add(parameterInArray);
}
return new Pair<Value, List<Unit>>(newArrayLocal, generated);
}
private Type getParameterArrayType(){
Type parameterArrayType = RefType.v("java.lang.Object");
Type parameterArray = ArrayType.v(parameterArrayType, 1);
return parameterArray;
}
private Value generateCorrectObject(Body body, Value value, List<Unit> generated){
if(value.getType() instanceof PrimType){
//in case of a primitive type, we use boxing (I know it is not nice, but it works...) in order to use the Object type
if(value.getType() instanceof BooleanType){
Local booleanLocal = generateFreshLocal(body, RefType.v("java.lang.Boolean"));
SootClass sootClass = Scene.v().getSootClass("java.lang.Boolean");
SootMethod valueOfMethod = sootClass.getMethod("java.lang.Boolean valueOf(boolean)");
StaticInvokeExpr staticInvokeExpr = Jimple.v().newStaticInvokeExpr(valueOfMethod.makeRef(), value);
Unit newAssignStmt = Jimple.v().newAssignStmt(booleanLocal, staticInvokeExpr);
generated.add(newAssignStmt);
return booleanLocal;
}
else if(value.getType() instanceof ByteType){
Local byteLocal = generateFreshLocal(body, RefType.v("java.lang.Byte"));
SootClass sootClass = Scene.v().getSootClass("java.lang.Byte");
SootMethod valueOfMethod = sootClass.getMethod("java.lang.Byte valueOf(byte)");
StaticInvokeExpr staticInvokeExpr = Jimple.v().newStaticInvokeExpr(valueOfMethod.makeRef(), value);
Unit newAssignStmt = Jimple.v().newAssignStmt(byteLocal, staticInvokeExpr);
generated.add(newAssignStmt);
return byteLocal;
}
else if(value.getType() instanceof CharType){
Local characterLocal = generateFreshLocal(body, RefType.v("java.lang.Character"));
SootClass sootClass = Scene.v().getSootClass("java.lang.Character");
SootMethod valueOfMethod = sootClass.getMethod("java.lang.Character valueOf(char)");
StaticInvokeExpr staticInvokeExpr = Jimple.v().newStaticInvokeExpr(valueOfMethod.makeRef(), value);
Unit newAssignStmt = Jimple.v().newAssignStmt(characterLocal, staticInvokeExpr);
generated.add(newAssignStmt);
return characterLocal;
}
else if(value.getType() instanceof DoubleType){
Local doubleLocal = generateFreshLocal(body, RefType.v("java.lang.Double"));
SootClass sootClass = Scene.v().getSootClass("java.lang.Double");
SootMethod valueOfMethod = sootClass.getMethod("java.lang.Double valueOf(double)");
StaticInvokeExpr staticInvokeExpr = Jimple.v().newStaticInvokeExpr(valueOfMethod.makeRef(), value);
Unit newAssignStmt = Jimple.v().newAssignStmt(doubleLocal, staticInvokeExpr);
generated.add(newAssignStmt);
return doubleLocal;
}
else if(value.getType() instanceof FloatType){
Local floatLocal = generateFreshLocal(body, RefType.v("java.lang.Float"));
SootClass sootClass = Scene.v().getSootClass("java.lang.Float");
SootMethod valueOfMethod = sootClass.getMethod("java.lang.Float valueOf(float)");
StaticInvokeExpr staticInvokeExpr = Jimple.v().newStaticInvokeExpr(valueOfMethod.makeRef(), value);
Unit newAssignStmt = Jimple.v().newAssignStmt(floatLocal, staticInvokeExpr);
generated.add(newAssignStmt);
return floatLocal;
}
else if(value.getType() instanceof IntType){
Local integerLocal = generateFreshLocal(body, RefType.v("java.lang.Integer"));
SootClass sootClass = Scene.v().getSootClass("java.lang.Integer");
SootMethod valueOfMethod = sootClass.getMethod("java.lang.Integer valueOf(int)");
StaticInvokeExpr staticInvokeExpr = Jimple.v().newStaticInvokeExpr(valueOfMethod.makeRef(), value);
Unit newAssignStmt = Jimple.v().newAssignStmt(integerLocal, staticInvokeExpr);
generated.add(newAssignStmt);
return integerLocal;
}
else if(value.getType() instanceof LongType){
Local longLocal = generateFreshLocal(body, RefType.v("java.lang.Long"));
SootClass sootClass = Scene.v().getSootClass("java.lang.Long");
SootMethod valueOfMethod = sootClass.getMethod("java.lang.Long valueOf(long)");
StaticInvokeExpr staticInvokeExpr = Jimple.v().newStaticInvokeExpr(valueOfMethod.makeRef(), value);
Unit newAssignStmt = Jimple.v().newAssignStmt(longLocal, staticInvokeExpr);
generated.add(newAssignStmt);
return longLocal;
}
else if(value.getType() instanceof ShortType){
Local shortLocal = generateFreshLocal(body, RefType.v("java.lang.Short"));
SootClass sootClass = Scene.v().getSootClass("java.lang.Short");
SootMethod valueOfMethod = sootClass.getMethod("java.lang.Short valueOf(short)");
StaticInvokeExpr staticInvokeExpr = Jimple.v().newStaticInvokeExpr(valueOfMethod.makeRef(), value);
Unit newAssignStmt = Jimple.v().newAssignStmt(shortLocal, staticInvokeExpr);
generated.add(newAssignStmt);
return shortLocal;
}
else
throw new RuntimeException("Ooops, something went all wonky!");
}
else
//just return the value, there is nothing to box
return value;
}
private Local generateFreshLocal(Body b, Type type){
LocalGenerator lg = new LocalGenerator(b);
return lg.generateLocal(type);
}
private boolean isInstrumentationNecessary(SootMethod method){
if (method.getDeclaringClass().isJavaLibraryClass())
return false;
if (method.getDeclaringClass().toString().startsWith("android."))
return false;
for (String cn: Settings.class2AddList) {
if (method.getDeclaringClass().toString().startsWith(cn))
return false;
}
return true;
}
/**
* This method iterates over all sources from the FlowDroid-results and extracts the
* category of the specific source. If there is no category found, it will return an empty set,
* otherwise the correct categories will be added.
* @param sourcesInfo: all possible sources from which we try to identify the category
* @return: set of categories for specific sink
*/
private Set<String> getDataIdList(Set<ResultSourceInfo> sourcesInfo){
Set<String> dataIdList = new HashSet<String>();
for(ResultSourceInfo sInfo : sourcesInfo){
if(sInfo.getSource().containsInvokeExpr()){
InvokeExpr invExpr = sInfo.getSource().getInvokeExpr();
for(SourceSinkDefinition meth : sources) {
AndroidMethod am = (AndroidMethod) meth.getMethod();
if(am.getSignature().equals(invExpr.getMethod().getSignature())) {
dataIdList.add(am.getCategory().toString());
}
}
}
else if (isSourceInfoParameter(sInfo)){
dataIdList.add(unknownCategory);
}
else
throw new RuntimeException("Currently not supported");
}
return dataIdList;
}
private boolean isSourceInfoParameter(ResultSourceInfo sInfo) {
return sInfo.getSource() instanceof IdentityStmt
&& ((IdentityStmt) sInfo.getSource()).getRightOp() instanceof ParameterRef;
}
private String getSourceCategory(ResultSourceInfo sourceInfo){
if(sourceInfo.getSource().containsInvokeExpr()){
InvokeExpr invExpr = sourceInfo.getSource().getInvokeExpr();
for(SourceSinkDefinition meth : sources) {
AndroidMethod am = (AndroidMethod) meth.getMethod();
if(am.getSignature().equals(invExpr.getMethod().getSignature())){
return am.getCategory().toString();
}
}
}
else if(isSourceInfoParameter(sourceInfo)){
return unknownCategory;
}
else
throw new RuntimeException("Currently not supported");
return null;
}
private boolean isMethodInterComponentSink(SootMethod sm) {
for (SourceSinkDefinition meth : sinks) {
AndroidMethod am = (AndroidMethod) meth.getMethod();
if(am.getCategory() == CATEGORY.INTER_APP_COMMUNICATION){
if(am.getSubSignature().equals(sm.getSubSignature()))
return true;
}
}
return false;
}
/**
* Return true if the method corresponding to the source 'si' is an
* Inter Component Communication source method such as "Intent.getExtras()".
* @param si
* @param cfg
* @return
*/
private boolean isInterComponentSourceNoCallback(ResultSourceInfo si, BiDiInterproceduralCFG<Unit, SootMethod> cfg){
if(!si.getSource().containsInvokeExpr())
return false;
InvokeExpr invExpr = si.getSource().getInvokeExpr();
SootMethod sm = invExpr.getMethod();
for(SourceSinkDefinition meth : sources){
AndroidMethod am = (AndroidMethod) meth.getMethod();
if(am.getCategory() == CATEGORY.INTER_APP_COMMUNICATION){
if(am.getSubSignature().equals(sm.getSubSignature())) {
log.info("source is: "+ am);
return true;
}
}
}
return false;
}
private boolean isInterComponentSourceCallback(ResultSourceInfo si,
BiDiInterproceduralCFG<Unit, SootMethod> cfg){
if(isSourceInfoParameter(si)){
SootMethod sm = cfg.getMethodOf(si.getSource());
if(entryPointCreator.getCallbackFunctions().containsKey(sm.getDeclaringClass())){
if(entryPointCreator.getCallbackFunctions().get(sm.getDeclaringClass()).contains(sm.getSignature()))
return true;
}
}
return false;
}
/**
* Generate a default assignment with 'local' as left-hand-side value.
* @param local
* @return
*/
private Unit createCorrectDummyAssignment(Local local){
Unit dummyAssignemnt = null;
if(local.getType() instanceof PrimType){
if(local.getType() instanceof IntType){
StaticInvokeExpr sie = Instrumentation.createJimpleStaticInvokeExpr(
Settings.SANITIZER, "dummyInteger", null);
dummyAssignemnt = Jimple.v().newAssignStmt(local, sie);
}
else if(local.getType() instanceof BooleanType){
StaticInvokeExpr sie = Instrumentation.createJimpleStaticInvokeExpr(
Settings.SANITIZER, "dummyBoolean", null);
dummyAssignemnt = Jimple.v().newAssignStmt(local, sie);
}
else if(local.getType() instanceof ByteType){
StaticInvokeExpr sie = Instrumentation.createJimpleStaticInvokeExpr(
Settings.SANITIZER, "dummyByte", null);
dummyAssignemnt = Jimple.v().newAssignStmt(local, sie);
}
else if(local.getType() instanceof CharType){
StaticInvokeExpr sie = Instrumentation.createJimpleStaticInvokeExpr(
Settings.SANITIZER, "dummyCharacter", null);
dummyAssignemnt = Jimple.v().newAssignStmt(local, sie);
}
else if(local.getType() instanceof DoubleType){
StaticInvokeExpr sie = Instrumentation.createJimpleStaticInvokeExpr(
Settings.SANITIZER, "dummyDouble", null);
dummyAssignemnt = Jimple.v().newAssignStmt(local, sie);
}
else if(local.getType() instanceof FloatType){
StaticInvokeExpr sie = Instrumentation.createJimpleStaticInvokeExpr(
Settings.SANITIZER, "dummyFloat", null);
dummyAssignemnt = Jimple.v().newAssignStmt(local, sie);
}
else if(local.getType() instanceof LongType){
StaticInvokeExpr sie = Instrumentation.createJimpleStaticInvokeExpr(
Settings.SANITIZER, "dummyLong", null);
dummyAssignemnt = Jimple.v().newAssignStmt(local, sie);
}
else if(local.getType() instanceof ShortType){
StaticInvokeExpr sie = Instrumentation.createJimpleStaticInvokeExpr(
Settings.SANITIZER, "dummyShort", null);
dummyAssignemnt = Jimple.v().newAssignStmt(local, sie);
}
else
throw new RuntimeException("Oops, the primitive type is not correct");
}
else{
if(local.getType().equals(RefType.v("java.lang.String")))
dummyAssignemnt = Jimple.v().newAssignStmt(local, StringConstant.v(""));
else
dummyAssignemnt = Jimple.v().newAssignStmt(local, NullConstant.v());
}
return dummyAssignemnt;
}
private Unit getLastIdentityStmt(Body b) {
Unit u = b.getUnits().getFirst();
while (u instanceof IdentityUnit)
u = b.getUnits().getSuccOf(u);
return b.getUnits().getPredOf(u);
}
}