Skip to content

Commit 6fa02d9

Browse files
authored
Merge pull request #824 from MarcMil/improve-code
Add support for app component factory
2 parents aa0a1f5 + 27b7147 commit 6fa02d9

36 files changed

+1304
-406
lines changed
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
package soot.jimple.infoflow.android;
2+
3+
import java.util.ArrayList;
4+
import java.util.Arrays;
5+
import java.util.List;
6+
7+
import soot.BooleanConstant;
8+
import soot.BooleanType;
9+
import soot.Hierarchy;
10+
import soot.Local;
11+
import soot.LocalGenerator;
12+
import soot.Modifier;
13+
import soot.RefType;
14+
import soot.Scene;
15+
import soot.SootClass;
16+
import soot.SootMethod;
17+
import soot.Type;
18+
import soot.UnitPatchingChain;
19+
import soot.jimple.Jimple;
20+
import soot.jimple.JimpleBody;
21+
import soot.jimple.NopStmt;
22+
import soot.jimple.StringConstant;
23+
import soot.jimple.infoflow.android.entryPointCreators.AndroidEntryPointConstants;
24+
import soot.jimple.infoflow.cfg.LibraryClassPatcher;
25+
import soot.jimple.infoflow.util.SootMethodRepresentationParser;
26+
import soot.jimple.toolkits.scalar.NopEliminator;
27+
import soot.util.Chain;
28+
29+
/**
30+
* In addition to the normal JVM library classes, this class also patches
31+
* certain Android library classes.
32+
*/
33+
public class AndroidLibraryClassPatcher extends LibraryClassPatcher {
34+
35+
@Override
36+
public void patchLibraries() {
37+
super.patchLibraries();
38+
39+
patchComponentFactory();
40+
}
41+
42+
/**
43+
* The generated implementation of this method are semantically equivalent to the AppComponentFactory in Android.
44+
* @see https://android.googlesource.com/platform/frameworks/base/+/refs/heads/main/core/java/android/app/AppComponentFactory.java
45+
*/
46+
protected void patchComponentFactory() {
47+
SootClass sc = Scene.v().forceResolve(AndroidEntryPointConstants.APPCOMPONENTFACTORYCLASS,
48+
SootClass.SIGNATURES);
49+
50+
patchInstantiate(sc, AndroidEntryPointConstants.APPCOMPONENTFACTORY_INSTANTIATEAPPLICATION,
51+
getAllNames(AndroidEntryPointConstants.APPLICATIONCLASS));
52+
patchInstantiate(sc, AndroidEntryPointConstants.APPCOMPONENTFACTORY_INSTANTIATEACTIVITY,
53+
getAllNames(AndroidEntryPointConstants.ACTIVITYCLASS));
54+
patchInstantiate(sc, AndroidEntryPointConstants.APPCOMPONENTFACTORY_INSTANTIATEPROVIDER,
55+
getAllNames(AndroidEntryPointConstants.BROADCASTRECEIVERCLASS));
56+
patchInstantiate(sc, AndroidEntryPointConstants.APPCOMPONENTFACTORY_INSTANTIATERECEIVER,
57+
getAllNames(AndroidEntryPointConstants.BROADCASTRECEIVERCLASS));
58+
59+
patchInstantiateClassLoader(sc);
60+
61+
}
62+
63+
/**
64+
* Patches the instantiate classloader class.
65+
* It returns the default class loader unmodified.
66+
* @param sc the class of the app component factory
67+
*/
68+
private void patchInstantiateClassLoader(SootClass sc) {
69+
SootMethod smInstantiate = getOrCreateMethod(sc,
70+
AndroidEntryPointConstants.APPCOMPONENTFACTORY_INSTANTIATECLASSLOADER);
71+
JimpleBody body = Jimple.v().newBody(smInstantiate);
72+
smInstantiate.setActiveBody(body);
73+
body.insertIdentityStmts();
74+
body.getUnits().add(Jimple.v().newReturnStmt(body.getParameterLocal(0)));
75+
76+
}
77+
78+
/**
79+
* Returns all class names that could be instantiated when
80+
* instantiating a class with the given class name, i.e. all subclasses/implementers.
81+
* @param className the class name (could also represent an interface)
82+
* @return a string array of all possible names.
83+
*/
84+
protected String[] getAllNames(String className) {
85+
List<String> names = new ArrayList<>();
86+
SootClass sc = Scene.v().getSootClassUnsafe(className);
87+
if (sc == null)
88+
return new String[0];
89+
Hierarchy fh = Scene.v().getActiveHierarchy();
90+
List<SootClass> components;
91+
if (sc.isInterface()) {
92+
components = fh.getImplementersOf(sc);
93+
} else {
94+
components = fh.getSubclassesOf(sc);
95+
96+
}
97+
for (SootClass c : components) {
98+
if (c.isConcrete())
99+
names.add(c.getName());
100+
}
101+
return names.toArray(new String[names.size()]);
102+
}
103+
104+
/**
105+
* Patches an instantiate method. Generates code equivalent to the following:
106+
*
107+
* <code>
108+
* public void instantiateActivity(ClassLoader cl, String className, Intent intent)
109+
* {
110+
*
111+
* if (className.equals("foo.bar.MainActivity"))
112+
* return new foo.bar.MainActivity(); //(1)
113+
* if (className.equals("foo.bar.FooActivity"))
114+
* return new foo.bar.FooActivity(); //(2)
115+
* return cl.loadClass(className).newInstance(); //(3)
116+
*
117+
* }
118+
* </code>
119+
* The instantiation statements (1) and (2) are used to help SPARK and other static algorithms to find
120+
* allocation sites. (3) is the fallback that would normally be the implementation when using Android's default
121+
* app component factory.
122+
* @param sc the class of the app component factory
123+
* @param subsig the sub signature of the method, in our example case instantiateActivity
124+
* @param names the names for each possible class instantiation, in our example case "foo.bar.MainActivity", "foo.bar.FooActivity"
125+
*/
126+
protected void patchInstantiate(SootClass sc, String subsig, String... names) {
127+
128+
if (!sc.isLibraryClass())
129+
sc.setLibraryClass();
130+
131+
// We sometimes seem to be missing the constructor
132+
SootMethod smInstantiate = getOrCreateMethod(sc, subsig);
133+
Jimple j = Jimple.v();
134+
JimpleBody body = j.newBody(smInstantiate);
135+
if (smInstantiate.isPhantom())
136+
smInstantiate.setPhantom(false);
137+
smInstantiate.setModifiers(Modifier.PUBLIC);
138+
smInstantiate.setActiveBody(body);
139+
body.insertIdentityStmts();
140+
Chain<Local> locals = body.getLocals();
141+
UnitPatchingChain units = body.getUnits();
142+
Scene scene = Scene.v();
143+
Local ret = j.newLocal("returnVal", smInstantiate.getReturnType());
144+
Local obj = j.newLocal("obj", scene.getObjectType());
145+
Local cls = j.newLocal("clazz", RefType.v("java.lang.Class"));
146+
locals.add(ret);
147+
locals.add(obj);
148+
locals.add(cls);
149+
LocalGenerator generator = Scene.v().createLocalGenerator(body);
150+
151+
Local cmp = null;
152+
NopStmt next = null;
153+
for (String n : names) {
154+
if (n != null) {
155+
RefType p = RefType.v(n);
156+
if (p.hasSootClass() && p.getSootClass().isApplicationClass()) {
157+
SootMethod ctor = p.getSootClass().getMethodUnsafe("void <init>()");
158+
if (ctor != null) {
159+
if (cmp == null) {
160+
cmp = j.newLocal("bool", BooleanType.v());
161+
locals.add(cmp);
162+
}
163+
if (next != null)
164+
units.add(next);
165+
units.add(j.newAssignStmt(cmp,
166+
j.newVirtualInvokeExpr(body.getParameterLocal(1),
167+
scene.makeMethodRef(RefType.v("java.lang.String").getSootClass(),
168+
"boolean equals(java.lang.Object)", false),
169+
StringConstant.v(p.getClassName()))));
170+
next = j.newNopStmt();
171+
units.add(j.newIfStmt(j.newEqExpr(cmp, BooleanConstant.v(false)), next));
172+
Local c = generator.generateLocal(p);
173+
units.add(j.newAssignStmt(c, j.newNewExpr(p)));
174+
units.add(j.newInvokeStmt(j.newSpecialInvokeExpr(c, ctor.makeRef())));
175+
units.add(j.newReturnStmt(c));
176+
}
177+
}
178+
}
179+
}
180+
if (next != null)
181+
units.add(next);
182+
units.add(
183+
j.newAssignStmt(cls,
184+
j.newVirtualInvokeExpr(body.getParameterLocal(0),
185+
scene.makeMethodRef(RefType.v("java.lang.ClassLoader").getSootClass(),
186+
"java.lang.Class loadClass(java.lang.String)", false),
187+
body.getParameterLocal(1))));
188+
units.add(j.newAssignStmt(obj, j.newVirtualInvokeExpr(cls, scene
189+
.makeMethodRef(RefType.v("java.lang.Class").getSootClass(), "java.lang.Object newInstance()", false))));
190+
units.add(j.newAssignStmt(ret, j.newCastExpr(obj, obj.getType())));
191+
units.add(j.newReturnStmt(ret));
192+
NopEliminator.v().transform(body);
193+
194+
}
195+
196+
/**
197+
* Creates a method if it doesn't exist. Otherwise, it returns the existing method
198+
* @param sc the class where the method is being looked for
199+
* @param subsig the sub signature of the method
200+
* @return the method
201+
*/
202+
private static SootMethod getOrCreateMethod(SootClass sc, String subsig) {
203+
SootMethod p = sc.getMethodUnsafe(subsig);
204+
if (p != null)
205+
return p;
206+
207+
SootMethodRepresentationParser parser = SootMethodRepresentationParser.v();
208+
String name = parser.getMethodNameFromSubSignature(subsig);
209+
210+
Scene scene = Scene.v();
211+
String sreturnType = parser.getReturnTypeFromSubSignature(subsig);
212+
213+
String[] paramTypes = parser.getParameterTypesFromSubSignature(subsig);
214+
Type returnType = scene.getTypeUnsafe(sreturnType, false);
215+
Type[] aparamTypes = new Type[paramTypes.length];
216+
for (int i = 0; i < paramTypes.length; i++) {
217+
aparamTypes[i] = scene.getTypeUnsafe(paramTypes[i], false);
218+
}
219+
p = Scene.v().makeSootMethod(name, Arrays.<Type>asList(aparamTypes), returnType, Modifier.PUBLIC);
220+
return sc.getOrAddMethod(p);
221+
}
222+
}

soot-infoflow-android/src/soot/jimple/infoflow/android/InfoflowAndroidConfiguration.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ public static class CallbackConfiguration {
320320
private int maxCallbackAnalysisDepth = -1;
321321
private boolean serializeCallbacks = false;
322322
private String callbacksFile = "";
323+
private boolean excludeStubsFromCallGraph = true;
323324

324325
/**
325326
* Copies the settings of the given configuration into this configuration object
@@ -335,6 +336,7 @@ public void merge(CallbackConfiguration cbConfig) {
335336
this.maxCallbackAnalysisDepth = cbConfig.maxCallbackAnalysisDepth;
336337
this.serializeCallbacks = cbConfig.serializeCallbacks;
337338
this.callbacksFile = cbConfig.callbacksFile;
339+
this.excludeStubsFromCallGraph = cbConfig.excludeStubsFromCallGraph;
338340
}
339341

340342
/**
@@ -510,6 +512,22 @@ public void setCallbacksFile(String callbacksFile) {
510512
this.callbacksFile = callbacksFile;
511513
}
512514

515+
/**
516+
* Returns whether FlowDroid should exclude stub methods when computing the call graph
517+
* @return true if stubs should be excluded
518+
*/
519+
public boolean getExcludeStubsFromCallGraph() {
520+
return excludeStubsFromCallGraph;
521+
}
522+
523+
/**
524+
* Sets whether FlowDroid should exclude stub methods when computing the call graph
525+
* @param value true if stubs should be excluded
526+
*/
527+
public void setExcludeStubsFromCallGraph(boolean value) {
528+
this.excludeStubsFromCallGraph = value;
529+
}
530+
513531
@Override
514532
public int hashCode() {
515533
final int prime = 31;
@@ -522,6 +540,7 @@ public int hashCode() {
522540
result = prime * result + maxCallbackAnalysisDepth;
523541
result = prime * result + maxCallbacksPerComponent;
524542
result = prime * result + (serializeCallbacks ? 1231 : 1237);
543+
result = prime * result + (excludeStubsFromCallGraph ? 1231 : 1237);
525544
return result;
526545
}
527546

@@ -553,6 +572,8 @@ public boolean equals(Object obj) {
553572
return false;
554573
if (serializeCallbacks != other.serializeCallbacks)
555574
return false;
575+
if (excludeStubsFromCallGraph != other.excludeStubsFromCallGraph)
576+
return false;
556577
return true;
557578
}
558579

soot-infoflow-android/src/soot/jimple/infoflow/android/SetupApplication.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1286,7 +1286,7 @@ private void initializeSoot() {
12861286
}
12871287

12881288
protected LibraryClassPatcher getLibraryClassPatcher() {
1289-
return new LibraryClassPatcher();
1289+
return new AndroidLibraryClassPatcher();
12901290
}
12911291

12921292
/**
@@ -1913,6 +1913,9 @@ private Set<SootClass> getComponentsToAnalyze(SootClass component) {
19131913

19141914
IAndroidApplication app = manifest.getApplication();
19151915
if (app != null) {
1916+
String factoryName = app.getAppComponentFactory();
1917+
if (factoryName != null && !factoryName.isEmpty())
1918+
components.add(Scene.v().getSootClassUnsafe(factoryName));
19161919
String applicationName = app.getName();
19171920
if (applicationName != null && !applicationName.isEmpty())
19181921
components.add(Scene.v().getSootClassUnsafe(applicationName));

0 commit comments

Comments
 (0)