Integrate Exodus (Closes: #566, #1008)

Code taken from:

https://github.com/Exodus-Privacy/exodus-core/blob/v1/exodus_core/analysis/static_analysis.py
This commit is contained in:
Jochen Sprickerhof 2022-06-01 17:24:32 +02:00 committed by Michael Pöhn
parent ea9299f216
commit 3bd09ef7f4

View file

@ -24,11 +24,14 @@ import sys
import traceback import traceback
import zipfile import zipfile
from argparse import ArgumentParser from argparse import ArgumentParser
from collections import namedtuple
from copy import deepcopy from copy import deepcopy
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
import logging import logging
import itertools import itertools
import requests
from . import _ from . import _
from . import common from . import common
from . import metadata from . import metadata
@ -140,7 +143,45 @@ def get_embedded_classes(apkfile, depth=0):
return classes return classes
def scan_binary(apkfile): # taken from exodus_core
def _compile_signatures(signatures):
"""
Compiles the regex associated to each signature, in order to speed up the trackers detection.
:return: A compiled list of signatures.
"""
compiled_tracker_signature = []
try:
compiled_tracker_signature = [
re.compile(track.code_signature) for track in signatures
]
except TypeError:
print("signatures is not iterable")
return compiled_tracker_signature
# taken from exodus_core
def load_trackers_signatures():
"""
Load trackers signatures from the official Exodus database.
:return: a dictionary containing signatures.
"""
signatures = []
exodus_url = "https://reports.exodus-privacy.eu.org/api/trackers"
r = requests.get(exodus_url)
data = r.json()
for e in data['trackers']:
signatures.append(
namedtuple('tracker', data['trackers'][e].keys())(
*data['trackers'][e].values()
)
)
logging.debug('{} trackers signatures loaded'.format(len(signatures)))
return signatures, _compile_signatures(signatures)
def scan_binary(apkfile, extract_signatures=None):
"""Scan output of dexdump for known non-free classes.""" """Scan output of dexdump for known non-free classes."""
logging.info(_('Scanning APK with dexdump for known non-free classes.')) logging.info(_('Scanning APK with dexdump for known non-free classes.'))
result = get_embedded_classes(apkfile) result = get_embedded_classes(apkfile)
@ -150,6 +191,29 @@ def scan_binary(apkfile):
if regexp.match(classname): if regexp.match(classname):
logging.debug("Found class '%s'" % classname) logging.debug("Found class '%s'" % classname)
problems += 1 problems += 1
if extract_signatures:
def _detect_tracker(sig, tracker, class_list):
for clazz in class_list:
if sig.search(clazz):
return tracker
return None
results = []
args = [(extract_signatures[1][index], tracker, result)
for (index, tracker) in enumerate(extract_signatures[0]) if
len(tracker.code_signature) > 3]
for res in itertools.starmap(_detect_tracker, args):
if res:
results.append(res)
trackers = [t for t in results if t is not None]
for tracker in trackers:
logging.debug("Found tracker {}".format(tracker.code_signature))
problems += len(trackers)
if problems: if problems:
logging.critical("Found problems in %s" % apkfile) logging.critical("Found problems in %s" % apkfile)
return problems return problems
@ -454,6 +518,11 @@ def main():
) )
common.setup_global_opts(parser) common.setup_global_opts(parser)
parser.add_argument("appid", nargs='*', help=_("application ID with optional versionCode in the form APPID[:VERCODE]")) parser.add_argument("appid", nargs='*', help=_("application ID with optional versionCode in the form APPID[:VERCODE]"))
parser.add_argument(
"--exodus",
action="store_true",
help="Use tracker scanner from Exodus project (requires internet)",
)
parser.add_argument("-f", "--force", action="store_true", default=False, parser.add_argument("-f", "--force", action="store_true", default=False,
help=_("Force scan of disabled apps and builds.")) help=_("Force scan of disabled apps and builds."))
parser.add_argument("--json", action="store_true", default=False, parser.add_argument("--json", action="store_true", default=False,
@ -473,10 +542,14 @@ def main():
probcount = 0 probcount = 0
exodus = []
if options.exodus:
exodus = load_trackers_signatures()
appids = [] appids = []
for apk in options.appid: for apk in options.appid:
if os.path.isfile(apk): if os.path.isfile(apk):
count = scan_binary(apk) count = scan_binary(apk, exodus)
if count > 0: if count > 0:
logging.warning( logging.warning(
_('Scanner found {count} problems in {apk}:').format( _('Scanner found {count} problems in {apk}:').format(