From d0bf33375b3c4b97ffb371225b19592b89cb88cd Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Tue, 12 Nov 2024 17:17:48 +0100 Subject: [PATCH] add hidden --- android_class_shadowing_scanner/__init__.py | 54 +++++++++++++++---- android_class_shadowing_scanner/analysis.py | 37 +++++++++++-- .../platform_classes.py | 2 + 3 files changed, 81 insertions(+), 12 deletions(-) diff --git a/android_class_shadowing_scanner/__init__.py b/android_class_shadowing_scanner/__init__.py index 7df3bc6..926610e 100644 --- a/android_class_shadowing_scanner/__init__.py +++ b/android_class_shadowing_scanner/__init__.py @@ -56,6 +56,16 @@ def main(): help="The directory where to output results, when no set results are printed to stdout", type=Path, ) + parser.add_argument( + "--output-dir-sdk34-classes", + help="SDK 34 redefinition", + type=Path, + ) + parser.add_argument( + "--output-dir-hidden-api", + help="Reference to hidden api", + type=Path, + ) parser.add_argument( "--json", help="Print the results in json format with additionnal data", @@ -95,6 +105,18 @@ def main(): raise RuntimeError("--output-dir must be a directory") args.output_dir.mkdir(parents=True, exist_ok=True) + if args.output_dir_sdk34_classes: + if not args.output_dir_sdk34_classes.exists(): + args.output_dir_sdk34_classes.mkdir(parents=True) + if not args.output_dir_sdk34_classes.is_dir(): + raise RuntimeError("--output-dir-sdk34-classes must be a directory") + + if args.output_dir_hidden_api: + if not args.output_dir_hidden_api.exists(): + args.output_dir_hidden_api.mkdir(parents=True) + if not args.output_dir_hidden_api.is_dir(): + raise RuntimeError("--output-dir-hidden-api must be a directory") + # Case 1: apk from file apks = [] if args.apk: @@ -121,7 +143,13 @@ def main(): json_out = json_data[sha256] else: json_out = None - entry = analyze(apk, sha256, json_out=json_out) + entry = analyze( + apk, + sha256, + json_out=json_out, + sdk34_dir=args.output_dir_sdk34_classes, + hidden_dir=args.output_dir_hidden_api, + ) except Exception as e: log = f"[{datetime.today().strftime('%Y-%m-%d %H:%M:%S')}] Failed to analyzed {sha256}: {e}, abort" if logfile: @@ -186,7 +214,13 @@ def main(): json_out = json_data[sha256] else: json_out = None - entry = analyze(apk, sha256, json_out=json_out) + entry = analyze( + apk, + sha256, + json_out=json_out, + sdk34_dir=args.output_dir_sdk34_classes, + hidden_dir=args.output_dir_hidden_api, + ) except Exception as e: log = f"[{datetime.today().strftime('%Y-%m-%d %H:%M:%S')}] Failed to analyzed {sha256}: {e}, abort" if logfile: @@ -307,13 +341,14 @@ def check_smali(): 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"), - ) - ) + # 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"), + # ) + # ) + apks = ["E0467A3E79C344216EEEC9691E43C49DCE3230EB312979F7DC37AAC829077249"] data = {} for sha256 in apks: with tempfile.TemporaryDirectory() as tmpdirname: @@ -356,6 +391,7 @@ def check_smali(): smali = None for cdir in smalli_dirs: if (cdir / cl_f).exists(): + print((cdir / cl_f)) with (cdir / cl_f).open() as file: smali_new = file.read() if smali is None: diff --git a/android_class_shadowing_scanner/analysis.py b/android_class_shadowing_scanner/analysis.py index 03a3c08..d623d28 100644 --- a/android_class_shadowing_scanner/analysis.py +++ b/android_class_shadowing_scanner/analysis.py @@ -3,6 +3,7 @@ import io from dataclasses import dataclass, asdict from enum import IntEnum +from pathlib import Path import androguard.core.dex # type: ignore from androguard.core.dex import DEX # type: ignore @@ -78,7 +79,11 @@ class PlatformClassesData: def scan_classes( - apk: zipfile.ZipFile, file_names: set[str], json_out: dict | None = None + apk: zipfile.ZipFile, + file_names: set[str], + json_out: dict | None = None, + sdk34_classes_file: Path | None = None, + hidden_file: Path | None = None, ) -> PlatformClassesData: all_classes = set() duplicated_classes = set() @@ -175,10 +180,22 @@ def scan_classes( data["sdk_34_classes"] = list(sdk_34_classes) data["platform_non_sdk_34_classes"] = list(platform_non_sdk_34_classes) data["ref_platform_non_sdk_34_classes"] = list(ref_platform_non_sdk_34_classes) + if sdk34_classes_file is not None: + with sdk34_classes_file.open("w") as file: + file.writelines(sorted(sdk_34_classes)) + if hidden_file is not None: + with hidden_file.open("w") as file: + file.writelines(sorted(ref_platform_non_sdk_34_classes)) return entry -def analyze(apk: zipfile.ZipFile, sha256: str, json_out: dict | None = None) -> ApkData: +def analyze( + apk: zipfile.ZipFile, + sha256: str, + json_out: dict | None = None, + sdk34_dir: Path | None = None, + hidden_dir: Path | None = None, +) -> ApkData: classes_dex = set( filter( lambda name: name.startswith("classes") and name.endswith(".dex"), @@ -226,7 +243,21 @@ def analyze(apk: zipfile.ZipFile, sha256: str, json_out: dict | None = None) -> has_non_consecutive_classes_dex = True break - platform_classes_data = scan_classes(apk, classes_dex, json_out=json_out) + if sdk34_dir: + sdk34_classes_file = sdk34_dir / sha256 + else: + sdk34_classes_file = None + if hidden_dir: + hidden_file = hidden_dir / sha256 + else: + hidden_file = None + platform_classes_data = scan_classes( + apk, + classes_dex, + json_out=json_out, + sdk34_classes_file=sdk34_classes_file, + hidden_file=hidden_file, + ) entry = ApkData( sha256=sha256, diff --git a/android_class_shadowing_scanner/platform_classes.py b/android_class_shadowing_scanner/platform_classes.py index 3624d1d..ac3e2a2 100644 --- a/android_class_shadowing_scanner/platform_classes.py +++ b/android_class_shadowing_scanner/platform_classes.py @@ -17,6 +17,8 @@ D8_CLASSES = { "Ldalvik/annotation/EnclosingClass;", "Ldalvik/annotation/Throws;", "Ldalvik/annotation/MemberClasses;", + "Ldalvik/annotation/AnnotationDefault;", + "Ldalvik/annotation/MethodParameters;", } with (local_dir / "android-32" / "classes.txt").open() as file: