Source code for androlyze.model.android.apk.FastApk


# encoding: utf-8

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

import sys
from xml.dom import minidom

from androlyze.loader.exception import CouldNotOpenApk, CouldNotOpenManifest
from androlyze.model.android.Constants import MANIFEST_FILENAME, MANIFEST_NS, \
    MANIFEST_VERSION_NAME, MANIFEST_PACKAGE, MANIFEST_TAG_NAME, COMPILED_APP_CODE
from androlyze.model.android.apk.Apk import Apk
from androlyze.util import Util
from androlyze.model.analysis.result.StaticResultKeys import RESOBJ_APK_META,\
    RESOBJ_APK_META_PACKAGE_NAME, RESOBJ_APK_META_VERSION_NAME,\
    RESOBJ_APK_META_PATH, RESOBJ_APK_META_HASH, RESOBJ_APK_META_IMPORT_DATE,\
    RESOBJ_APK_META_TAG, RESOBJ_APK_META_BUILD_DATE
from datetime import datetime

[docs]class FastApk(Apk, object): ''' Provides a fast way to access the basic attributes of an APK file. See Also -------- http://developer.android.com/guide/topics/manifest/manifest-intro.html ''' def __init__(self, package_name, version_name, path = None, _hash = None, import_date = None, tag = None, size_app_code = 0, build_date = None): ''' Create an apk instance. If the sha digest is not given, it will be calculated by loading the file at the time of the first retrieval. ''' Apk.__init__(self) self.package_name = package_name self.version_name = version_name self.import_date = import_date self.tag = tag self.hash = _hash self.path = path self.size_app_code = size_app_code self.build_date = build_date def __eq__(self, other): if isinstance(other, FastApk): return self is other or self.hash == other.hash return False ############################################################ #--- Other ############################################################ @staticmethod
[docs] def androguard_load_from_path(apk_file_path): ''' Load a FastApk from path with the help of androguard. Notes ----- Androguard cannot read data properly from manifest files which do not have the android prefix. Parameters ---------- apk_file_path: str path of apk Returns ------- apk: FastApk ''' from androguard.core.bytecodes import apk as androapk aapk = androapk.APK(apk_file_path) return FastApk(aapk.get_package(), aapk.get_androidversion_name(), apk_file_path)
@staticmethod
[docs] def fast_load_from_io(file_like_object = None, apk_file_path = None, calculate_hash = True): ''' Load a FastApk from file-like object or path by unzipping only the manifest file and calculating the hash. Parameters ---------- file_like_object : file-like-object, optional (default is None) A file-like obj that points to the apk. If non given, try to open a file_like_object from the given `apk_file_path`. apk_file_path: str, optional (default is "not set") Path of apk calculate_hash : bool If true calculate the hash. This means the file has be to loaded completely into memory. If False, the hash will be calculated the first time it gets retrieved. Returns ------- apk: FastApk Raises ------ CouldNotOpenApk If the apk file could not be opened CouldNotReadManifest If the manifest file could not be read ''' from androguard.core.bytecodes import apk as androapk from androguard.patch import zipfile # apk will be loaded from `flo` variable flo = file_like_object # indicates if file has been opened from path file_open_from_path = False # no file_like_object given, open file from path if file_like_object is None and isinstance(apk_file_path, str): try: flo = open(apk_file_path, "rb") file_open_from_path = True except IOError as e: flo.close() raise CouldNotOpenApk(apk_file_path, e), None, sys.exc_info()[2] # if file path not set, show at least that it's not seen in the exceptions if apk_file_path is None: apk_file_path = "not set" # load apk into memory and calculate hash if option set flo.seek(0) _hash = None if calculate_hash: data = flo.read() # calculate hash _hash = Util.sha256(data) flo.seek(0) try: if zipfile.is_zipfile(flo): z = zipfile.ZipFile(flo) # only read manifest from zip file binary_manifest = z.read(MANIFEST_FILENAME) ap = androapk.AXMLPrinter(binary_manifest) dom = minidom.parseString(ap.get_buff()) manifest_tag = dom.getElementsByTagName(MANIFEST_TAG_NAME) # check that manifest tag is available if len(manifest_tag) > 0: # get size of uncompresses .dex file size_app_code = z.getinfo(COMPILED_APP_CODE).file_size # get build date (last timestamp of classes.dex in zipfile) build_date = datetime( # use tuple from zipfile and pass the unpacked content to the constructor *z.getinfo(COMPILED_APP_CODE).date_time ) manifest_items = manifest_tag[0].attributes # use the namespace to ignore wrong prefixes like "ns0" version_name = manifest_items.getNamedItemNS(MANIFEST_NS, MANIFEST_VERSION_NAME).nodeValue package = manifest_items.getNamedItem(MANIFEST_PACKAGE).nodeValue return FastApk(package, version_name, path = apk_file_path, _hash = _hash, size_app_code = size_app_code, build_date = build_date) raise CouldNotOpenManifest(apk_file_path), None, sys.exc_info()[2] except Exception as e: raise CouldNotOpenApk(apk_file_path, caused_by = e), None, sys.exc_info()[2] finally: # close file if manually opened from path if file_open_from_path: flo.close()
@staticmethod
[docs] def androguard_load_from_io(file_like_object = None, apk_file_path = None, calculate_hash = True): ''' Load a FastApk from file-like object or path by using `androgaurd`. Parameters ---------- file_like_object : file-like-object, optional (default is None) A file-like obj that points to the apk. If non given, try to open a file_like_object from the given `apk_file_path`. apk_file_path: str, optional (default is "not set") Path of apk calculate_hash : bool If true calculate the hash. This means the file has be to loaded completely into memory. If False, the hash will be calculated the first time it gets retrieved. Returns ------- apk: FastApk Raises ------ CouldNotOpenApk If the apk file could not be opened ''' # prevent circular import from androlyze.analyze import AnalyzeUtil # apk will be loaded from `flo` variable flo = file_like_object # indicates if file has been opened from path file_open_from_path = False # no file_like_object given, open file from path if file_like_object is None and isinstance(apk_file_path, str): try: flo = open(apk_file_path, "rb") file_open_from_path = True except IOError as e: flo.close() raise CouldNotOpenApk(apk_file_path, e), None, sys.exc_info()[2] # if file path not set, show at least that it's not seen in the exceptions if apk_file_path is None: apk_file_path = "not set" # load apk into memory and calculate hash if option set flo.seek(0) _hash = None data = flo.read() if calculate_hash: # calculate hash _hash = Util.sha256(data) flo.seek(0) apk = AnalyzeUtil.open_apk(data, raw = True, path = apk_file_path) if file_open_from_path: flo.close() if apk is not None: try: return FastApk.load_from_eandroapk(apk) except KeyError: pass # could not open apk -> raise error raise CouldNotOpenApk(file_path = apk_file_path)
@staticmethod
[docs] def load_from_androguard_apk(andro_apk): ''' Load a `FastApk` from androguard `APK` Parameters ---------- andro_apk: APK (androguard apk) Returns ------- FastApk ''' return FastApk(andro_apk.get_package(), andro_apk.get_androidversion_name(), andro_apk.filename)
@staticmethod
[docs] def load_from_eandroapk(eandro_apk): ''' Load a `FastApk` from an `EAndroApk`. Parameters ---------- eandro_apk: EAndroApk Returns ------- FastApk ''' return FastApk(eandro_apk.package_name, eandro_apk.version_name, path = eandro_apk.path, _hash = eandro_apk.hash, import_date = eandro_apk.import_date, tag = eandro_apk.tag, size_app_code = eandro_apk.size_app_code, build_date = eandro_apk.build_date)
@staticmethod
[docs] def load_from_result_dict(res_dict): ''' Load a `FastApk` from the `res_dict`. Parameters ---------- res_dict : dict See `ResultObject.description_dict` ''' package_name = res_dict[RESOBJ_APK_META][RESOBJ_APK_META_PACKAGE_NAME] version_name = res_dict[RESOBJ_APK_META][RESOBJ_APK_META_VERSION_NAME] path = res_dict[RESOBJ_APK_META][RESOBJ_APK_META_PATH] _hash = res_dict[RESOBJ_APK_META][RESOBJ_APK_META_HASH] import_date = res_dict[RESOBJ_APK_META][RESOBJ_APK_META_IMPORT_DATE] tag = res_dict[RESOBJ_APK_META][RESOBJ_APK_META_TAG] build_date = res_dict[RESOBJ_APK_META][RESOBJ_APK_META_BUILD_DATE] return FastApk(package_name, version_name, path, _hash, import_date, tag, build_date = build_date)
if __name__ == '__main__': import json from androlyze.log.Log import log #from androlyze.model.script.AndroScript import AndroScript #APK_NAME = "/mnt/stuff/android/apks/com.parkdroid.apk" APK_NAME = "/mnt/stuff/androlyze/import/apk/com.whatsapp/2.7.3581/071435b4c72d45593ba64d411463ad18e02cbd3d90296d38f5b42d7e9d96ea9b/com.whatsapp_2.7.3581.apk" try: with open(APK_NAME, "rb") as f: print FastApk.fast_load_from_io(file_like_object = f) apk = FastApk.fast_load_from_io(file_like_object = None, apk_file_path = APK_NAME) apk.tag = "exploitable" print apk print json.dumps(apk.meta_dict(), indent = 4) print apk #res.log_true("check1", "checks") #res.log_true("check2", "checks") #storage.store_result_for_apk(apk, AndroScript("script1"), res) # androguard cannot load some apk files which the fast loader can except Exception as e: log.exception(e) print "done"