From 4f197877caca0cda7586bd5947c9839a0afda40b Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Tue, 5 Nov 2024 16:01:34 +0100 Subject: [PATCH] check smali --- android_class_shadowing_scanner/__init__.py | 107 +++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/android_class_shadowing_scanner/__init__.py b/android_class_shadowing_scanner/__init__.py index 8be090a..df2df0d 100644 --- a/android_class_shadowing_scanner/__init__.py +++ b/android_class_shadowing_scanner/__init__.py @@ -2,12 +2,16 @@ import zipfile import io import hashlib import json +import sqlite3 +import tempfile +import subprocess from argparse import ArgumentParser from pathlib import Path from getpass import getpass from datetime import datetime + from .androzoo import download_apk from .data import ApkData, load_from_directory from .analysis import analyze @@ -130,7 +134,7 @@ def main(): print(entry.to_string()) else: with (args.output_dir / sha256).open("w") as file: - file.write(entry) + file.write(entry.to_string()) if apks: if args.json: @@ -231,3 +235,104 @@ def collect_to_db(): ) args = parser.parse_args() load_from_directory(args.dir, args.db, args.androzoo_list) + + +def check_smali(): + parser = ArgumentParser( + prog="Smalli Check", + description="Check if duplicated classes have distinct smali", + ) + parser.add_argument( + "--db", + help="Path to the database storing the results", + type=Path, + required=True, + ) + key_parser = parser.add_mutually_exclusive_group(required=False) + key_parser.add_argument( + "--api-key-file", + help="The path to a file containing the Androzoo API key", + type=Path, + ) + key_parser.add_argument( + "--api-key", help="The Androzoo API key (Usage NOT recommanded)", type=str + ) + SECRET_STORAGE_IMPORTED = False + try: + import secretstorage + + SECRET_STORAGE_IMPORTED = True + + key_parser.add_argument( + "--api-key-keyring-id", + help="The ID of the Androzoo API key in the secret service storage", + type=str, + ) + except ModuleNotFoundError: + pass + args = parser.parse_args() + + api_key = "" + if args.api_key: + api_key = args.api_key + if args.api_key_file: + with args.api_key_file.open("r") as file: + api_key = file.read().strip() + if SECRET_STORAGE_IMPORTED and not api_key: + if args.api_key_keyring_id: + key_id = args.api_key_keyring_id + else: + key_id = "androzoo" + try: + with secretstorage.dbus_init() as connection: + collection = secretstorage.get_default_collection(connection) + item = next(collection.search_items({"Title": key_id})) + item.unlock() + api_key = item.get_secret().decode("utf-8").strip() + except: + pass + if not api_key: + api_key = getpass(prompt="Androzoo API key: ").strip() + + with sqlite3.connect(args.db) as conn: + apks = list( + map( + lambda t: t[0], + conn.execute("SELECT sha256 FROM data WHERE nb_duplicate_classes >= 1"), + ) + ) + data = {} + for sha256 in apks: + with tempfile.TemporaryDirectory() as tmpdirname: + d = Path(tmpdirname) + apk_bin = download_apk(sha256, api_key, logfile=None) + if apk_bin is None: + continue + with (d / "app.apk").open("wb") as fp: + fp.write(apk_bin) + with zipfile.ZipFile(io.BytesIO(apk_bin)) as apk: + json_data[sha256] = {} + entry = analyze(apk, sha256, json_out=json_data[sha256]) + subprocess.run(["apktool", "d", "app.apk", "-o", "apktool_out"], cwd=d) + smalli_dirs = [] + for dex in json_data[sha256]["class_dex"]: + if dex == "classes.dex": + smalli_dirs.append(out / "apktool_out" / "smali") + else: + smalli_dirs.append( + out / "apktool_out" / "smali_" + dex.removesuffix(".dex") + ) + dist_dup_classes = set() + for cl in json_data[sha256]["duplicated_classes"]: + cl_f = cl.removesuffix(";").removeprefix("L") + ".smali" + smali = None + for cdir in smalli_dirs: + if (cdir / cl_f).exists(): + with (cdir / cl_f).open() as file: + smali_new = file.read() + if smali is None: + smali = smali_new + elif smali != smali_new: + dist_dup_classes.add(cl) + json_data[sha256]["redef_classes"] = list(dist_dup_classes) + print(json.dumps(json_data)) diff --git a/pyproject.toml b/pyproject.toml index 4a14ffd..87171bc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,3 +21,4 @@ build-backend = "poetry.core.masonry.api" [tool.poetry.scripts] scan = 'android_class_shadowing_scanner.__init__:main' collect-scan = 'android_class_shadowing_scanner.__init__:collect_to_db' +check-class-redef = 'android_class_shadowing_scanner.__init__:check_smali'