/*
* Copyright (C) 2015 The Pennsylvania State University and the University of Wisconsin
* Systems and Internet Infrastructure Security Laboratory
*
* Author: Damien Octeau
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package edu.psu.cse.siis.ic3;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
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 org.xmlpull.v1.XmlPullParserException;
import soot.PackManager;
import soot.Scene;
import soot.SootClass;
import soot.SootMethod;
import soot.Transform;
import soot.Value;
import soot.jimple.StaticFieldRef;
import soot.jimple.infoflow.android.data.AndroidMethod;
import soot.jimple.infoflow.android.manifest.ProcessManifest;
import soot.options.Options;
import edu.psu.cse.siis.coal.Analysis;
import edu.psu.cse.siis.coal.AnalysisParameters;
import edu.psu.cse.siis.coal.FatalAnalysisException;
import edu.psu.cse.siis.coal.PropagationSceneTransformer;
import edu.psu.cse.siis.coal.PropagationSceneTransformerFilePrinter;
import edu.psu.cse.siis.coal.SymbolFilter;
import edu.psu.cse.siis.coal.arguments.ArgumentValueManager;
import edu.psu.cse.siis.coal.arguments.MethodReturnValueManager;
import edu.psu.cse.siis.coal.field.transformers.FieldTransformerManager;
import edu.psu.cse.siis.ic3.db.SQLConnection;
import edu.psu.cse.siis.ic3.manifest.ManifestPullParser;
public class Ic3Analysis extends Analysis<Ic3CommandLineArguments> {
private static final String INTENT = "android.content.Intent";
private static final String INTENT_FILTER = "android.content.IntentFilter";
private static final String BUNDLE = "android.os.Bundle";
private static final String COMPONENT_NAME = "android.content.ComponentName";
private static final String ACTIVITY = "android.app.Activity";
private static final String[] frameworkClassesArray = { INTENT, INTENT_FILTER, BUNDLE,
COMPONENT_NAME, ACTIVITY };
protected static final List<String> frameworkClasses = Arrays.asList(frameworkClassesArray);
private final Logger logger = LoggerFactory.getLogger(getClass());
private Ic3Data.Application.Builder ic3Builder;
private Map<String, Ic3Data.Application.Component.Builder> componentNameToBuilderMap;
protected String outputDir;
protected Writer writer;
protected ManifestPullParser detailedManifest;
protected Map<String, Integer> componentToIdMap;
protected SetupApplication setupApplication;
protected String packageName;
@Override
protected void registerFieldTransformerFactories(Ic3CommandLineArguments commandLineArguments) {
Timers.v().totalTimer.start();
FieldTransformerManager.v().registerDefaultFieldTransformerFactories();
}
@Override
protected void registerArgumentValueAnalyses(Ic3CommandLineArguments commandLineArguments) {
ArgumentValueManager.v().registerDefaultArgumentValueAnalyses();
ArgumentValueManager.v().registerArgumentValueAnalysis("classType",
new ClassTypeValueAnalysis());
ArgumentValueManager.v().registerArgumentValueAnalysis("authority",
new AuthorityValueAnalysis());
ArgumentValueManager.v().registerArgumentValueAnalysis("Set<authority>",
new AuthorityValueAnalysis());
ArgumentValueManager.v().registerArgumentValueAnalysis("path", new PathValueAnalysis());
ArgumentValueManager.v().registerArgumentValueAnalysis("Set<path>", new PathValueAnalysis());
}
@Override
protected void registerMethodReturnValueAnalyses(Ic3CommandLineArguments commandLineArguments) {
MethodReturnValueManager.v().registerDefaultMethodReturnValueAnalyses();
}
@Override
protected void initializeAnalysis(Ic3CommandLineArguments commandLineArguments)
throws FatalAnalysisException {
long startTime = System.currentTimeMillis() / 1000;
outputDir = commandLineArguments.getOutput();
prepareManifestFile(commandLineArguments);
if (commandLineArguments.getProtobufDestination() != null) {
ic3Builder = Ic3Data.Application.newBuilder();
ic3Builder.setAnalysisStart(startTime);
if (commandLineArguments.getSample() != null) {
ic3Builder.setSample(commandLineArguments.getSample());
}
componentNameToBuilderMap = detailedManifest.populateProtobuf(ic3Builder);
} else if (commandLineArguments.getDb() != null) {
SQLConnection.init(commandLineArguments.getDbName(), commandLineArguments.getDb(),
commandLineArguments.getSsh(), commandLineArguments.getDbLocalPort());
componentToIdMap = detailedManifest.writeToDb(false);
}
Timers.v().mainGeneration.start();
setupApplication =
new SetupApplication(commandLineArguments.getManifest(), commandLineArguments.getInput(),
commandLineArguments.getClasspath());
Map<String, Set<String>> callBackMethods;
Set<String> entryPointClasses = null;
if (detailedManifest == null) {
ProcessManifest manifest;
try {
manifest = new ProcessManifest(commandLineArguments.getManifest());
entryPointClasses = manifest.getEntryPointClasses();
packageName = manifest.getPackageName();
} catch (IOException | XmlPullParserException e) {
throw new FatalAnalysisException("Could not process manifest file "
+ commandLineArguments.getManifest() + ": " + e);
}
} else {
entryPointClasses = detailedManifest.getEntryPointClasses();
packageName = detailedManifest.getPackageName();
}
try {
callBackMethods =
setupApplication.calculateSourcesSinksEntrypoints(new HashSet<AndroidMethod>(),
new HashSet<AndroidMethod>(), packageName, entryPointClasses);
} catch (IOException e) {
logger.error("Could not calculate entry points", e);
throw new FatalAnalysisException();
}
Timers.v().mainGeneration.end();
Timers.v().misc.start();
// Application package name is now known.
ArgumentValueManager.v().registerArgumentValueAnalysis("context",
new ContextValueAnalysis(packageName));
AndroidMethodReturnValueAnalyses.registerAndroidMethodReturnValueAnalyses(packageName);
if (outputDir != null && packageName != null) {
String outputFile = String.format("%s/%s.csv", outputDir, packageName);
try {
writer = new BufferedWriter(new FileWriter(outputFile, false));
} catch (IOException e1) {
logger.error("Could not open file " + outputFile, e1);
}
}
// reset Soot:
soot.G.reset();
Map<SootMethod, Set<String>> entryPointMap =
commandLineArguments.computeComponents() ? new HashMap<SootMethod, Set<String>>() : null;
addSceneTransformer(entryPointMap);
if (commandLineArguments.computeComponents()) {
addEntryPointMappingSceneTransformer(entryPointClasses, callBackMethods, entryPointMap);
}
Options.v().set_no_bodies_for_excluded(true);
Options.v().set_allow_phantom_refs(true);
Options.v().set_output_format(Options.output_format_none);
Options.v().set_whole_program(true);
Options.v().set_soot_classpath(
commandLineArguments.getInput() + File.pathSeparator + commandLineArguments.getClasspath());
Options.v().set_ignore_resolution_errors(true);
Options.v().set_process_dir(frameworkClasses);
Options.v().setPhaseOption("cg.spark", "on");
// do not merge variables (causes problems with PointsToSets)
Options.v().setPhaseOption("jb.ulp", "off");
Options.v().setPhaseOption("jb.uce", "remove-unreachable-traps:true");
Options.v().setPhaseOption("cg", "trim-clinit:false");
Options.v().set_prepend_classpath(true);
if (AnalysisParameters.v().useShimple()) {
Options.v().set_via_shimple(true);
Options.v().set_whole_shimple(true);
}
Options.v().set_src_prec(Options.src_prec_java);
Timers.v().misc.end();
Timers.v().classLoading.start();
for (String frameworkClass : frameworkClasses) {
SootClass c = Scene.v().loadClassAndSupport(frameworkClass);
Scene.v().forceResolve(frameworkClass, SootClass.BODIES);
c.setApplicationClass();
}
Scene.v().loadNecessaryClasses();
Timers.v().classLoading.end();
Timers.v().entryPointMapping.start();
Scene.v().setEntryPoints(
Collections.singletonList(setupApplication.getEntryPointCreator().createDummyMain()));
Timers.v().entryPointMapping.end();
}
protected void prepareManifestFile(Ic3CommandLineArguments commandLineArguments) {
if (commandLineArguments.getDb() != null
|| commandLineArguments.getProtobufDestination() != null) {
detailedManifest = new ManifestPullParser();
detailedManifest.loadManifestFile(commandLineArguments.getManifest());
}
}
@Override
protected void setApplicationClasses(Ic3CommandLineArguments commandLineArguments)
throws FatalAnalysisException {
AnalysisParameters.v().addAnalysisClasses(
computeAnalysisClasses(commandLineArguments.getInput()));
AnalysisParameters.v().addAnalysisClasses(frameworkClasses);
}
@Override
protected void handleFatalAnalysisException(Ic3CommandLineArguments commandLineArguments,
FatalAnalysisException exception) {
logger.error("Could not process application " + packageName, exception);
if (outputDir != null && packageName != null) {
try {
if (writer == null) {
String outputFile = String.format("%s/%s.csv", outputDir, packageName);
writer = new BufferedWriter(new FileWriter(outputFile, false));
}
writer.write(commandLineArguments.getInput() + " -1\n");
writer.close();
} catch (IOException e1) {
logger.error("Could not write to file after failure to process application", e1);
}
}
}
@Override
protected void processResults(Ic3CommandLineArguments commandLineArguments)
throws FatalAnalysisException {
System.out.println("\n*****Manifest*****");
System.out.println(detailedManifest.toString());
if (commandLineArguments.getProtobufDestination() != null) {
ProtobufResultProcessor resultProcessor = new ProtobufResultProcessor();
try {
resultProcessor.processResult(packageName, ic3Builder,
commandLineArguments.getProtobufDestination(), commandLineArguments.binary(),
componentNameToBuilderMap, AnalysisParameters.v().getAnalysisClasses().size(), writer);
} catch (IOException e) {
logger.error("Could not process analysis results", e);
throw new FatalAnalysisException();
}
} else {
ResultProcessor resultProcessor = new ResultProcessor();
try {
resultProcessor.processResult(commandLineArguments.getDb() != null, packageName,
componentToIdMap, AnalysisParameters.v().getAnalysisClasses().size(), writer);
} catch (IOException | SQLException e) {
logger.error("Could not process analysis results", e);
throw new FatalAnalysisException();
}
}
}
@Override
protected void finalizeAnalysis(Ic3CommandLineArguments commandLineArguments) {
}
protected void addSceneTransformer(Map<SootMethod, Set<String>> entryPointMap) {
Ic3ResultBuilder resultBuilder = new Ic3ResultBuilder();
resultBuilder.setEntryPointMap(entryPointMap);
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
String debugDirPath = System.getProperty("user.home") + File.separator + "debug";
File debugDir = new File(debugDirPath);
if (!debugDir.exists()) {
debugDir.mkdir();
}
String fileName = dateFormat.format(new Date()) + ".txt";
String debugFilename = debugDirPath + File.separator + fileName;
String pack = AnalysisParameters.v().useShimple() ? "wstp" : "wjtp";
Transform transform =
new Transform(pack + ".ifds", new PropagationSceneTransformer(resultBuilder,
new PropagationSceneTransformerFilePrinter(debugFilename, new SymbolFilter() {
@Override
public boolean filterOut(Value symbol) {
return symbol instanceof StaticFieldRef
&& ((StaticFieldRef) symbol).getField().getDeclaringClass().getName()
.startsWith("android.provider");
}
})));
if (PackManager.v().getPack(pack).get(pack + ".ifds") == null) {
PackManager.v().getPack(pack).add(transform);
} else {
Iterator<?> it = PackManager.v().getPack(pack).iterator();
while (it.hasNext()) {
Object current = it.next();
if (current instanceof Transform
&& ((Transform) current).getPhaseName().equals(pack + ".ifds")) {
it.remove();
break;
}
}
PackManager.v().getPack(pack).add(transform);
}
}
protected void addEntryPointMappingSceneTransformer(Set<String> entryPointClasses,
Map<String, Set<String>> entryPointMapping, Map<SootMethod, Set<String>> entryPointMap) {
String pack = AnalysisParameters.v().useShimple() ? "wstp" : "wjtp";
Transform transform =
new Transform(pack + ".epm", new EntryPointMappingSceneTransformer(entryPointClasses,
entryPointMapping, entryPointMap));
if (PackManager.v().getPack(pack).get(pack + ".epm") == null) {
PackManager.v().getPack(pack).add(transform);
} else {
Iterator<?> it = PackManager.v().getPack(pack).iterator();
while (it.hasNext()) {
Object current = it.next();
if (current instanceof Transform
&& ((Transform) current).getPhaseName().equals(pack + ".epm")) {
it.remove();
break;
}
}
PackManager.v().getPack(pack).add(transform);
}
}
}