Skip to content

Commit bba1564

Browse files
committed
Add support for JavaScript interfaces in dummy main
1 parent 5ffe931 commit bba1564

File tree

5 files changed

+148
-10
lines changed

5 files changed

+148
-10
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ public class SetupApplication implements ITaintWrapperDataFlowAnalysis {
122122
protected InfoflowAndroidConfiguration config = new InfoflowAndroidConfiguration();
123123

124124
protected Set<SootClass> entrypoints = null;
125+
protected MultiMap<SootMethod, Stmt> javascriptInterfaceStmts = new HashMultiMap<>();
125126
protected Set<String> callbackClasses = null;
126127
protected AndroidEntryPointCreator entryPointCreator = null;
127128
protected IccInstrumenter iccInstrumenter = null;
@@ -776,6 +777,9 @@ private void calculateCallbackMethods(LayoutFileParser lfp, SootClass component)
776777
if (entrypoints.addAll(jimpleClass.getDynamicManifestComponents()))
777778
hasChanged = true;
778779

780+
if (javascriptInterfaceStmts.putAll(jimpleClass.getJavaScriptInterfaces()))
781+
hasChanged = true;
782+
779783
// Collect the XML-based callback methods
780784
if (collectXmlBasedCallbackMethods(lfp, jimpleClass))
781785
hasChanged = true;
@@ -1634,6 +1638,7 @@ protected void processEntryPoint(ISourceSinkDefinitionProvider sourcesAndSinks,
16341638
// We don't need the computed callbacks anymore
16351639
this.callbackMethods.clear();
16361640
this.fragmentClasses.clear();
1641+
this.javascriptInterfaceStmts.clear();
16371642

16381643
// Notify our result handlers
16391644
for (ResultsAvailableHandler handler : resultsAvailableHandlers)
@@ -1787,6 +1792,7 @@ private AndroidEntryPointCreator createEntryPointCreator(SootClass component) {
17871792
entryPointCreator.setCallbackFunctions(callbackMethodSigs);
17881793
entryPointCreator.setFragments(fragmentClasses);
17891794
entryPointCreator.setComponents(components);
1795+
entryPointCreator.setJavaScriptInterfaces(javascriptInterfaceStmts);
17901796
return entryPointCreator;
17911797
}
17921798

soot-infoflow-android/src/soot/jimple/infoflow/android/callbacks/AbstractCallbackAnalyzer.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,8 @@ else if (arrayLocals.contains(lop))
199199

200200
});
201201

202+
private MultiMap<SootMethod, Stmt> javaScriptInterfaces = new HashMultiMap<SootMethod, Stmt>();
203+
202204
public AbstractCallbackAnalyzer(InfoflowAndroidConfiguration config, Set<SootClass> entryPointClasses)
203205
throws IOException {
204206
this(config, entryPointClasses, "AndroidCallbacks.txt");
@@ -432,6 +434,28 @@ protected void analyzeMethodForDynamicBroadcastReceiver(SootMethod method) {
432434
}
433435
}
434436

437+
protected void analyzeMethodForJavascriptInterfaces(SootMethod method) {
438+
// Do not analyze system classes
439+
if (SystemClassHandler.v().isClassInSystemPackage(method.getDeclaringClass().getName()))
440+
return;
441+
if (!method.isConcrete() || !method.hasActiveBody())
442+
return;
443+
444+
final FastHierarchy fastHierarchy = Scene.v().getFastHierarchy();
445+
final RefType webViewType = RefType.v("android.webkit.WebView");
446+
for (Unit u : method.getActiveBody().getUnits()) {
447+
Stmt stmt = (Stmt) u;
448+
if (stmt.containsInvokeExpr()) {
449+
final InvokeExpr iexpr = stmt.getInvokeExpr();
450+
final SootMethodRef methodRef = iexpr.getMethodRef();
451+
if (methodRef.getName().equals("addJavascriptInterface") && iexpr.getArgCount() == 2
452+
&& fastHierarchy.canStoreType(methodRef.getDeclaringClass().getType(), webViewType)) {
453+
this.javaScriptInterfaces.put(method, stmt);
454+
}
455+
}
456+
}
457+
}
458+
435459
/**
436460
* Checks whether the given method dynamically registers a new service
437461
* connection
@@ -1046,4 +1070,12 @@ public void setValueProvider(IValueProvider valueProvider) {
10461070
this.valueProvider = valueProvider;
10471071
}
10481072

1073+
/**
1074+
* Returns a set of all statements which add a javascript interface
1075+
* @return the statement list
1076+
*/
1077+
public MultiMap<SootMethod, Stmt> getJavaScriptInterfaces() {
1078+
return javaScriptInterfaces;
1079+
}
1080+
10491081
}

soot-infoflow-android/src/soot/jimple/infoflow/android/callbacks/DefaultCallbackAnalyzer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ private void analyzeReachableMethods(SootClass lifecycleElement, List<MethodOrMe
209209
analyzeMethodForServiceConnection(method);
210210
analyzeMethodForFragmentTransaction(lifecycleElement, method);
211211
analyzeMethodForViewPagers(lifecycleElement, method);
212+
analyzeMethodForJavascriptInterfaces(method);
212213
}
213214
}
214215
}

soot-infoflow-android/src/soot/jimple/infoflow/android/entryPointCreators/AndroidEntryPointCreator.java

Lines changed: 84 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
******************************************************************************/
1111
package soot.jimple.infoflow.android.entryPointCreators;
1212

13+
import java.util.ArrayList;
1314
import java.util.Collection;
1415
import java.util.Collections;
1516
import java.util.HashMap;
@@ -23,18 +24,25 @@
2324
import org.slf4j.LoggerFactory;
2425

2526
import soot.Body;
27+
import soot.Hierarchy;
2628
import soot.Local;
2729
import soot.Modifier;
2830
import soot.RefType;
2931
import soot.Scene;
3032
import soot.SootClass;
3133
import soot.SootField;
3234
import soot.SootMethod;
35+
import soot.Type;
3336
import soot.Unit;
37+
import soot.UnitPatchingChain;
38+
import soot.Value;
39+
import soot.jimple.AssignStmt;
3440
import soot.jimple.IfStmt;
41+
import soot.jimple.InvokeStmt;
3542
import soot.jimple.Jimple;
3643
import soot.jimple.NopStmt;
3744
import soot.jimple.NullConstant;
45+
import soot.jimple.StaticFieldRef;
3846
import soot.jimple.Stmt;
3947
import soot.jimple.infoflow.android.entryPointCreators.AndroidEntryPointUtils.ComponentType;
4048
import soot.jimple.infoflow.android.entryPointCreators.components.AbstractComponentEntryPointCreator;
@@ -50,6 +58,7 @@
5058
import soot.jimple.infoflow.cfg.LibraryClassPatcher;
5159
import soot.jimple.infoflow.data.SootMethodAndClass;
5260
import soot.jimple.infoflow.entryPointCreators.IEntryPointCreator;
61+
import soot.jimple.infoflow.entryPointCreators.SimulatedCodeElementTag;
5362
import soot.jimple.infoflow.util.SootMethodRepresentationParser;
5463
import soot.jimple.infoflow.util.SystemClassHandler;
5564
import soot.jimple.toolkits.scalar.NopEliminator;
@@ -92,6 +101,8 @@ public class AndroidEntryPointCreator extends AbstractAndroidEntryPointCreator i
92101

93102
private Collection<SootClass> components;
94103

104+
private MultiMap<SootMethod, Stmt> javascriptInterfaceStmts;
105+
95106
/**
96107
* Creates a new instance of the {@link AndroidEntryPointCreator} class and
97108
* registers a list of classes to be automatically scanned for Android lifecycle
@@ -305,6 +316,7 @@ protected SootMethod createDummyMainInternal() {
305316
addApplicationCallbackMethods();
306317
createIfStmt(beforeAppCallbacks);
307318
}
319+
createJavascriptCallbacks();
308320

309321
createIfStmt(outerStartStmt);
310322

@@ -326,6 +338,60 @@ protected SootMethod createDummyMainInternal() {
326338
return mainMethod;
327339
}
328340

341+
private void createJavascriptCallbacks() {
342+
Jimple j = Jimple.v();
343+
Hierarchy h = Scene.v().getActiveHierarchy();
344+
for (SootMethod m : javascriptInterfaceStmts.keySet()) {
345+
Set<Stmt> statements = javascriptInterfaceStmts.get(m);
346+
for (Stmt s : statements) {
347+
UnitPatchingChain units = m.retrieveActiveBody().getUnits();
348+
Stmt succ = (Stmt) units.getSuccOf(s);
349+
SootField f = null;
350+
Value arg = s.getInvokeExpr().getArg(0);
351+
if (succ.getTag(SimulatedCodeElementTag.TAG_NAME) != null) {
352+
if (succ instanceof AssignStmt) {
353+
AssignStmt assign = (AssignStmt) succ;
354+
if (assign.getRightOp() == arg && assign.getLeftOp() instanceof StaticFieldRef) {
355+
f = ((StaticFieldRef) assign.getLeftOp()).getField();
356+
}
357+
}
358+
}
359+
if (f == null) {
360+
//create field
361+
f = createField(arg.getType(), "jsInterface");
362+
AssignStmt assign = j.newAssignStmt(j.newStaticFieldRef(f.makeRef()), arg);
363+
assign.addTag(SimulatedCodeElementTag.TAG);
364+
units.insertAfter(assign, s);
365+
}
366+
367+
Local l = j.newLocal(f.getName(), f.getType());
368+
body.getLocals().add(l);
369+
Stmt assignF = j.newAssignStmt(l, j.newStaticFieldRef(f.makeRef()));
370+
body.getUnits().add(assignF);
371+
SootClass cbtype = ((RefType) f.getType()).getSootClass();
372+
List<SootClass> allPossibleImpls;
373+
if (cbtype.isInterface()) {
374+
allPossibleImpls = h.getImplementersOf(cbtype);
375+
} else {
376+
allPossibleImpls = h.getSubclassesOfIncluding(cbtype);
377+
}
378+
for (SootClass c : allPossibleImpls) {
379+
for (SootMethod cbm : c.getMethods()) {
380+
if (AndroidEntryPointUtils.isCallableFromJS(cbm)) {
381+
List<Value> args = new ArrayList<>();
382+
for (Type t : cbm.getParameterTypes())
383+
args.add(getSimpleDefaultValue(t));
384+
InvokeStmt st = j.newInvokeStmt(j.newVirtualInvokeExpr(l, cbm.makeRef(), args));
385+
body.getUnits().add(st);
386+
}
387+
}
388+
}
389+
createIfStmt(assignF);
390+
391+
}
392+
}
393+
}
394+
329395
/**
330396
* Checks whether a lifecycle call should be added to the given SootClass,
331397
* designed to be overridden by different implementations
@@ -392,20 +458,24 @@ private void initializeApplicationClass() {
392458
baseName = baseName.substring(baseName.lastIndexOf(".") + 1);
393459

394460
// Generate a fresh field name
395-
SootClass dummyMainClass = mainMethod.getDeclaringClass();
396-
int idx = 0;
397-
String fieldName = baseName;
398-
while (dummyMainClass.declaresFieldByName(fieldName)) {
399-
fieldName = baseName + "_" + idx;
400-
idx++;
401-
}
402-
SootField fld = Scene.v().makeSootField(fieldName, RefType.v(callbackClass),
403-
Modifier.PRIVATE | Modifier.STATIC);
404-
mainMethod.getDeclaringClass().addField(fld);
461+
SootField fld = createField(RefType.v(callbackClass), baseName);
405462
callbackClassToField.put(callbackClass, fld);
406463
}
407464
}
408465

466+
protected SootField createField(Type type, String baseName) {
467+
SootClass dummyMainClass = mainMethod.getDeclaringClass();
468+
int idx = 0;
469+
String fieldName = baseName;
470+
while (dummyMainClass.declaresFieldByName(fieldName)) {
471+
fieldName = baseName + "_" + idx;
472+
idx++;
473+
}
474+
SootField fld = Scene.v().makeSootField(fieldName, type, Modifier.PRIVATE | Modifier.STATIC);
475+
mainMethod.getDeclaringClass().addField(fld);
476+
return fld;
477+
}
478+
409479
/**
410480
* Removes if statements that jump to the fall-through successor
411481
*
@@ -602,4 +672,8 @@ public void removeGeneratedMethods(boolean removeClass) {
602672
}
603673
}
604674

675+
public void setJavaScriptInterfaces(MultiMap<SootMethod, Stmt> javascriptInterfaceStmts) {
676+
this.javascriptInterfaceStmts = javascriptInterfaceStmts;
677+
}
678+
605679
}

soot-infoflow-android/src/soot/jimple/infoflow/android/entryPointCreators/AndroidEntryPointUtils.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
import soot.SootClass;
1818
import soot.SootMethod;
1919
import soot.jimple.infoflow.util.SystemClassHandler;
20+
import soot.tagkit.AnnotationTag;
21+
import soot.tagkit.Tag;
22+
import soot.tagkit.VisibilityAnnotationTag;
2023

2124
/**
2225
* Class containing common utility methods for dealing with Android entry points
@@ -52,6 +55,8 @@ public enum ComponentType {
5255
GCMListenerService, HostApduService, ServiceConnection, Plain
5356
}
5457

58+
private static final String JS_INTERFACE = "Landroid/webkit/JavascriptInterface;";
59+
5560
/**
5661
* Creates a new instance of the {@link AndroidEntryPointUtils} class. Soot must
5762
* already be running when this constructor is invoked.
@@ -280,4 +285,24 @@ private static Collection<? extends MethodOrMethodContext> getLifecycleMethods(S
280285
return lifecycleMethods;
281286
}
282287

288+
/**
289+
* Checks whether a method is potentially callable from JavaScript
290+
* @param m the method
291+
* @return true if the method is pot. callable from JS
292+
*/
293+
public static boolean isCallableFromJS(SootMethod m) {
294+
if (!m.isPublic() || m.isStatic())
295+
return false;
296+
297+
for (Tag tag : m.getTags()) {
298+
if (tag instanceof VisibilityAnnotationTag) {
299+
VisibilityAnnotationTag vatag = (VisibilityAnnotationTag) tag;
300+
for (AnnotationTag t : vatag.getAnnotations()) {
301+
if (t.getType().equals(JS_INTERFACE))
302+
return true;
303+
}
304+
}
305+
}
306+
return false;
307+
}
283308
}

0 commit comments

Comments
 (0)