FlowDroid
FlowDroid copied to clipboard
A couple of questions regarding an issue running call flow analysis
Hello everyone!
I'm testing the call flow generation (for starters without any taint analysis or information flow analysis, just the call flows) on the InsecureBank apk. I'm seeing some unexpected output. I've added my questions first then the relevant code snippets.
Here are my questions:
- The call graph for many methods is empty (my output snippet below), my guess is that it's not created as their connections to the source are not being found. If there are methods that are not connected with the source of the call graph, can I at least see them as disconnected trees? I don't want to miss any code when visualizing the application.
- I've heard FlowDroid has some issues with implicit calls. I've noticed the methods with the missing call flows are called from the callbacks. Could this be it? Can you give me an example of which kinds of calls would be problematic for FlowDroid to find connections between? (I know there are problems with ICC but there are some solutions for that, right? Is there anything else?)
- I've noticed in
onCreate
call (method jimple listed below) we have outgoing calls tosetOnClickListener
, but those calls are not displayed on the list. I'm not sure what is special for these calls to be omitted. Is there a setting that can keep them in the tree? I'd expect this would be a problem for taint analysis if any parameters in those method calls were tainted. One more question here, as they are not calling the methods directly but are actually creating callbacks, will this callback code be properly displayed as part of the tree? - Lastly, am I missing any other parameters that might cut out some code so that it does not appear in the final call flow tree (or forest, if the things in question 1 can be implemented)? I can attach any files necessary.
Thanks for any information!
This is my POC app:
public static void main(String[] args)
{
String androidJar = "android.jar";
String apkPath = args.length == 0 ? "InsecureBank.apk" : args[0];
InfoflowConfiguration.CallgraphAlgorithm cgAlgorithm = InfoflowConfiguration.CallgraphAlgorithm.SPARK;
final InfoflowAndroidConfiguration config = new InfoflowAndroidConfiguration();
config.getAnalysisFileConfig().setTargetAPKFile(apkPath);
config.getAnalysisFileConfig().setAndroidPlatformDir(androidJar);
config.setCodeEliminationMode(InfoflowConfiguration.CodeEliminationMode.NoCodeElimination);
config.setCallgraphAlgorithm(cgAlgorithm); // SPARK
config.setIgnoreFlowsInSystemPackages(false); // Without this line the onCreate method doesn't have any entries
SetupApplication app = new SetupApplication(config);
app.constructCallgraph();
CallGraph callGraph = Scene.v().getCallGraph();
Chain<SootClass> classes = Scene.v().getClasses();
for (SootClass cls : classes)
{
if (!cls.getName().startsWith("com.android.insecurebank"))
continue;
System.out.println(String.format("Class %s:", cls.getName()));
for (SootMethod sootMethod : cls.getMethods())
{
System.out.println(String.format("\tMethod %s, Phantom: %b", sootMethod.getName(), sootMethod.isPhantom()));
for (Edge e : iteratorToIterable(callGraph.edgesInto(sootMethod)))
{
System.out.println(String.format("\t\tCall graph call from: %s:%s", e.src().getDeclaringClass().getName(), e.src().getName()));
}
for (Edge e : iteratorToIterable(callGraph.edgesOutOf(sootMethod)))
{
System.out.println(String.format("\t\tCall graph call to: %s:%s", e.tgt().getDeclaringClass().getName(), e.tgt().getName()));
}
}
}
}
This is the Jimple method in question 3 (class LoginScreen, method onCreate):
public void onCreate(android.os.Bundle)
{
com.android.insecurebank.LoginScreen r0;
android.os.Bundle $r1;
android.view.View $r2;
android.widget.EditText $r3;
android.widget.Button $r4;
com.android.insecurebank.LoginScreen$1 $r5;
com.android.insecurebank.LoginScreen$2 $r6;
com.android.insecurebank.LoginScreen$3 $r7;
r0 := @this: com.android.insecurebank.LoginScreen;
$r1 := @parameter0: android.os.Bundle;
specialinvoke r0.<android.app.Activity: void onCreate(android.os.Bundle)>($r1);
virtualinvoke r0.<com.android.insecurebank.LoginScreen: void setContentView(int)>(2130903041);
$r2 = virtualinvoke r0.<com.android.insecurebank.LoginScreen: android.view.View findViewById(int)>(2131099649);
$r3 = (android.widget.EditText) $r2;
r0.<com.android.insecurebank.LoginScreen: android.widget.EditText Username_Text> = $r3;
$r2 = virtualinvoke r0.<com.android.insecurebank.LoginScreen: android.view.View findViewById(int)>(2131099650);
$r3 = (android.widget.EditText) $r2;
r0.<com.android.insecurebank.LoginScreen: android.widget.EditText Password_Text> = $r3;
$r2 = virtualinvoke r0.<com.android.insecurebank.LoginScreen: android.view.View findViewById(int)>(2131099652);
$r4 = (android.widget.Button) $r2;
r0.<com.android.insecurebank.LoginScreen: android.widget.Button Login_Button> = $r4;
$r2 = virtualinvoke r0.<com.android.insecurebank.LoginScreen: android.view.View findViewById(int)>(2131099654);
$r4 = (android.widget.Button) $r2;
r0.<com.android.insecurebank.LoginScreen: android.widget.Button Preferences> = $r4;
$r2 = virtualinvoke r0.<com.android.insecurebank.LoginScreen: android.view.View findViewById(int)>(2131099653);
$r4 = (android.widget.Button) $r2;
r0.<com.android.insecurebank.LoginScreen: android.widget.Button Fill_Data> = $r4;
$r4 = r0.<com.android.insecurebank.LoginScreen: android.widget.Button Login_Button>;
$r5 = new com.android.insecurebank.LoginScreen$1;
specialinvoke $r5.<com.android.insecurebank.LoginScreen$1: void <init>(com.android.insecurebank.LoginScreen)>(r0);
virtualinvoke $r4.<android.widget.Button: void setOnClickListener(android.view.View$OnClickListener)>($r5);
$r4 = r0.<com.android.insecurebank.LoginScreen: android.widget.Button Fill_Data>;
$r6 = new com.android.insecurebank.LoginScreen$2;
specialinvoke $r6.<com.android.insecurebank.LoginScreen$2: void <init>(com.android.insecurebank.LoginScreen)>(r0);
virtualinvoke $r4.<android.widget.Button: void setOnClickListener(android.view.View$OnClickListener)>($r6);
$r4 = r0.<com.android.insecurebank.LoginScreen: android.widget.Button Preferences>;
$r7 = new com.android.insecurebank.LoginScreen$3;
specialinvoke $r7.<com.android.insecurebank.LoginScreen$3: void <init>(com.android.insecurebank.LoginScreen)>(r0);
virtualinvoke $r4.<android.widget.Button: void setOnClickListener(android.view.View$OnClickListener)>($r7);
return;
}
This is the output I'm seeing:
Class com.android.insecurebank.LoginScreen:
Method <init>, Phantom: false
Call graph call from: dummyMainClass:dummyMainMethod_com_android_insecurebank_LoginScreen
Call graph call to: android.app.Activity:<init>
Method access$0, Phantom: false
Method access$1, Phantom: false
Method dologin, Phantom: false
Method fill_data, Phantom: false
Method onCreate, Phantom: false
Call graph call from: dummyMainClass:dummyMainMethod_com_android_insecurebank_LoginScreen
Call graph call to: com.android.insecurebank.LoginScreen$3:<init>
Call graph call to: android.app.Activity:findViewById
Call graph call to: android.app.Activity:findViewById
Call graph call to: android.app.Activity:findViewById
Call graph call to: android.app.Activity:findViewById
Call graph call to: android.app.Activity:findViewById
Call graph call to: android.app.Activity:setContentView
Call graph call to: android.app.Activity:onCreate
Call graph call to: com.android.insecurebank.LoginScreen$1:<init>
Call graph call to: com.android.insecurebank.LoginScreen$2:<init>
Method rememberme, Phantom: false
Method setpref, Phantom: false
Method getIntent, Phantom: false
Method setIntent, Phantom: false
Method setResult, Phantom: false
EDIT: Uh, only now I've realised this might be similar to #335 but it doesn't have any answers yet, too.