-
Notifications
You must be signed in to change notification settings - Fork 308
Add support for app component factory #824
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
MarcMil
wants to merge
25
commits into
secure-software-engineering:develop
Choose a base branch
from
MarcMil:improve-code
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
4454ba9
Add support for app component factory
MarcMil 978e5ea
Proper support for app component factories
MarcMil 61f7c8a
Reenable timeout
MarcMil 22e4aca
Do not generate phantom classes
MarcMil 204d8ea
Fix naming, add missing files
MarcMil 076b003
Fix invalid import
MarcMil d0ed9ac
Fix
MarcMil 2a13a68
Set modifiers
MarcMil 04b50f1
Make sure to not create phantom classes accidentally
MarcMil cafb7cd
Be more precise with the typing if possible
MarcMil 20bff15
Fix bug with interface calls
MarcMil 1b4cd59
Fix NPE
MarcMil 3f744e5
Fix loading classes
MarcMil 35c1b12
Make sure the application class is loaded
MarcMil 0591e79
Proper method management
MarcMil 197d4ff
Use JNI-style class notation for dummy main class constant
MarcMil 4dcd1db
Make sure that the method is not phantomized
MarcMil 5100c2d
Set modifiers to public
MarcMil f3d6eb2
Fix adding the statement multiple times
MarcMil a6d6913
Add attachBaseContext support and optionally do not exclude stub meth…
MarcMil 5c06b0a
Fix for non-existing context wrapper class
MarcMil a09342a
Fix NPE
MarcMil 8f488ff
Adding JavaDoc
MarcMil a8c0108
Support for multiple pseudoconstants and fragment types
MarcMil 27b7147
Remove unnecessary fields, improving code quality
MarcMil File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
222 changes: 222 additions & 0 deletions
222
soot-infoflow-android/src/soot/jimple/infoflow/android/AndroidLibraryClassPatcher.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
package soot.jimple.infoflow.android; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
|
||
import soot.BooleanConstant; | ||
import soot.BooleanType; | ||
import soot.Hierarchy; | ||
import soot.Local; | ||
import soot.LocalGenerator; | ||
import soot.Modifier; | ||
import soot.RefType; | ||
import soot.Scene; | ||
import soot.SootClass; | ||
import soot.SootMethod; | ||
import soot.Type; | ||
import soot.UnitPatchingChain; | ||
import soot.jimple.Jimple; | ||
import soot.jimple.JimpleBody; | ||
import soot.jimple.NopStmt; | ||
import soot.jimple.StringConstant; | ||
import soot.jimple.infoflow.android.entryPointCreators.AndroidEntryPointConstants; | ||
import soot.jimple.infoflow.cfg.LibraryClassPatcher; | ||
import soot.jimple.infoflow.util.SootMethodRepresentationParser; | ||
import soot.jimple.toolkits.scalar.NopEliminator; | ||
import soot.util.Chain; | ||
|
||
/** | ||
* In addition to the normal JVM library classes, this class also patches | ||
* certain Android library classes. | ||
*/ | ||
public class AndroidLibraryClassPatcher extends LibraryClassPatcher { | ||
|
||
@Override | ||
public void patchLibraries() { | ||
super.patchLibraries(); | ||
|
||
patchComponentFactory(); | ||
} | ||
|
||
/** | ||
* The generated implementation of this method are semantically equivalent to the AppComponentFactory in Android. | ||
* @see https://android.googlesource.com/platform/frameworks/base/+/refs/heads/main/core/java/android/app/AppComponentFactory.java | ||
*/ | ||
protected void patchComponentFactory() { | ||
SootClass sc = Scene.v().forceResolve(AndroidEntryPointConstants.APPCOMPONENTFACTORYCLASS, | ||
SootClass.SIGNATURES); | ||
|
||
patchInstantiate(sc, AndroidEntryPointConstants.APPCOMPONENTFACTORY_INSTANTIATEAPPLICATION, | ||
getAllNames(AndroidEntryPointConstants.APPLICATIONCLASS)); | ||
patchInstantiate(sc, AndroidEntryPointConstants.APPCOMPONENTFACTORY_INSTANTIATEACTIVITY, | ||
getAllNames(AndroidEntryPointConstants.ACTIVITYCLASS)); | ||
patchInstantiate(sc, AndroidEntryPointConstants.APPCOMPONENTFACTORY_INSTANTIATEPROVIDER, | ||
getAllNames(AndroidEntryPointConstants.BROADCASTRECEIVERCLASS)); | ||
patchInstantiate(sc, AndroidEntryPointConstants.APPCOMPONENTFACTORY_INSTANTIATERECEIVER, | ||
getAllNames(AndroidEntryPointConstants.BROADCASTRECEIVERCLASS)); | ||
|
||
patchInstantiateClassLoader(sc); | ||
|
||
} | ||
|
||
/** | ||
* Patches the instantiate classloader class. | ||
* It returns the default class loader unmodified. | ||
* @param sc the class of the app component factory | ||
*/ | ||
private void patchInstantiateClassLoader(SootClass sc) { | ||
SootMethod smInstantiate = getOrCreateMethod(sc, | ||
AndroidEntryPointConstants.APPCOMPONENTFACTORY_INSTANTIATECLASSLOADER); | ||
JimpleBody body = Jimple.v().newBody(smInstantiate); | ||
smInstantiate.setActiveBody(body); | ||
body.insertIdentityStmts(); | ||
body.getUnits().add(Jimple.v().newReturnStmt(body.getParameterLocal(0))); | ||
|
||
} | ||
|
||
/** | ||
* Returns all class names that could be instantiated when | ||
* instantiating a class with the given class name, i.e. all subclasses/implementers. | ||
* @param className the class name (could also represent an interface) | ||
* @return a string array of all possible names. | ||
*/ | ||
protected String[] getAllNames(String className) { | ||
MarcMil marked this conversation as resolved.
Show resolved
Hide resolved
|
||
List<String> names = new ArrayList<>(); | ||
SootClass sc = Scene.v().getSootClassUnsafe(className); | ||
if (sc == null) | ||
return new String[0]; | ||
Hierarchy fh = Scene.v().getActiveHierarchy(); | ||
List<SootClass> components; | ||
if (sc.isInterface()) { | ||
components = fh.getImplementersOf(sc); | ||
} else { | ||
components = fh.getSubclassesOf(sc); | ||
|
||
} | ||
for (SootClass c : components) { | ||
if (c.isConcrete()) | ||
names.add(c.getName()); | ||
} | ||
return names.toArray(new String[names.size()]); | ||
} | ||
|
||
/** | ||
* Patches an instantiate method. Generates code equivalent to the following: | ||
* | ||
* <code> | ||
* public void instantiateActivity(ClassLoader cl, String className, Intent intent) | ||
* { | ||
* | ||
* if (className.equals("foo.bar.MainActivity")) | ||
* return new foo.bar.MainActivity(); //(1) | ||
* if (className.equals("foo.bar.FooActivity")) | ||
* return new foo.bar.FooActivity(); //(2) | ||
* return cl.loadClass(className).newInstance(); //(3) | ||
* | ||
* } | ||
* </code> | ||
* The instantiation statements (1) and (2) are used to help SPARK and other static algorithms to find | ||
* allocation sites. (3) is the fallback that would normally be the implementation when using Android's default | ||
* app component factory. | ||
* @param sc the class of the app component factory | ||
* @param subsig the sub signature of the method, in our example case instantiateActivity | ||
* @param names the names for each possible class instantiation, in our example case "foo.bar.MainActivity", "foo.bar.FooActivity" | ||
*/ | ||
protected void patchInstantiate(SootClass sc, String subsig, String... names) { | ||
|
||
if (!sc.isLibraryClass()) | ||
sc.setLibraryClass(); | ||
|
||
// We sometimes seem to be missing the constructor | ||
SootMethod smInstantiate = getOrCreateMethod(sc, subsig); | ||
Jimple j = Jimple.v(); | ||
JimpleBody body = j.newBody(smInstantiate); | ||
if (smInstantiate.isPhantom()) | ||
smInstantiate.setPhantom(false); | ||
smInstantiate.setModifiers(Modifier.PUBLIC); | ||
smInstantiate.setActiveBody(body); | ||
body.insertIdentityStmts(); | ||
Chain<Local> locals = body.getLocals(); | ||
UnitPatchingChain units = body.getUnits(); | ||
Scene scene = Scene.v(); | ||
Local ret = j.newLocal("returnVal", smInstantiate.getReturnType()); | ||
Local obj = j.newLocal("obj", scene.getObjectType()); | ||
Local cls = j.newLocal("clazz", RefType.v("java.lang.Class")); | ||
locals.add(ret); | ||
locals.add(obj); | ||
locals.add(cls); | ||
LocalGenerator generator = Scene.v().createLocalGenerator(body); | ||
|
||
Local cmp = null; | ||
NopStmt next = null; | ||
for (String n : names) { | ||
if (n != null) { | ||
RefType p = RefType.v(n); | ||
if (p.hasSootClass() && p.getSootClass().isApplicationClass()) { | ||
SootMethod ctor = p.getSootClass().getMethodUnsafe("void <init>()"); | ||
if (ctor != null) { | ||
if (cmp == null) { | ||
cmp = j.newLocal("bool", BooleanType.v()); | ||
locals.add(cmp); | ||
} | ||
if (next != null) | ||
units.add(next); | ||
units.add(j.newAssignStmt(cmp, | ||
j.newVirtualInvokeExpr(body.getParameterLocal(1), | ||
scene.makeMethodRef(RefType.v("java.lang.String").getSootClass(), | ||
"boolean equals(java.lang.Object)", false), | ||
StringConstant.v(p.getClassName())))); | ||
next = j.newNopStmt(); | ||
units.add(j.newIfStmt(j.newEqExpr(cmp, BooleanConstant.v(false)), next)); | ||
Local c = generator.generateLocal(p); | ||
units.add(j.newAssignStmt(c, j.newNewExpr(p))); | ||
units.add(j.newInvokeStmt(j.newSpecialInvokeExpr(c, ctor.makeRef()))); | ||
units.add(j.newReturnStmt(c)); | ||
} | ||
} | ||
} | ||
} | ||
if (next != null) | ||
units.add(next); | ||
units.add( | ||
j.newAssignStmt(cls, | ||
j.newVirtualInvokeExpr(body.getParameterLocal(0), | ||
scene.makeMethodRef(RefType.v("java.lang.ClassLoader").getSootClass(), | ||
"java.lang.Class loadClass(java.lang.String)", false), | ||
body.getParameterLocal(1)))); | ||
units.add(j.newAssignStmt(obj, j.newVirtualInvokeExpr(cls, scene | ||
.makeMethodRef(RefType.v("java.lang.Class").getSootClass(), "java.lang.Object newInstance()", false)))); | ||
units.add(j.newAssignStmt(ret, j.newCastExpr(obj, obj.getType()))); | ||
units.add(j.newReturnStmt(ret)); | ||
NopEliminator.v().transform(body); | ||
|
||
} | ||
|
||
/** | ||
* Creates a method if it doesn't exist. Otherwise, it returns the existing method | ||
* @param sc the class where the method is being looked for | ||
* @param subsig the sub signature of the method | ||
* @return the method | ||
*/ | ||
private static SootMethod getOrCreateMethod(SootClass sc, String subsig) { | ||
SootMethod p = sc.getMethodUnsafe(subsig); | ||
if (p != null) | ||
return p; | ||
|
||
SootMethodRepresentationParser parser = SootMethodRepresentationParser.v(); | ||
String name = parser.getMethodNameFromSubSignature(subsig); | ||
StevenArzt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Scene scene = Scene.v(); | ||
String sreturnType = parser.getReturnTypeFromSubSignature(subsig); | ||
|
||
String[] paramTypes = parser.getParameterTypesFromSubSignature(subsig); | ||
Type returnType = scene.getTypeUnsafe(sreturnType, false); | ||
Type[] aparamTypes = new Type[paramTypes.length]; | ||
for (int i = 0; i < paramTypes.length; i++) { | ||
aparamTypes[i] = scene.getTypeUnsafe(paramTypes[i], false); | ||
} | ||
p = Scene.v().makeSootMethod(name, Arrays.<Type>asList(aparamTypes), returnType, Modifier.PUBLIC); | ||
return sc.getOrAddMethod(p); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.