Skip to content

Commit 5dfe9b1

Browse files
authored
Merge pull request #708 from MarcMil/extensibility
Add support for JS interfaces and WebViewClient
2 parents 5dbe46d + 83989cf commit 5dfe9b1

File tree

7 files changed

+181
-11
lines changed

7 files changed

+181
-11
lines changed

soot-infoflow-android/AndroidCallbacks.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ android.webkit.WebMessagePort$WebMessageCallback
349349
android.webkit.WebView$FindListener
350350
android.webkit.WebView$PictureListener
351351
android.webkit.WebView$VisualStateCallback
352+
android.webkit.WebViewClient
352353
android.widget.AbsListView$MultiChoiceModeListener
353354
android.widget.AbsListView$OnScrollListener
354355
android.widget.AbsListView$RecyclerListener
@@ -401,4 +402,4 @@ javax.security.auth.callback.PasswordCallback
401402
javax.sql.ConnectionEventListener
402403
javax.sql.RowSetListener
403404
javax.sql.StatementEventListener
404-
javax.xml.transform.ErrorListener
405+
javax.xml.transform.ErrorListener

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: 74 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;
@@ -30,8 +31,13 @@
3031
import soot.SootClass;
3132
import soot.SootField;
3233
import soot.SootMethod;
34+
import soot.Type;
3335
import soot.Unit;
36+
import soot.UnitPatchingChain;
37+
import soot.Value;
38+
import soot.jimple.AssignStmt;
3439
import soot.jimple.IfStmt;
40+
import soot.jimple.InvokeStmt;
3541
import soot.jimple.Jimple;
3642
import soot.jimple.NopStmt;
3743
import soot.jimple.NullConstant;
@@ -50,6 +56,8 @@
5056
import soot.jimple.infoflow.cfg.LibraryClassPatcher;
5157
import soot.jimple.infoflow.data.SootMethodAndClass;
5258
import soot.jimple.infoflow.entryPointCreators.IEntryPointCreator;
59+
import soot.jimple.infoflow.entryPointCreators.SimulatedCodeElementTag;
60+
import soot.jimple.infoflow.typing.TypeUtils;
5361
import soot.jimple.infoflow.util.SootMethodRepresentationParser;
5462
import soot.jimple.infoflow.util.SystemClassHandler;
5563
import soot.jimple.toolkits.scalar.NopEliminator;
@@ -92,6 +100,8 @@ public class AndroidEntryPointCreator extends AbstractAndroidEntryPointCreator i
92100

93101
private Collection<SootClass> components;
94102

103+
private MultiMap<SootMethod, Stmt> javascriptInterfaceStmts;
104+
95105
/**
96106
* Creates a new instance of the {@link AndroidEntryPointCreator} class and
97107
* registers a list of classes to be automatically scanned for Android lifecycle
@@ -305,6 +315,7 @@ protected SootMethod createDummyMainInternal() {
305315
addApplicationCallbackMethods();
306316
createIfStmt(beforeAppCallbacks);
307317
}
318+
createJavascriptCallbacks();
308319

309320
createIfStmt(outerStartStmt);
310321

@@ -326,6 +337,51 @@ protected SootMethod createDummyMainInternal() {
326337
return mainMethod;
327338
}
328339

340+
private void createJavascriptCallbacks() {
341+
Jimple j = Jimple.v();
342+
for (SootMethod m : javascriptInterfaceStmts.keySet()) {
343+
Set<Stmt> statements = javascriptInterfaceStmts.get(m);
344+
for (Stmt s : statements) {
345+
UnitPatchingChain units = m.retrieveActiveBody().getUnits();
346+
SootField f = null;
347+
Value arg = s.getInvokeExpr().getArg(0);
348+
DummyMainFieldElementTag dm = (DummyMainFieldElementTag) s.getTag(DummyMainFieldElementTag.TAG_NAME);
349+
if (dm != null) {
350+
f = mainMethod.getDeclaringClass().getFieldByNameUnsafe(dm.getFieldName());
351+
}
352+
if (f == null) {
353+
//create field
354+
f = createField(arg.getType(), "jsInterface");
355+
AssignStmt assign = j.newAssignStmt(j.newStaticFieldRef(f.makeRef()), arg);
356+
assign.addTag(SimulatedCodeElementTag.TAG);
357+
s.addTag(new DummyMainFieldElementTag(f.getName()));
358+
units.insertAfter(assign, s);
359+
}
360+
361+
Local l = j.newLocal(f.getName(), f.getType());
362+
body.getLocals().add(l);
363+
Stmt assignF = j.newAssignStmt(l, j.newStaticFieldRef(f.makeRef()));
364+
body.getUnits().add(assignF);
365+
SootClass cbtype = ((RefType) f.getType()).getSootClass();
366+
367+
for (SootClass c : TypeUtils.getAllDerivedClasses(cbtype)) {
368+
for (SootMethod cbm : c.getMethods()) {
369+
if (AndroidEntryPointUtils.isCallableFromJS(cbm)) {
370+
List<Value> args = new ArrayList<>();
371+
for (Type t : cbm.getParameterTypes())
372+
args.add(getSimpleDefaultValue(t));
373+
InvokeStmt st = j.newInvokeStmt(j.newVirtualInvokeExpr(l, cbm.makeRef(), args));
374+
body.getUnits().add(st);
375+
}
376+
}
377+
378+
}
379+
createIfStmt(assignF);
380+
381+
}
382+
}
383+
}
384+
329385
/**
330386
* Checks whether a lifecycle call should be added to the given SootClass,
331387
* designed to be overridden by different implementations
@@ -392,20 +448,24 @@ private void initializeApplicationClass() {
392448
baseName = baseName.substring(baseName.lastIndexOf(".") + 1);
393449

394450
// 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);
451+
SootField fld = createField(RefType.v(callbackClass), baseName);
405452
callbackClassToField.put(callbackClass, fld);
406453
}
407454
}
408455

456+
protected SootField createField(Type type, String baseName) {
457+
SootClass dummyMainClass = mainMethod.getDeclaringClass();
458+
int idx = 0;
459+
String fieldName = baseName;
460+
while (dummyMainClass.declaresFieldByName(fieldName)) {
461+
fieldName = baseName + "_" + idx;
462+
idx++;
463+
}
464+
SootField fld = Scene.v().makeSootField(fieldName, type, Modifier.PRIVATE | Modifier.STATIC);
465+
mainMethod.getDeclaringClass().addField(fld);
466+
return fld;
467+
}
468+
409469
/**
410470
* Removes if statements that jump to the fall-through successor
411471
*
@@ -602,4 +662,8 @@ public void removeGeneratedMethods(boolean removeClass) {
602662
}
603663
}
604664

665+
public void setJavaScriptInterfaces(MultiMap<SootMethod, Stmt> javascriptInterfaceStmts) {
666+
this.javascriptInterfaceStmts = javascriptInterfaceStmts;
667+
}
668+
605669
}

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() || m.isAbstract())
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
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package soot.jimple.infoflow.android.entryPointCreators;
2+
3+
import soot.tagkit.AttributeValueException;
4+
import soot.tagkit.Tag;
5+
6+
/**
7+
* Tag to denote that a certain method or class was created by an entry point
8+
* creator and should not be considered real app code
9+
*
10+
* @author Steven Arzt
11+
*
12+
*/
13+
public class DummyMainFieldElementTag implements Tag {
14+
15+
public static final String TAG_NAME = "DummyMainFieldElementTag";
16+
public static DummyMainFieldElementTag TAG = new DummyMainFieldElementTag();
17+
private String fieldName;
18+
19+
private DummyMainFieldElementTag() {
20+
//
21+
}
22+
23+
public DummyMainFieldElementTag(String name) {
24+
this.fieldName = name;
25+
}
26+
27+
public String getFieldName() {
28+
return fieldName;
29+
}
30+
31+
@Override
32+
public String getName() {
33+
return TAG_NAME;
34+
}
35+
36+
@Override
37+
public byte[] getValue() throws AttributeValueException {
38+
return null;
39+
}
40+
41+
}

0 commit comments

Comments
 (0)