add androzoo support

This commit is contained in:
Jean-Marie Mineau 2024-10-17 14:57:08 +02:00
parent 43eef100f1
commit 01abb1879d
6 changed files with 465 additions and 206 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
__pycache__

View file

@ -1,214 +1,84 @@
import zipfile
import io
from dataclasses import dataclass, asdict
from enum import IntEnum
from argparse import ArgumentParser
from pathlib import Path
from getpass import getpass
import androguard.core.dex # type: ignore
from androguard.core.dex import DEX # type: ignore
# loguru -> logging framework used by androgard
from loguru import logger # type: ignore
from .platform_classes import (
PLATFORM_32_CLASSES,
SDK_32_CLASSES,
PLATFORM_33_CLASSES,
SDK_33_CLASSES,
PLATFORM_34_CLASSES,
SDK_34_CLASSES,
)
# Remove Androguard logs
logger.remove()
# Patch Androguard
class PatchedDomapiApiFlag(IntEnum):
NONE = 0
CORE_PLATFORM_API = 1
TEST_API = 2
UNKN_3 = 3
UNKN_4 = 4
UNKN_5 = 5
UNKN_6 = 6
UNKN_7 = 7
UNKN_8 = 8
UNKN_9 = 9
INKN_10 = 10
class PatchedRestrictionApiFlag(IntEnum):
WHITELIST = 0
GREYLIST = 1
BLACKLIST = 2
GREYLIST_MAX_O = 3
GREYLIST_MAX_P = 4
GREYLIST_MAX_Q = 5
GREYLIST_MAX_R = 6
GREYLIST_MAX_7 = 7
GREYLIST_MAX_8 = 8
GREYLIST_MAX_9 = 9
GREYLIST_MAX_10 = 10
androguard.core.dex.HiddenApiClassDataItem.DomapiApiFlag = PatchedDomapiApiFlag
androguard.core.dex.HiddenApiClassDataItem.RestrictionApiFlag = (
PatchedRestrictionApiFlag
)
@dataclass
class Entry:
# 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
class PlatformClassesData:
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
def scan_classes(apk: zipfile.ZipFile, file_names: set[str]) -> PlatformClassesData:
all_classes = set()
duplicated_classes = set()
platform_32_classes = set()
sdk_32_classes = set()
platform_non_sdk_32_classes = set()
platform_33_classes = set()
sdk_33_classes = set()
platform_non_sdk_33_classes = set()
platform_34_classes = set()
sdk_34_classes = set()
platform_non_sdk_34_classes = set()
for name in file_names:
with apk.open(name) as dex_f:
dex = DEX(dex_f.read())
for clazz in map(lambda c: c.name, dex.get_classes()):
if clazz in all_classes:
duplicated_classes.add(clazz)
if clazz in PLATFORM_32_CLASSES:
platform_32_classes.add(clazz)
if clazz in SDK_32_CLASSES:
sdk_32_classes.add(clazz)
if clazz in PLATFORM_32_CLASSES and clazz not in SDK_32_CLASSES:
platform_non_sdk_32_classes.add(clazz)
if clazz in PLATFORM_33_CLASSES:
platform_33_classes.add(clazz)
if clazz in SDK_33_CLASSES:
sdk_33_classes.add(clazz)
if clazz in PLATFORM_33_CLASSES and clazz not in SDK_33_CLASSES:
platform_non_sdk_33_classes.add(clazz)
if clazz in PLATFORM_34_CLASSES:
platform_34_classes.add(clazz)
if clazz in SDK_34_CLASSES:
sdk_34_classes.add(clazz)
if clazz in PLATFORM_34_CLASSES and clazz not in SDK_34_CLASSES:
platform_non_sdk_34_classes.add(clazz)
all_classes.add(clazz)
return PlatformClassesData(
nb_duplicate_classes=len(duplicated_classes),
nb_platform_32_classes=len(platform_32_classes),
nb_platform_non_sdk_32_classes=len(platform_non_sdk_32_classes),
nb_sdk_32_classes=len(sdk_32_classes),
nb_platform_33_classes=len(platform_33_classes),
nb_platform_non_sdk_33_classes=len(platform_non_sdk_33_classes),
nb_sdk_33_classes=len(sdk_33_classes),
nb_platform_34_classes=len(platform_34_classes),
nb_platform_non_sdk_34_classes=len(platform_non_sdk_34_classes),
nb_sdk_34_classes=len(sdk_34_classes),
)
def analyze(apk: zipfile.ZipFile) -> Entry:
classes_dex = set(
filter(
lambda name: name.startswith("classes") and name.endswith(".dex"),
apk.namelist(),
)
)
dex_numbers = list(
map(
int,
filter(
lambda string: string not in ("", "1")
and string.isnumeric()
and string[0] != "0",
map(
lambda name: name.removeprefix("classes").removesuffix(".dex"),
classes_dex,
),
),
)
)
dex_numbers.sort()
has_non_numeric_classes_dex = False
for name in classes_dex:
if name == "classes.dex":
continue
if not name.removeprefix("classes").removesuffix(".dex").isnumeric():
has_non_numeric_classes_dex = True
has_non_consecutive_classes_dex = False
if "classes.dex" in classes_dex and dex_numbers:
has_non_consecutive_classes_dex = True
last_number = 1
for i in range(len(dex_numbers)):
if dex_numbers[i] == 0 or dex_numbers[i] == 1:
continue
# the list is sorted
if dex_numbers[i] != last_number + 1:
has_non_consecutive_classes_dex = True
break
platform_classes_data = scan_classes(apk, classes_dex)
return Entry(
**asdict(platform_classes_data),
has_classes0_dex="classes0.dex" in classes_dex,
has_classes1_dex="classes1.dex" in classes_dex,
has_classes0X_dex=any(
map(lambda name: name.startswith("classes0"), classes_dex)
),
has_classes_dex_over_10=any(map(lambda x: x >= 10, dex_numbers)),
has_non_numeric_classes_dex=has_non_numeric_classes_dex,
has_non_consecutive_classes_dex=has_non_consecutive_classes_dex,
)
from .androzoo import download_apk
from .analysis import analyze
def main():
import sys
import pprint
with open(sys.argv[1], "rb") as file:
apk = file.read()
parser = ArgumentParser(
prog="Android Class Shadowing Scanner",
description="Scan application for patern that could be reveling of class shadowing obfuscation",
)
apk_parser = parser.add_mutually_exclusive_group(required=True)
apk_parser.add_argument(
"--sha256", help="The sha256 hash of the APK to download", type=str
)
apk_parser.add_argument("--apk", help="The APK to use", type=Path)
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
with zipfile.ZipFile(io.BytesIO(apk)) as apk:
entry = analyze(apk)
SECRET_STORAGE_IMPORTED = True
pprint.pprint(entry)
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()
if args.apk:
with args.apk.open("rb") as file:
with zipfile.ZipFile(file) as apk:
entry = analyze(apk)
pprint.pprint(entry)
exit()
sha256s = []
if args.sha256:
sha256s.append(args.sha256)
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()
for sha256 in sha256s:
with zipfile.ZipFile(io.BytesIO(download_apk(sha256, api_key))) as apk:
entry = analyze(apk, sha256)
pprint.pprint(entry)

View file

@ -0,0 +1,202 @@
import zipfile
import io
from dataclasses import dataclass, asdict
from enum import IntEnum
import androguard.core.dex # type: ignore
from androguard.core.dex import DEX # type: ignore
# loguru -> logging framework used by androgard
from loguru import logger # type: ignore
from .platform_classes import (
PLATFORM_32_CLASSES,
SDK_32_CLASSES,
PLATFORM_33_CLASSES,
SDK_33_CLASSES,
PLATFORM_34_CLASSES,
SDK_34_CLASSES,
)
# Remove Androguard logs
logger.remove()
# Patch Androguard
class PatchedDomapiApiFlag(IntEnum):
NONE = 0
CORE_PLATFORM_API = 1
TEST_API = 2
UNKN_3 = 3
UNKN_4 = 4
UNKN_5 = 5
UNKN_6 = 6
UNKN_7 = 7
UNKN_8 = 8
UNKN_9 = 9
INKN_10 = 10
class PatchedRestrictionApiFlag(IntEnum):
WHITELIST = 0
GREYLIST = 1
BLACKLIST = 2
GREYLIST_MAX_O = 3
GREYLIST_MAX_P = 4
GREYLIST_MAX_Q = 5
GREYLIST_MAX_R = 6
GREYLIST_MAX_7 = 7
GREYLIST_MAX_8 = 8
GREYLIST_MAX_9 = 9
GREYLIST_MAX_10 = 10
androguard.core.dex.HiddenApiClassDataItem.DomapiApiFlag = PatchedDomapiApiFlag
androguard.core.dex.HiddenApiClassDataItem.RestrictionApiFlag = (
PatchedRestrictionApiFlag
)
@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
class PlatformClassesData:
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
def scan_classes(apk: zipfile.ZipFile, file_names: set[str]) -> PlatformClassesData:
all_classes = set()
duplicated_classes = set()
platform_32_classes = set()
sdk_32_classes = set()
platform_non_sdk_32_classes = set()
platform_33_classes = set()
sdk_33_classes = set()
platform_non_sdk_33_classes = set()
platform_34_classes = set()
sdk_34_classes = set()
platform_non_sdk_34_classes = set()
for name in file_names:
with apk.open(name) as dex_f:
dex = DEX(dex_f.read())
for clazz in map(lambda c: c.name, dex.get_classes()):
if clazz in all_classes:
duplicated_classes.add(clazz)
if clazz in PLATFORM_32_CLASSES:
platform_32_classes.add(clazz)
if clazz in SDK_32_CLASSES:
sdk_32_classes.add(clazz)
if clazz in PLATFORM_32_CLASSES and clazz not in SDK_32_CLASSES:
platform_non_sdk_32_classes.add(clazz)
if clazz in PLATFORM_33_CLASSES:
platform_33_classes.add(clazz)
if clazz in SDK_33_CLASSES:
sdk_33_classes.add(clazz)
if clazz in PLATFORM_33_CLASSES and clazz not in SDK_33_CLASSES:
platform_non_sdk_33_classes.add(clazz)
if clazz in PLATFORM_34_CLASSES:
platform_34_classes.add(clazz)
if clazz in SDK_34_CLASSES:
sdk_34_classes.add(clazz)
if clazz in PLATFORM_34_CLASSES and clazz not in SDK_34_CLASSES:
platform_non_sdk_34_classes.add(clazz)
all_classes.add(clazz)
return PlatformClassesData(
nb_duplicate_classes=len(duplicated_classes),
nb_platform_32_classes=len(platform_32_classes),
nb_platform_non_sdk_32_classes=len(platform_non_sdk_32_classes),
nb_sdk_32_classes=len(sdk_32_classes),
nb_platform_33_classes=len(platform_33_classes),
nb_platform_non_sdk_33_classes=len(platform_non_sdk_33_classes),
nb_sdk_33_classes=len(sdk_33_classes),
nb_platform_34_classes=len(platform_34_classes),
nb_platform_non_sdk_34_classes=len(platform_non_sdk_34_classes),
nb_sdk_34_classes=len(sdk_34_classes),
)
def analyze(apk: zipfile.ZipFile, sha256: str) -> ApkData:
classes_dex = set(
filter(
lambda name: name.startswith("classes") and name.endswith(".dex"),
apk.namelist(),
)
)
dex_numbers = list(
map(
int,
filter(
lambda string: string not in ("", "1")
and string.isnumeric()
and string[0] != "0",
map(
lambda name: name.removeprefix("classes").removesuffix(".dex"),
classes_dex,
),
),
)
)
dex_numbers.sort()
has_non_numeric_classes_dex = False
for name in classes_dex:
if name == "classes.dex":
continue
if not name.removeprefix("classes").removesuffix(".dex").isnumeric():
has_non_numeric_classes_dex = True
has_non_consecutive_classes_dex = False
if "classes.dex" in classes_dex and dex_numbers:
has_non_consecutive_classes_dex = True
last_number = 1
for i in range(len(dex_numbers)):
if dex_numbers[i] == 0 or dex_numbers[i] == 1:
continue
# the list is sorted
if dex_numbers[i] != last_number + 1:
has_non_consecutive_classes_dex = True
break
platform_classes_data = scan_classes(apk, classes_dex)
return ApkData(
sha256=sha256,
**asdict(platform_classes_data),
has_classes0_dex="classes0.dex" in classes_dex,
has_classes1_dex="classes1.dex" in classes_dex,
has_classes0X_dex=any(
map(lambda name: name.startswith("classes0"), classes_dex)
),
has_classes_dex_over_10=any(map(lambda x: x >= 10, dex_numbers)),
has_non_numeric_classes_dex=has_non_numeric_classes_dex,
has_non_consecutive_classes_dex=has_non_consecutive_classes_dex,
)

View file

@ -0,0 +1,10 @@
import http.client
def download_apk(sha256: str, api_key: str) -> bytes:
conn = http.client.HTTPSConnection("androzoo.uni.lu")
conn.request("GET", f"/api/download?apikey={api_key}&sha256={sha256}")
resp = conn.getresponse()
if resp.status != 200:
raise RuntimeError(f"Failled to download APK {sha256}: {resp.reason}")
return resp.read()

174
poetry.lock generated
View file

@ -102,6 +102,85 @@ files = [
[package.extras]
dev = ["mypy", "wheel"]
[[package]]
name = "cffi"
version = "1.17.1"
description = "Foreign Function Interface for Python calling C code."
optional = true
python-versions = ">=3.8"
files = [
{file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"},
{file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"},
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"},
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"},
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"},
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"},
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"},
{file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"},
{file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"},
{file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"},
{file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"},
{file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"},
{file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"},
{file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"},
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"},
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"},
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"},
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"},
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"},
{file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"},
{file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"},
{file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"},
{file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"},
{file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"},
{file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"},
{file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"},
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"},
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"},
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"},
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"},
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"},
{file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"},
{file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"},
{file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"},
{file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"},
{file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"},
{file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"},
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"},
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"},
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"},
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"},
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"},
{file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"},
{file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"},
{file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"},
{file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"},
{file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"},
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"},
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"},
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"},
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"},
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"},
{file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"},
{file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"},
{file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"},
{file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"},
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"},
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"},
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"},
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"},
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"},
{file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"},
{file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"},
{file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"},
{file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"},
{file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"},
{file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"},
]
[package.dependencies]
pycparser = "*"
[[package]]
name = "click"
version = "8.1.7"
@ -211,6 +290,55 @@ mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.11.1)", "types-Pil
test = ["Pillow", "contourpy[test-no-images]", "matplotlib"]
test-no-images = ["pytest", "pytest-cov", "pytest-rerunfailures", "pytest-xdist", "wurlitzer"]
[[package]]
name = "cryptography"
version = "43.0.1"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
optional = true
python-versions = ">=3.7"
files = [
{file = "cryptography-43.0.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d"},
{file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062"},
{file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962"},
{file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277"},
{file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a"},
{file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042"},
{file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494"},
{file = "cryptography-43.0.1-cp37-abi3-win32.whl", hash = "sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2"},
{file = "cryptography-43.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d"},
{file = "cryptography-43.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d"},
{file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806"},
{file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85"},
{file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c"},
{file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1"},
{file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa"},
{file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4"},
{file = "cryptography-43.0.1-cp39-abi3-win32.whl", hash = "sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47"},
{file = "cryptography-43.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb"},
{file = "cryptography-43.0.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ea25acb556320250756e53f9e20a4177515f012c9eaea17eb7587a8c4d8ae034"},
{file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d"},
{file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289"},
{file = "cryptography-43.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5b43d1ea6b378b54a1dc99dd8a2b5be47658fe9a7ce0a58ff0b55f4b43ef2b84"},
{file = "cryptography-43.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:88cce104c36870d70c49c7c8fd22885875d950d9ee6ab54df2745f83ba0dc365"},
{file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9d3cdb25fa98afdd3d0892d132b8d7139e2c087da1712041f6b762e4f807cc96"},
{file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e710bf40870f4db63c3d7d929aa9e09e4e7ee219e703f949ec4073b4294f6172"},
{file = "cryptography-43.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7c05650fe8023c5ed0d46793d4b7d7e6cd9c04e68eabe5b0aeea836e37bdcec2"},
{file = "cryptography-43.0.1.tar.gz", hash = "sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d"},
]
[package.dependencies]
cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""}
[package.extras]
docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"]
docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"]
nox = ["nox"]
pep8test = ["check-sdist", "click", "mypy", "ruff"]
sdist = ["build"]
ssh = ["bcrypt (>=3.1.5)"]
test = ["certifi", "cryptography-vectors (==43.0.1)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
test-randomorder = ["pytest-randomly"]
[[package]]
name = "cycler"
version = "0.12.1"
@ -504,6 +632,21 @@ docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alab
qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"]
[[package]]
name = "jeepney"
version = "0.8.0"
description = "Low-level, pure Python DBus protocol wrapper."
optional = true
python-versions = ">=3.7"
files = [
{file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"},
{file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"},
]
[package.extras]
test = ["async-timeout", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"]
trio = ["async_generator", "trio"]
[[package]]
name = "kiwisolver"
version = "1.4.7"
@ -1256,6 +1399,17 @@ files = [
[package.extras]
tests = ["pytest"]
[[package]]
name = "pycparser"
version = "2.22"
description = "C parser in Python"
optional = true
python-versions = ">=3.8"
files = [
{file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
]
[[package]]
name = "pydot"
version = "3.0.2"
@ -1379,6 +1533,21 @@ files = [
{file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
]
[[package]]
name = "secretstorage"
version = "3.3.3"
description = "Python bindings to FreeDesktop.org Secret Service API"
optional = true
python-versions = ">=3.6"
files = [
{file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"},
{file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"},
]
[package.dependencies]
cryptography = ">=2.0"
jeepney = ">=0.6"
[[package]]
name = "six"
version = "1.16.0"
@ -1537,7 +1706,10 @@ files = [
[package.extras]
dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
[extras]
secretstorage = ["SecretStorage"]
[metadata]
lock-version = "2.0"
python-versions = "^3.12"
content-hash = "b53b77f57e118a7dc8649764dff18035672596a44b2be1983d3cbe17e1d28bc2"
content-hash = "17b8e803d4fecdffce0e19b557cedc226cc67d501c8f86b8228af513d9dfc3e3"

View file

@ -7,9 +7,13 @@ readme = "README.md"
[tool.poetry.dependencies]
python = "^3.12"
androguard = "^4.1.2"
SecretStorage = { version = "^3.3.3", optional = true }
[tool.poetry.extras]
secretstorage = ["SecretStorage"]
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"