add saving options
This commit is contained in:
parent
01abb1879d
commit
fe3c16de35
3 changed files with 61 additions and 24 deletions
|
|
@ -6,12 +6,11 @@ from pathlib import Path
|
||||||
from getpass import getpass
|
from getpass import getpass
|
||||||
|
|
||||||
from .androzoo import download_apk
|
from .androzoo import download_apk
|
||||||
|
from .data import ApkData
|
||||||
from .analysis import analyze
|
from .analysis import analyze
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
import pprint
|
|
||||||
|
|
||||||
parser = ArgumentParser(
|
parser = ArgumentParser(
|
||||||
prog="Android Class Shadowing Scanner",
|
prog="Android Class Shadowing Scanner",
|
||||||
description="Scan application for patern that could be reveling of class shadowing obfuscation",
|
description="Scan application for patern that could be reveling of class shadowing obfuscation",
|
||||||
|
|
@ -21,6 +20,11 @@ def main():
|
||||||
"--sha256", help="The sha256 hash of the APK to download", type=str
|
"--sha256", help="The sha256 hash of the APK to download", type=str
|
||||||
)
|
)
|
||||||
apk_parser.add_argument("--apk", help="The APK to use", type=Path)
|
apk_parser.add_argument("--apk", help="The APK to use", type=Path)
|
||||||
|
apk_parser.add_argument(
|
||||||
|
"--sha256-list",
|
||||||
|
help="A file containing a list of application sha256s (one by line)",
|
||||||
|
type=Path,
|
||||||
|
)
|
||||||
key_parser = parser.add_mutually_exclusive_group(required=False)
|
key_parser = parser.add_mutually_exclusive_group(required=False)
|
||||||
key_parser.add_argument(
|
key_parser.add_argument(
|
||||||
"--api-key-file",
|
"--api-key-file",
|
||||||
|
|
@ -30,6 +34,12 @@ def main():
|
||||||
key_parser.add_argument(
|
key_parser.add_argument(
|
||||||
"--api-key", help="The Androzoo API key (Usage NOT recommanded)", type=str
|
"--api-key", help="The Androzoo API key (Usage NOT recommanded)", type=str
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--output-dir",
|
||||||
|
help="The directory where to output results, when no set results are printed to stdout",
|
||||||
|
type=Path,
|
||||||
|
)
|
||||||
|
|
||||||
SECRET_STORAGE_IMPORTED = False
|
SECRET_STORAGE_IMPORTED = False
|
||||||
try:
|
try:
|
||||||
import secretstorage
|
import secretstorage
|
||||||
|
|
@ -45,6 +55,11 @@ def main():
|
||||||
pass
|
pass
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.output_dir:
|
||||||
|
if not args.output_dir.is_dir():
|
||||||
|
raise RuntimeError("--output-dir must be a directory")
|
||||||
|
args.output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
if args.apk:
|
if args.apk:
|
||||||
with args.apk.open("rb") as file:
|
with args.apk.open("rb") as file:
|
||||||
with zipfile.ZipFile(file) as apk:
|
with zipfile.ZipFile(file) as apk:
|
||||||
|
|
@ -55,6 +70,10 @@ def main():
|
||||||
sha256s = []
|
sha256s = []
|
||||||
if args.sha256:
|
if args.sha256:
|
||||||
sha256s.append(args.sha256)
|
sha256s.append(args.sha256)
|
||||||
|
if args.sha256_list:
|
||||||
|
with args.sha256_list.open("r") as file:
|
||||||
|
for line in file:
|
||||||
|
sha256s.append(line.strip())
|
||||||
|
|
||||||
api_key = ""
|
api_key = ""
|
||||||
if args.api_key:
|
if args.api_key:
|
||||||
|
|
@ -79,6 +98,12 @@ def main():
|
||||||
api_key = getpass(prompt="Androzoo API key: ").strip()
|
api_key = getpass(prompt="Androzoo API key: ").strip()
|
||||||
|
|
||||||
for sha256 in sha256s:
|
for sha256 in sha256s:
|
||||||
|
if args.output_dir and (args.output_dir / sha256).exists():
|
||||||
|
continue
|
||||||
with zipfile.ZipFile(io.BytesIO(download_apk(sha256, api_key))) as apk:
|
with zipfile.ZipFile(io.BytesIO(download_apk(sha256, api_key))) as apk:
|
||||||
entry = analyze(apk, sha256)
|
entry = analyze(apk, sha256)
|
||||||
pprint.pprint(entry)
|
if not args.output_dir:
|
||||||
|
print(entry.to_string())
|
||||||
|
else:
|
||||||
|
with (args.output_dir / sha256).open("w") as file:
|
||||||
|
file.write(entry)
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ from .platform_classes import (
|
||||||
PLATFORM_34_CLASSES,
|
PLATFORM_34_CLASSES,
|
||||||
SDK_34_CLASSES,
|
SDK_34_CLASSES,
|
||||||
)
|
)
|
||||||
|
from .data import ApkData
|
||||||
|
|
||||||
# Remove Androguard logs
|
# Remove Androguard logs
|
||||||
logger.remove()
|
logger.remove()
|
||||||
|
|
@ -58,27 +59,6 @@ androguard.core.dex.HiddenApiClassDataItem.RestrictionApiFlag = (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ApkData:
|
|
||||||
sha256: str
|
|
||||||
nb_duplicate_classes: int
|
|
||||||
nb_platform_32_classes: int
|
|
||||||
nb_platform_non_sdk_32_classes: int
|
|
||||||
nb_sdk_32_classes: int
|
|
||||||
nb_platform_33_classes: int
|
|
||||||
nb_platform_non_sdk_33_classes: int
|
|
||||||
nb_sdk_33_classes: int
|
|
||||||
nb_platform_34_classes: int
|
|
||||||
nb_platform_non_sdk_34_classes: int
|
|
||||||
nb_sdk_34_classes: int
|
|
||||||
has_classes0_dex: bool
|
|
||||||
has_classes1_dex: bool
|
|
||||||
has_classes0X_dex: bool
|
|
||||||
has_classes_dex_over_10: bool
|
|
||||||
has_non_numeric_classes_dex: bool
|
|
||||||
has_non_consecutive_classes_dex: bool
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PlatformClassesData:
|
class PlatformClassesData:
|
||||||
nb_duplicate_classes: int
|
nb_duplicate_classes: int
|
||||||
|
|
|
||||||
32
android_class_shadowing_scanner/data.py
Normal file
32
android_class_shadowing_scanner/data.py
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
from dataclasses import dataclass, astuple, fields
|
||||||
|
from typing import Self
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ApkData:
|
||||||
|
sha256: str
|
||||||
|
nb_duplicate_classes: int
|
||||||
|
nb_platform_32_classes: int
|
||||||
|
nb_platform_non_sdk_32_classes: int
|
||||||
|
nb_sdk_32_classes: int
|
||||||
|
nb_platform_33_classes: int
|
||||||
|
nb_platform_non_sdk_33_classes: int
|
||||||
|
nb_sdk_33_classes: int
|
||||||
|
nb_platform_34_classes: int
|
||||||
|
nb_platform_non_sdk_34_classes: int
|
||||||
|
nb_sdk_34_classes: int
|
||||||
|
has_classes0_dex: bool
|
||||||
|
has_classes1_dex: bool
|
||||||
|
has_classes0X_dex: bool
|
||||||
|
has_classes_dex_over_10: bool
|
||||||
|
has_non_numeric_classes_dex: bool
|
||||||
|
has_non_consecutive_classes_dex: bool
|
||||||
|
|
||||||
|
def to_string(self) -> str:
|
||||||
|
return "|".join(map(str, astuple(self)))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_string(val: str) -> "ApkData":
|
||||||
|
return ApkData(
|
||||||
|
*(map(lambda f_v: f_v[1] == "True" if f_v[0].type is bool else f_v[0].type(f_v[1]), zip(fields(ApkData), val.strip().split("|")))) # type: ignore
|
||||||
|
)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue