secure-software-engineering / FlowDroid

FlowDroid Static Data Flow Tracker
GNU Lesser General Public License v2.1
1.02k stars 292 forks source link

(Question)Display SootClass and SootMethod Information for Each Stmt of Taint Path in FlowDroid #719

Closed luoyashuo closed 3 months ago

luoyashuo commented 3 months ago

Hello, I am currently using FlowDroid and would like to print the SootClass and SootMethod information for each Stmt in the Taint Path. This is to better visualize how the Source flows to the Sink. Below is my current code:

public void run(String apkFilePath) throws XmlPullParserException, IOException {
    InfoflowAndroidConfiguration conf = new InfoflowAndroidConfiguration();
    conf.getAnalysisFileConfig().setAndroidPlatformDir(androidDirPath);
    conf.getAnalysisFileConfig().setTargetAPKFile(apkFilePath);
    conf.getAnalysisFileConfig().setSourceSinkFile(sourceSinkFilePath);
    File apk = new File(apkFilePath);
    File droidReport = new File(String.valueOf(Paths.get(apk.getParent()).resolve(apk.getName().replace(".apk","")).resolve("flow-report.xml")));
    File droidDirectory = new File((droidReport.getParent()));
    if(!droidDirectory.exists()){
        droidDirectory.mkdirs();
    }
    conf.getAnalysisFileConfig().setOutputFile(droidReport.getAbsolutePath());
    // 显示TaintPath
    conf.getPathConfiguration().setPathReconstructionMode(InfoflowConfiguration.PathReconstructionMode.Precise);
    conf.setLogSourcesAndSinks(true);
    conf.setMergeDexFiles(true);
    conf.getCallbackConfig().setCallbackAnalyzer(InfoflowAndroidConfiguration.CallbackAnalyzer.Fast);
    conf.getCallbackConfig().setEnableCallbacks(true);
    SetupApplication setup = new SetupApplication(conf);
    setup.setTaintWrapper(new EasyTaintWrapper(taintWrapperFilePath));

    InfoflowResults results = setup.runInfoflow();
    // TODO:Print every stmt in the taint propagation path, and the name of the SootClass and SootMethod where each stmt is located

}

When I run this code, the result is something like:

<DataFlowResults FileFormatVersion="102" TerminationState="Success">
    <!-- omitted for brevity -->
    <TaintPath>
        <PathElement
            Statement="$r1 = virtualinvoke $r2.&lt;java.lang.StringBuilder: java.lang.String toString()&gt;()"
            Method="&lt;com.example.myapplication.MainActivity: java.lang.String step3(java.lang.String)&gt;">
            <AccessPath Value="$r1" Type="java.lang.String" TaintSubFields="true"></AccessPath>
        </PathElement>
        <!-- other PathElements -->
    </TaintPath>
    <!-- omitted for brevity -->
</DataFlowResults>

While each Stmt of the Taint Path (e.g., $r1 = virtualinvoke $r2.<java.lang.StringBuilder: java.lang.String toString()>()) is shown, I cannot acquire which specific Class and Method in the program that the Taint Path is located. I hope to exhibit the complete path of Source propagating to Sink. Could you suggest any approach to achieve this?

BTW, the full result is like following:

<?xml version="1.0" encoding="UTF-8"?>
<DataFlowResults FileFormatVersion="102" TerminationState="Success">
    <Results>
        <Result>
            <Sink
                Statement="specialinvoke r0.&lt;com.example.myapplication.MainActivity: void logData(java.lang.String)&gt;($r2)"
                Method="&lt;com.example.myapplication.MainActivity: void onCreate(android.os.Bundle)&gt;"
                MethodSourceSinkDefinition="&lt;com.example.myapplication.MainActivity: void logData(java.lang.String)&gt;">
                <AccessPath Value="$r2" Type="java.lang.String" TaintSubFields="true"></AccessPath>
            </Sink>
            <Sources>
                <Source
                    Statement="$r1 = specialinvoke r0.&lt;com.example.myapplication.MainActivity: java.lang.String getSensitiveData()&gt;()"
                    Method="&lt;com.example.myapplication.MainActivity: java.lang.String step1(java.lang.String)&gt;"
                    MethodSourceSinkDefinition="&lt;com.example.myapplication.MainActivity: java.lang.String getSensitiveData()&gt;">
                    <AccessPath Value="$r1" Type="java.lang.String" TaintSubFields="true"></AccessPath>
                    <TaintPath>
                        <PathElement
                            Statement="$r1 = specialinvoke r0.&lt;com.example.myapplication.MainActivity: java.lang.String getSensitiveData()&gt;()"
                            Method="&lt;com.example.myapplication.MainActivity: java.lang.String step1(java.lang.String)&gt;">
                            <AccessPath Value="$r1" Type="java.lang.String" TaintSubFields="true"></AccessPath>
                        </PathElement>
                        <PathElement
                            Statement="$r2 = virtualinvoke $r2.&lt;java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)&gt;($r1)"
                            Method="&lt;com.example.myapplication.MainActivity: java.lang.String step1(java.lang.String)&gt;">
                            <AccessPath Value="$r2" Type="java.lang.StringBuilder"
                                TaintSubFields="true"></AccessPath>
                        </PathElement>
                        <PathElement
                            Statement="$r1 = virtualinvoke $r2.&lt;java.lang.StringBuilder: java.lang.String toString()&gt;()"
                            Method="&lt;com.example.myapplication.MainActivity: java.lang.String step1(java.lang.String)&gt;">
                            <AccessPath Value="$r1" Type="java.lang.String" TaintSubFields="true"></AccessPath>
                        </PathElement>
                        <PathElement Statement="return $r1"
                            Method="&lt;com.example.myapplication.MainActivity: java.lang.String step1(java.lang.String)&gt;">
                            <AccessPath Value="$r1" Type="java.lang.String" TaintSubFields="true"></AccessPath>
                        </PathElement>
                        <PathElement
                            Statement="$r2 = virtualinvoke $r2.&lt;java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)&gt;($r1)"
                            Method="&lt;com.example.myapplication.MainActivity: java.lang.String step2(java.lang.String)&gt;">
                            <AccessPath Value="$r2" Type="java.lang.StringBuilder"
                                TaintSubFields="true"></AccessPath>
                        </PathElement>
                        <PathElement
                            Statement="$r2 = virtualinvoke $r2.&lt;java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)&gt;(&quot;&lt;-step2&quot;)"
                            Method="&lt;com.example.myapplication.MainActivity: java.lang.String step2(java.lang.String)&gt;">
                            <AccessPath Value="$r2" Type="java.lang.StringBuilder"
                                TaintSubFields="true"></AccessPath>
                        </PathElement>
                        <PathElement
                            Statement="$r1 = virtualinvoke $r2.&lt;java.lang.StringBuilder: java.lang.String toString()&gt;()"
                            Method="&lt;com.example.myapplication.MainActivity: java.lang.String step2(java.lang.String)&gt;">
                            <AccessPath Value="$r1" Type="java.lang.String" TaintSubFields="true"></AccessPath>
                        </PathElement>
                        <PathElement Statement="return $r1"
                            Method="&lt;com.example.myapplication.MainActivity: java.lang.String step2(java.lang.String)&gt;">
                            <AccessPath Value="$r1" Type="java.lang.String" TaintSubFields="true"></AccessPath>
                        </PathElement>
                        <PathElement
                            Statement="$r2 = virtualinvoke $r2.&lt;java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)&gt;($r1)"
                            Method="&lt;com.example.myapplication.MainActivity: java.lang.String step3(java.lang.String)&gt;">
                            <AccessPath Value="$r2" Type="java.lang.StringBuilder"
                                TaintSubFields="true"></AccessPath>
                        </PathElement>
                        <PathElement
                            Statement="$r2 = virtualinvoke $r2.&lt;java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)&gt;(&quot;&lt;-step3&quot;)"
                            Method="&lt;com.example.myapplication.MainActivity: java.lang.String step3(java.lang.String)&gt;">
                            <AccessPath Value="$r2" Type="java.lang.StringBuilder"
                                TaintSubFields="true"></AccessPath>
                        </PathElement>
                        <PathElement
                            Statement="$r1 = virtualinvoke $r2.&lt;java.lang.StringBuilder: java.lang.String toString()&gt;()"
                            Method="&lt;com.example.myapplication.MainActivity: java.lang.String step3(java.lang.String)&gt;">
                            <AccessPath Value="$r1" Type="java.lang.String" TaintSubFields="true"></AccessPath>
                        </PathElement>
                        <PathElement Statement="return $r1"
                            Method="&lt;com.example.myapplication.MainActivity: java.lang.String step3(java.lang.String)&gt;">
                            <AccessPath Value="$r2" Type="java.lang.String" TaintSubFields="true"></AccessPath>
                        </PathElement>
                        <PathElement
                            Statement="specialinvoke r0.&lt;com.example.myapplication.MainActivity: void logData(java.lang.String)&gt;($r2)"
                            Method="&lt;com.example.myapplication.MainActivity: void onCreate(android.os.Bundle)&gt;">
                            <AccessPath Value="$r2" Type="java.lang.String" TaintSubFields="true"></AccessPath>
                        </PathElement>
                    </TaintPath>
                </Source>
            </Sources>
        </Result>
    </Results>
    <PerformanceData>
        <PerformanceEntry Name="CallgraphConstructionSeconds" Value="10"></PerformanceEntry>
        <PerformanceEntry Name="TotalRuntimeSeconds" Value="11"></PerformanceEntry>
        <PerformanceEntry Name="MaxMemoryConsumption" Value="1462"></PerformanceEntry>
        <PerformanceEntry Name="SourceCount" Value="1"></PerformanceEntry>
        <PerformanceEntry Name="SinkCount" Value="2"></PerformanceEntry>
    </PerformanceData>
</DataFlowResults>

Looking forward to your replies. Best regards, luoyashuo

StevenArzt commented 3 months ago

I am not sure I understand your problem. In the XML, there is a method attribute for each statement on the taint path. What else do you need?

You can try this:

setup.addResultsAvailableHandler(new ResultsAvailableHandler() {

@Override
public void onResultsAvailable(IInfoflowCFG cfg, InfoflowResults results) {
  for (DataFlowResult result : results.getResultSet()) {
    for (Stmt stmt : result.getSource().getPath) {
      System.out.println(icfg.getMethodOf(stmt).getSignature());
    }
  }
}

});
setup.runInfoflow();

I haven't had the time to test it, i.e., I just wrote it down here. You might need to fix typos.

luoyashuo commented 3 months ago

Thank you for your response; it's almost exactly what I needed. To better assist others, here's my code snippet:

    public void run(String apkFilePath) throws XmlPullParserException, IOException, URISyntaxException {
        InfoflowAndroidConfiguration conf = new InfoflowAndroidConfiguration();
        conf.getAnalysisFileConfig().setAndroidPlatformDir(androidDirPath);
        conf.getAnalysisFileConfig().setTargetAPKFile(apkFilePath);
        conf.getAnalysisFileConfig().setSourceSinkFile(sourceSinkFilePath);
        File apk = new File(apkFilePath);
        File droidReport = new File(String.valueOf(Paths.get(apk.getParent()).resolve(apk.getName().replace(".apk","")).resolve("flow-report.xml")));
        File droidDirectory = new File((droidReport.getParent()));
        if(!droidDirectory.exists()){
            droidDirectory.mkdirs();
        }
        conf.getAnalysisFileConfig().setOutputFile(droidReport.getAbsolutePath());
        // display TaintPath
        conf.getPathConfiguration().setPathReconstructionMode(InfoflowConfiguration.PathReconstructionMode.Precise);
        conf.setLogSourcesAndSinks(true);
        conf.setMergeDexFiles(true);
        conf.getCallbackConfig().setCallbackAnalyzer(InfoflowAndroidConfiguration.CallbackAnalyzer.Fast);
        conf.getCallbackConfig().setEnableCallbacks(true);
        SetupApplication setup = new SetupApplication(conf);
        setup.setTaintWrapper(new EasyTaintWrapper(taintWrapperFilePath));
//        setup.setTaintWrapper(new SummaryTaintWrapper(new LazySummaryProvider("summariesManual")));
        ResultsAvailableHandler resultsHandler = new ResultsAvailableHandler() {
            @Override
            public void onResultsAvailable(IInfoflowCFG cfg, InfoflowResults results) {
                // Handle the results
                if (results != null && results.getResults() !=null) {
                    for (ResultSinkInfo sink : results.getResults().keySet()) {
                        System.out.println("Sink: " + sink);
                        for (ResultSourceInfo source : results.getResults().get(sink)) {
                            System.out.println("Source: " + source);
                            if (source.getPath() != null) {
                                int idx = 0;
                                for (Stmt stmt : source.getPath()) {
                                    System.out.println("[Chain]:" + (++idx));
                                    System.out.println("Stmt: " + stmt);

                                    SootMethod sootMethod = cfg.getMethodOf(stmt);
                                    System.out.println("Method: " + sootMethod);

                                    System.out.println("Class: " + sootMethod.getDeclaringClass());

                                    if (sootMethod.hasActiveBody()) {
                                        Body body = sootMethod.getActiveBody();
                                        int lineNumber = 1;
                                        for (Unit unit : body.getUnits()) {
                                            if(stmt.equals(unit)){
                                                System.out.println("\t\tLine in Method: " + lineNumber);
                                            }
                                            System.out.println("\t\tLine "+lineNumber+": "+unit);
                                            lineNumber++;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        };
        setup.addResultsAvailableHandler(resultsHandler);
        InfoflowResults results = setup.runInfoflow();