Logging

AndroLyzeLab comes with a built-in logging object which is directly connected to MongoDB. Internally the logging object is represented as an collections.OrderedDict.

Before you can use it, you have to register the basic structure. This is intended for better comparison of the results afterwards.

If you try to log to some key that hasn’t been registered, an exception will be raised. Therefore you should test your script locally before you run it on a cluster!

During the script development you should use the the static function AndroScript.test() to test if your script runs correctly.

There are three different keys that you can register and/or log to:

  • bool: Registers a boolean key. Will be set to False by default.
  • enum: Register an enumeration key. Will create a list internally which can be used to append iterative.
  • normal: Will register None as default. You can log any JSON Serializable data.

Demo

The logging is pretty self-explenatory. So we don’t have to spend much time here. Just have a look at the logging of this very simple script and the produced output:


# encoding: utf-8

__author__ = "Nils Tobias Schmidt"
__email__ = "schmidt89 at informatik.uni-marburg.de"

from androlyze.model.script.AndroScript import AndroScript

#categories
CAT_CLASS_DETAILS = "class details"
CAT_METHODS = "methods"
CAT_FIELDS = "fields"

class ShowLoggingFuncs(AndroScript):
    ''' Example for demonstrating available logging options and to do some query checks '''

    VERSION = "0.1"

    def _analyze(self, apk, dalvik_vm_format, vm_analysis, gvm_analysis, *args, **kwargs):
        res = self.res

        CAT_UNLOGGED = "category1", "category2", "unlogged"
        CAT_LOGGED = "category1", "category2", "logged"

        res.register_keys(["normal"], *CAT_LOGGED)
        res.register_keys(["normal"], *CAT_UNLOGGED)
        res.register_bool_keys(["bool"], *CAT_LOGGED)
        res.register_bool_keys(["bool"], *CAT_UNLOGGED)
        res.register_enum_keys(["enum"], *CAT_LOGGED)
        res.register_enum_keys(["enum"], *CAT_UNLOGGED)

        res.log("normal", "some value", *CAT_LOGGED)
        res.log_true("bool", *CAT_LOGGED)
        res.log_append_to_enum("enum", "list element", *CAT_LOGGED)

There are a few things to notice. First there is a static and a dynamic part of the result. The categories “apk meta” and “script meta” are part of every result. The dynamic part is the data you log. Second it shows the result layout before and after logging values to it. The layout after registering the basic layout can be seen at (“category1”, “category2”, “unlogged”). The final result after logging some values to it can be seen at (“category1”, “category2”, “logged”).

{
        "apk meta": {
                "package name": "com.myfitnesspal.android",
                "version name": "3.7.3",
                "sha256": "4d2afc03880795a561e8eb762314d135d7a777d50daa72fafbcb64b1cbb7ae4d",
                "import date": "2015-06-20T20:07:49.775000",
                "build_date": "2015-02-09T07:47:10",
                "path": "/home/worker/androlyze/apks/02.03.2015_top_free_4/apps_topselling_free/HEALTH_AND_FITNESS/com.myfitnesspal.android.apk",
                "tag": null
        },
        "script meta": {
                "name": "ShowLoggingFuncs",
                "sha256": "2331b99382b960948d38c7ba85789ca903c02d0e42ddda7603138d413c78e889",
                "analysis date": "2015-06-21T19:26:40.158000",
                "version": "0.1"
        },
        "category1": {
                "category2": {
                        "logged": {
                                "normal": "some value",
                                "bool": true,
                                "enum": [
                                        "list element"
                                ]
                        },
                        "unlogged": {
                                "normal": null,
                                "bool": false,
                                "enum": []
                        }
                }
        }
}

Note

The fact that you need to register a structure doesn’t mean you can’t log to dynamic keys. It just means you have to register the key before you try to log to it!

Using different output formats

You don’t have to use the built-in ResultObject for logging. You can also supply your custom log object and specify a custom file name extension.

Just have a look at the following example which creates a control flow graph and store’s it as .gexf file:


# encoding: utf-8

__author__ = "Nils Tobias Schmidt"
__email__ = "schmidt89 at informatik.uni-marburg.de"

from androlyze.model.script.AndroScript import AndroScript

class GVMAnalysisExample(AndroScript):
    ''' Example that uses the `GVMAnalysis` object from `androguard` to create a graph '''
    
    VERSION = "0.1"
    
    def _analyze(self, apk, dalvik_vm_format, vm_analysis, gvm_analysis, *args, **kwargs):
        self.cres = gvm_analysis.export_to_gexf()

    def custom_result_object(self):
        '''
        Overwrite this method, if you want to use your own result logging framework/object,
        You can supply it here and access it via `self.cres`.
        
        E.g. you could return ("", "txt") for simply logging with a string to a .txt file.
        
        Returns
        -------
        tuple<object, str>
            First argument is the result object you want to use,
            the second is the file name extension used for storage (without a leading point)
        '''
        # Simply use str for logging
        # The first parameter isn't needed at all,
        # because we only set the result at the end of the _analyze method
        return ("", "gexf")
    
    def needs_gvmanalysis(self):
        return True
    

Common mistakes

  • Forgetting to unpack tuple for category usage!

    res.log("key", "value", ("foo", "bar"))
    

instead of

res.log("key", "value", *("foo", "bar"))