diff --git a/objection/commands/mobile_packages.py b/objection/commands/mobile_packages.py index ad3b61ac..603e0a91 100644 --- a/objection/commands/mobile_packages.py +++ b/objection/commands/mobile_packages.py @@ -97,11 +97,12 @@ def patch_ios_ipa(source: str, codesign_signature: str, provision_file: str, bin def patch_android_apk(source: str, architecture: str, pause: bool, skip_cleanup: bool = True, - enable_debug: bool = True, gadget_version: str = None, skip_resources: bool = False, + enable_debug: bool = True, gadget_version: str = None, decode_resources: bool = False, network_security_config: bool = False, target_class: str = None, - use_aapt2: bool = False, gadget_config: str = None, script_source: str = None, + use_aapt1: bool = False, gadget_name: str = 'libfrida-gadget.so', + gadget_config: str = None, script_source: str = None, ignore_nativelibs: bool = True, manifest: str = None, skip_signing: bool = False, - only_main_classes: bool = False, fix_concurrency_to = None) -> None: + only_main_classes: bool = False, fix_concurrency_to = None, lief: bool = False) -> None: """ Patches an Android APK by extracting, patching SMALI, repackaging and signing a new APK. @@ -112,10 +113,11 @@ def patch_android_apk(source: str, architecture: str, pause: bool, skip_cleanup: :param skip_cleanup: :param enable_debug: :param gadget_version: - :param skip_resources: + :param decode_resources: :param network_security_config: :param target_class: - :param use_aapt2: + :param use_aapt1: + :param gadget_name: :param gadget_config: :param script_source: :param manifest: @@ -123,10 +125,10 @@ def patch_android_apk(source: str, architecture: str, pause: bool, skip_cleanup: :param ignore_nativelibs: :param only_main_classes: :param fix_concurrency_to: + :param lief: :return: """ - github = Github(gadget_version=gadget_version) android_gadget = AndroidGadget(github) @@ -180,7 +182,7 @@ def patch_android_apk(source: str, architecture: str, pause: bool, skip_cleanup: click.secho('Patcher will be using Gadget version: {0}'.format(github_version), fg='green') - patcher = AndroidPatcher(skip_cleanup=skip_cleanup, skip_resources=skip_resources, manifest=manifest, only_main_classes=only_main_classes) + patcher = AndroidPatcher(skip_cleanup=skip_cleanup, decode_resources=decode_resources, manifest=manifest, only_main_classes=only_main_classes) # ensure that we have all of the commandline requirements if not patcher.are_requirements_met(): @@ -205,8 +207,13 @@ def patch_android_apk(source: str, architecture: str, pause: bool, skip_cleanup: if network_security_config: patcher.add_network_security_config() - patcher.inject_load_library(target_class=target_class) - patcher.add_gadget_to_apk(architecture, android_gadget.get_frida_library_path(), gadget_config) + patcher.add_gadget_to_apk( + architecture, + android_gadget.get_frida_library_path(), + gadget_config, + gadget_name + ) + patcher.inject_load_library(target_class=target_class, use_lief=lief) if script_source: click.secho('Copying over a custom script to use with the gadget config.', fg='green') @@ -223,7 +230,7 @@ def patch_android_apk(source: str, architecture: str, pause: bool, skip_cleanup: input('Press ENTER to continue...') - patcher.build_new_apk(use_aapt2=use_aapt2, fix_concurrency_to=fix_concurrency_to) + patcher.build_new_apk(use_aapt1=use_aapt1, fix_concurrency_to=fix_concurrency_to) patcher.zipalign_apk() if not skip_signing: patcher.sign_apk() diff --git a/objection/console/cli.py b/objection/console/cli.py index ff490b49..5e227f90 100644 --- a/objection/console/cli.py +++ b/objection/console/cli.py @@ -299,14 +299,20 @@ def patchipa(source: str, gadget_version: str, codesign_signature: str, provisio help='Set the android:debuggable flag to true in the application manifest.', show_default=True) @click.option('--network-security-config', '-N', is_flag=True, default=False, help='Include a network_security_config.xml file allowing for user added CA\'s to be trusted on ' - 'Android 7 and up. This option can not be used with the --skip-resources flag.') -@click.option('--skip-resources', '-D', is_flag=True, default=False, - help='Skip resource decoding as part of the apktool processing.', show_default=False) + 'Android 7 and up. This option requires the --decode-resources flag.') +@click.option('--decode-resources', '-D', is_flag=True, default=True, + help='Decode resource as part of the apktool processing.', show_default=False) @click.option('--skip-signing', '-C', is_flag=True, default=False, help='Skip signing the apk file.', show_default=False) @click.option('--target-class', '-t', help='The target class to patch.', default=None) -@click.option('--use-aapt2', '-2', is_flag=True, default=False, - help='Use the aapt2 binary instead of aapt as part of the apktool processing.', show_default=False) +@click.option('--use-aapt1', '-1', is_flag=True, default=False, + help='Use the aapt binary instead of aapt2 as part of the apktool processing.', show_default=True) +@click.option('--gadget-name', '-g', default='libfrida-gadget.so', + help=( + 'Name of the gadget library. Can be named whatever you want to dodge anti-frida ' + 'detection schemes looking for loaded libraries with frida in the name.' + 'Refer to https://frida.re/docs/gadget/ for more information.'), + show_default=True) @click.option('--gadget-config', '-c', default=None, help=( 'The gadget configuration file to use. ' 'Refer to https://frida.re/docs/gadget/ for more information.'), show_default=False) @@ -318,27 +324,39 @@ def patchipa(source: str, gadget_version: str, codesign_signature: str, provisio @click.option('--manifest', '-m', help='A decoded AndroidManifest.xml file to read.', default=None) @click.option('--only-main-classes', help="Only patch classes that are in the main dex file.", is_flag=True, default=False) @click.option('--fix-concurrency-to', '-j', help="Only use N threads for repackaging - set to 1 if running into OOM errors.", default=None) +@click.option('--lief', is_flag=True, default=False, + help='Use LIEF to patch existing native libraries instead of Smali patching.', show_default=False) def patchapk(source: str, architecture: str, gadget_version: str, pause: bool, skip_cleanup: bool, - enable_debug: bool, skip_resources: bool, network_security_config: bool, target_class: str, - use_aapt2: bool, gadget_config: str, script_source: str, ignore_nativelibs: bool, manifest: str, skip_signing: bool, only_main_classes:bool = False, fix_concurrency_to = None) -> None: + enable_debug: bool, decode_resources: bool, network_security_config: bool, target_class: str, + use_aapt1: bool, gadget_name: str, gadget_config: str, script_source: str, ignore_nativelibs: bool, manifest: str, skip_signing: bool, only_main_classes:bool = False, fix_concurrency_to = None, lief: bool = False) -> None: """ Patch an APK with the frida-gadget.so. """ # ensure we decode resources if we have the network-security-config flag. - if network_security_config and skip_resources: - click.secho('The --network-security-config flag is incompatible with the --skip-resources flag.', fg='red') + if network_security_config and not decode_resources: + click.secho('The --network-security-config flag requires the --decode-resources flag.', fg='red') return # ensure we decode resources if we have the enable-debug flag. - if enable_debug and skip_resources: - click.secho('The --enable-debug flag is incompatible with the --skip-resources flag.', fg='red') + if enable_debug and not decode_resources: + click.secho('The --enable-debug flag is incompatible with the --decode-resources flag.', fg='red') + return + + # ensure we decode libs if we have the --lief flag. + if ignore_nativelibs and lief: + click.secho('The --ignore-nativelibs cannot be used when --lief is specified.', fg='red') + return + + # ensure we decode resources if we have the --decode-resources flag. + if not ignore_nativelibs and not decode_resources: + click.secho('The --ignore-nativelibs flag cannot be used when --decode-resources is specified.', fg='red') return - # ensure we decode resources if we do not have the --ignore-nativelibs flag. - if not ignore_nativelibs and skip_resources: - click.secho('The --ignore-nativelibs flag is required with the --skip-resources flag.', fg='red') + # ensure provided gadget name is a valid android lib name + if not gadget_name.startswith('lib') or not gadget_name.endswith('.so'): + click.secho("Gadget name should start with 'lib' and end in '.so'", fg='red') return patch_android_apk(**locals()) diff --git a/objection/utils/patchers/android.py b/objection/utils/patchers/android.py index ea1881d4..c8bdcad4 100644 --- a/objection/utils/patchers/android.py +++ b/objection/utils/patchers/android.py @@ -1,4 +1,5 @@ import contextlib +import functools import lzma import os import re @@ -8,6 +9,7 @@ import click import delegator +import lief import requests import semver @@ -200,7 +202,7 @@ class AndroidPatcher(BasePlatformPatcher): } } - def __init__(self, skip_cleanup: bool = False, skip_resources: bool = False, manifest: str = None, only_main_classes: bool = False): + def __init__(self, skip_cleanup: bool = False, decode_resources: bool = False, manifest: str = None, only_main_classes: bool = False): super(AndroidPatcher, self).__init__() self.apk_source = None @@ -209,9 +211,11 @@ def __init__(self, skip_cleanup: bool = False, skip_resources: bool = False, man self.apk_temp_frida_patched_aligned = self.apk_temp_directory + '.aligned.objection.apk' self.aapt = None self.skip_cleanup = skip_cleanup - self.skip_resources = skip_resources + self.decode_resources = decode_resources self.manifest = manifest + self.architecture = None + self.keystore = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../assets', 'objection.jks') self.netsec_config = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../assets', 'network_security_config.xml') @@ -289,11 +293,11 @@ def _get_android_manifest(self) -> ElementTree: :return: """ - # error if --skip-resources was used because the manifest is encoded - if self.skip_resources is True and self.manifest is None: - click.secho('Cannot manually parse the AndroidManifest.xml when --skip-resources ' - 'is set, remove this and try again, or manually specify a manifest with --manifest.', fg='red') - raise Exception('Cannot --skip-resources when trying to manually parse the AndroidManifest.xml') + # error if --decode-resources was not used because the manifest is encoded + if not self.decode_resources is True and self.manifest is None: + click.secho('Cannot manually parse the AndroidManifest.xml when --decoode-resources ' + 'is not set, add this and try again, or manually specify a manifest with --manifest.', fg='red') + raise Exception('Cannot --decode-resources when trying to manually parse the AndroidManifest.xml') # use the android namespace ElementTree.register_namespace('android', 'http://schemas.android.com/apk/res/android') @@ -412,7 +416,7 @@ def unpack_apk(self, fix_concurrency_to = None): 'decode', '-f', ] + - (['-r'] if self.skip_resources else []) + + (['-r'] if not self.decode_resources else []) + (['--only-main-classes'] if self.only_main_classes else []) + [ '-o', @@ -813,15 +817,44 @@ def _h(): return patched_smali - def inject_load_library(self, target_class: str = None): + @functools.cache + def _find_libs_path(self): + """ + Find the libraries path for the target architecture within the APK. + """ + base_libs_path = os.path.join(self.apk_temp_directory, 'lib') + available_libs_arch = os.listdir(base_libs_path) + if self.architecture in available_libs_arch: + # Exact match with arch + return os.path.join(base_libs_path, self.architecture) + else: + # Try to use prefix search + try: + matching_arch = next( + item for item in available_libs_arch if item.startswith(self.architecture) + ) + click.secho('Using matching architecture {0} from provided architecture {1}.'.format( + matching_arch, self.architecture + ), dim=True) + return os.path.join(base_libs_path, matching_arch) + except StopIteration: + # Might create the arch folder inside the APK tree + return os.path.join(base_libs_path, self.architecture) + + def inject_load_library(self, target_class: str = None, use_lief: bool = False): """ Injects a loadLibrary call into a class. If a target class is not specified, we will make an attempt at searching for a launchable activity in the target APK. + + If use_lief is True, will attempt to patch existing native libraries + using LIEF instead of smali patching. Most of the idea for this comes from: https://koz.io/using-frida-on-android-without-root/ + :param target_class: Target class to patch (optional) + :param use_lief: Whether to use LIEF for patching native libraries :return: """ @@ -832,9 +865,67 @@ def inject_load_library(self, target_class: str = None): if target_class: click.secho('Using target class: {0} for patch'.format(target_class), fg='green', bold=True) - else: - click.secho('Target class not specified, searching for launchable activity instead...', fg='green', + elif use_lief: + click.secho('Target class not specified, injecting through existing native libraries...', fg='green', bold=True) + # Inspired by https://fadeevab.com/frida-gadget-injection-on-android-no-root-2-methods/ + if not self.architecture or not self.libfridagadget_name: + raise Exception('Frida-gadget should have been copied prior to injecting!') + libs_path = self._find_libs_path() + existing_libs_in_apk = [ + lib + for lib in os.listdir(libs_path) + if lib not in [self.libfridagadget_name, self.libfridagadgetconfig_name] + ] + if existing_libs_in_apk: + # Present a selection of native libraries to patch + click.secho('Found {0} native libraries in APK:'.format(len(existing_libs_in_apk)), fg='cyan') + + # Display all available libraries with indices + for i, lib in enumerate(existing_libs_in_apk, 1): + click.secho(' {0}. {1}'.format(i, lib), fg='white') + + # Add option for all libraries + click.secho(' {0}. All libraries'.format(len(existing_libs_in_apk) + 1), fg='black', bold=True) + + # Get user selection + while True: + try: + choice = click.prompt( + 'Select which library to patch (number)', + type=int, + default=len(existing_libs_in_apk) + 1 + ) + if 1 <= choice <= len(existing_libs_in_apk): + # Single library selected + selected_lib = existing_libs_in_apk[choice - 1] + click.secho('Patching library: {0}'.format(selected_lib), fg='green', bold=True) + libnative = lief.parse(os.path.join(libs_path, selected_lib)) + libnative.add_library(self.libfridagadget_name) # Injection! + libnative.write(os.path.join(libs_path, selected_lib)) + break + elif choice == len(existing_libs_in_apk) + 1: + # All libraries selected + click.secho('Patching all libraries...', fg='green', bold=True) + for lib in existing_libs_in_apk: + click.secho(' Patching: {0}'.format(lib), fg='green') + libnative = lief.parse(os.path.join(libs_path, lib)) + libnative.add_library(self.libfridagadget_name) # Injection! + libnative.write(os.path.join(libs_path, lib)) + break + else: + click.secho('Invalid selection. Please choose a number between 1 and {0}.'.format( + len(existing_libs_in_apk) + 1), fg='red') + except (ValueError, click.Abort): + click.secho('Invalid input. Please enter a number.', fg='red') + + return + else: + click.secho('No native libraries found in APK, searching for launchable activity instead...', fg='green', + bold=True) + else: + # When no target class is specified and LIEF is not used, search for launchable activity + click.secho('Target class not specified, searching for launchable activity...', fg='green', bold=True) activity_path = self._determine_smali_path_for_class( target_class if target_class else self._get_launchable_activity()) @@ -864,7 +955,9 @@ def inject_load_library(self, target_class: str = None): with open(activity_path, 'w') as f: f.write(''.join(patched_smali)) - def add_gadget_to_apk(self, architecture: str, gadget_source: str, gadget_config: str): + def add_gadget_to_apk(self, architecture: str, + gadget_source: str, gadget_config: str, + libfridagadget_name: str = 'libfrida-gadget.so'): """ Copies a frida gadget for a specific architecture to an extracted APK's lib path. @@ -872,10 +965,14 @@ def add_gadget_to_apk(self, architecture: str, gadget_source: str, gadget_config :param architecture: :param gadget_source: :param gadget_config: + :param libfridagadget_name: :return: """ + self.architecture = architecture + self.libfridagadget_name = libfridagadget_name + self.libfridagadgetconfig_name = libfridagadget_name.replace('.so', '.config.so') - libs_path = os.path.join(self.apk_temp_directory, 'lib', architecture) + libs_path = self._find_libs_path() # check if the libs path exists if not os.path.exists(libs_path): @@ -883,13 +980,13 @@ def add_gadget_to_apk(self, architecture: str, gadget_source: str, gadget_config os.makedirs(libs_path) click.secho('Copying Frida gadget to libs path...', fg='green', dim=True) - shutil.copyfile(gadget_source, os.path.join(libs_path, 'libfrida-gadget.so')) + shutil.copyfile(gadget_source, os.path.join(libs_path, self.libfridagadget_name)) if gadget_config: click.secho('Adding a gadget configuration file...', fg='green') - shutil.copyfile(gadget_config, os.path.join(libs_path, 'libfrida-gadget.config.so')) + shutil.copyfile(gadget_config, os.path.join(libs_path, self.libfridagadgetconfig_name)) - def build_new_apk(self, use_aapt2: bool = False, fix_concurrency_to = None): + def build_new_apk(self, use_aapt1: bool = False, fix_concurrency_to = None): """ Build a new .apk with the frida-gadget patched in. @@ -901,7 +998,7 @@ def build_new_apk(self, use_aapt2: bool = False, fix_concurrency_to = None): self.list2cmdline([self.required_commands['apktool']['location'], 'build', self.apk_temp_directory, - ] + (['--use-aapt2'] if use_aapt2 else []) + [ + ] + (['--use-aapt1'] if use_aapt1 else []) + [ '-o', self.apk_temp_frida_patched ]+ ([] if fix_concurrency_to is None else ['-j', fix_concurrency_to])) diff --git a/pyproject.toml b/pyproject.toml index b8a6ffd8..e15f5557 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,7 @@ dependencies = [ "flask>=3.0.0", "frida>=16.0.0", "frida-tools>=10.0.0", + "lief>=0.17.1", "litecli>=1.3.0", "packaging>=23.0", "prompt-toolkit>=3.0.30,<4.0.0", diff --git a/uv.lock b/uv.lock index a3623250..3f6c98cf 100644 --- a/uv.lock +++ b/uv.lock @@ -453,6 +453,66 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2f/9c/6753e6522b8d0ef07d3a3d239426669e984fb0eba15a315cdbc1253904e4/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24e864cb30ab82311c6425655b0cdab0a98c5d973b065c66a3f020740c2324c", size = 346110, upload-time = "2025-11-09T20:49:21.817Z" }, ] +[[package]] +name = "lief" +version = "0.17.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/2e/46df19b190a620d30ccac8f49db445a0e84300144270e1d5be3cc320867f/lief-0.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:55f6122716c50fb1eabc73b5f36baa35667a6d900214a84aada3c493af6de5cc", size = 2986779, upload-time = "2025-10-25T13:14:11.204Z" }, + { url = "https://files.pythonhosted.org/packages/e5/8a/aa30c445eb09a1526e59151bf1ce25be30f2998abe2bc6579d2e96025343/lief-0.17.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:64637d926607919082fadbeca784e09f59e8fc03ae527cc06f82bfd644c4a12a", size = 3096043, upload-time = "2025-10-25T13:14:13.928Z" }, + { url = "https://files.pythonhosted.org/packages/b2/ab/6114bb50d683368e6bd0ccd9f3a7a6e39cd09658e708c80764a2c52fd1b0/lief-0.17.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:d2b7dfb2523b57824abf7d8f951899a00042bf618e73fee8eb2d451fc4744f3b", size = 3668042, upload-time = "2025-10-25T13:14:15.913Z" }, + { url = "https://files.pythonhosted.org/packages/44/fe/2e0f95b4dffec3615991e79e21bf5d9e15f0ad8f2d40436b8e2f5e4c7d0c/lief-0.17.1-cp310-cp310-manylinux_2_28_i686.whl", hash = "sha256:f22ba9d271d174f973036ce99d07954b96020517a1ced479e6b5686059b8a682", size = 3494657, upload-time = "2025-10-25T13:14:17.807Z" }, + { url = "https://files.pythonhosted.org/packages/fe/f8/e91b269c7e4db991551da237ae0b052cf333789d2d76b146d54ef2a22cf9/lief-0.17.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:e4150376dccc792cf9f1bdd54149e3a8191a2e7e458ac434352e47a93890c157", size = 3395767, upload-time = "2025-10-25T13:14:19.477Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2d/22b0bf2fad7fbfae61a6437cca0d45c3515e08d8836931b3586b5359859d/lief-0.17.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1058bdd99be989075585daa7517b1da58e2c2a98e7cb3c0f95123d3f2240ccb5", size = 3588230, upload-time = "2025-10-25T13:14:21.273Z" }, + { url = "https://files.pythonhosted.org/packages/ff/9b/5e2af3a878b81dffe9f20497b0f6aee6aa62c3072495db292a26ca748930/lief-0.17.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:96b301eac113e211d58c12a58357c50a5a403f076c4788458b58b23bcdfe9f6b", size = 3950449, upload-time = "2025-10-25T13:14:22.868Z" }, + { url = "https://files.pythonhosted.org/packages/0c/a4/d85c3e8f2fceb2ba5f5ecbd36af28edfe2f13df3c77b4c2bc1ed2418f1bf/lief-0.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4cb22f259cb44832fb279769818cbec50814b80c0df0a9eb08ffe25f1087076a", size = 3697949, upload-time = "2025-10-25T13:14:24.43Z" }, + { url = "https://files.pythonhosted.org/packages/d7/99/369641adb09e4facb617a7dd9b3d3165d223b5813f841ba514751b63725e/lief-0.17.1-cp310-cp310-win32.whl", hash = "sha256:dea013fb64dbf84523f19adf1a8ceac8d69582d72c297320af8ff2123204620c", size = 3450293, upload-time = "2025-10-25T13:14:25.823Z" }, + { url = "https://files.pythonhosted.org/packages/b2/72/d2ca0d547283d02066016bfe9726ff3590735a3b4e8efab93c681fff7b76/lief-0.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:d6d60f6c866d287e37af155aeb356142fe106f603528e7ea2b984529eb1af4e6", size = 3626321, upload-time = "2025-10-25T13:14:27.253Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c7/ae80d842df622f97b551e698baa5a14f8c5c42598072f3eba8ed22b79465/lief-0.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:632c6ff6b0a7a87349eed334f16e9bb81ce8b455bfa35371965e606c88813ee8", size = 2994396, upload-time = "2025-10-25T13:14:29.637Z" }, + { url = "https://files.pythonhosted.org/packages/01/dd/d592bc4757a411754b6fe948d3f318a4236aee869da6de838613fb3fb3eb/lief-0.17.1-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:8195890069b3baee3aeaff22f9cc26b7ed3aba711f16d0e7824eb8c0eaca7183", size = 3097004, upload-time = "2025-10-25T13:14:31.074Z" }, + { url = "https://files.pythonhosted.org/packages/3a/94/b6078176aa4763508c965928126461381aff83fdb45cb61be488a6313fbd/lief-0.17.1-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d3eb2a7e77d956a8d88caf6def2974d751c9c7ff0a82e981e147446945a7256e", size = 3665322, upload-time = "2025-10-25T13:14:32.535Z" }, + { url = "https://files.pythonhosted.org/packages/0c/36/ba0ec61634cbf5d76f0a116194dc164dd3495a6c4f2d8abf6439edc002d1/lief-0.17.1-cp311-cp311-manylinux_2_28_i686.whl", hash = "sha256:99eb7b3e2d947e455b23b128ec6e6e85d160a5e6efaa8370250f2d6324b7470a", size = 3495108, upload-time = "2025-10-25T13:14:34.44Z" }, + { url = "https://files.pythonhosted.org/packages/19/5e/70980325ad63d04d0d74a645345fbd56b357ab3ff1aacf4fdf967b68a37c/lief-0.17.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:702f612fdf9627fd7bd9a0af4f3ea5d437479a03d4f5872c1f250038a228466a", size = 3395697, upload-time = "2025-10-25T13:14:35.808Z" }, + { url = "https://files.pythonhosted.org/packages/0e/64/7f2b2acb98650e117c14cae809310f2de5a26d6dffe0e1dec7035839bbe7/lief-0.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a5419f3ad5011716a46d55637961fa6f39188dccd43f00f1043328e1a34df391", size = 3587887, upload-time = "2025-10-25T13:14:37.259Z" }, + { url = "https://files.pythonhosted.org/packages/89/fe/1f2243d3e0c64e651ac2844085e238dab4c7b05a6601eb3b4e6952aa3b6e/lief-0.17.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d3db7bded3d9f4742a1f98fdee7dea14702d943ef7afd817714801610cc284f1", size = 3950889, upload-time = "2025-10-25T13:14:39.265Z" }, + { url = "https://files.pythonhosted.org/packages/b3/66/68d13a42dabdc9ceb0ccf5590cbcbacde35883bb767149cd7fc6aa5186f2/lief-0.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d3337314ee663ef41f78e2b0ff6f6563819b68a75630d8c13f1867f0af9c6e5b", size = 3697835, upload-time = "2025-10-25T13:14:40.923Z" }, + { url = "https://files.pythonhosted.org/packages/0c/75/fa6bd509984a575251b776814eb3c680770fd193d8a7f34b2759127bc76a/lief-0.17.1-cp311-cp311-win32.whl", hash = "sha256:2dd4fe914111aaccc3ad7385caf4265e9ba4766ba6646f0b5556c9a8b4b2168f", size = 3450074, upload-time = "2025-10-25T13:14:42.837Z" }, + { url = "https://files.pythonhosted.org/packages/de/3c/98a03ad4609552b2fe78d91cafc4bac1fb1cd91e0bed3f282031261b72ab/lief-0.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ceb9801b3c6366cf7ef7c808afad7b3f94f41338f1ef33ee4e3f916121d78354", size = 3626741, upload-time = "2025-10-25T13:14:44.449Z" }, + { url = "https://files.pythonhosted.org/packages/d2/16/ee332a0993d67b5df3ce3fdca422df027bed9f64e15b39f6978e19911056/lief-0.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:d734be459521ec0162d92357104069e734cbaa5569f25fb74a19463bf9a8ef25", size = 3469284, upload-time = "2025-10-25T13:14:48.036Z" }, + { url = "https://files.pythonhosted.org/packages/ad/90/09ab478d99c2a7dc5f5b82beee777e13257477eb1c123820778fa8acb66a/lief-0.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:48a8d96e0132323cca4d065f2767db5c30cc80ebcf13ced387db2b1f743a0d1c", size = 2993876, upload-time = "2025-10-25T13:14:51.501Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f5/b0fa0504adad771c92ba9e01c715054708ef408abe948cb711e36e60a44f/lief-0.17.1-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:a89bc93808e870341067252b349bdc98135f006456b0bead99f5544ccb627ff7", size = 3106973, upload-time = "2025-10-25T13:14:54.868Z" }, + { url = "https://files.pythonhosted.org/packages/c7/4c/ede797fdaaffd77028c6eddac21f2bb23ea6cfbef7578cda3c1624559d3b/lief-0.17.1-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:629e92ec77ce7d1c6842f89ee155c61b70aefc02477b9d579da0a5c274aaf84c", size = 3669706, upload-time = "2025-10-25T13:14:57.987Z" }, + { url = "https://files.pythonhosted.org/packages/4d/25/301afca668eab4429d6cda399907c1a47241a2770398eb2ddd761463ab6d/lief-0.17.1-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:594bb50ab54c3ad280c57bb9b34f8f13137952db36709a2cd925558eaad268d4", size = 3501905, upload-time = "2025-10-25T13:15:00.797Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f8/9dcc8225c3ab39561509f9bf9c94d541e61c624a3509b54d65b1cf987ab6/lief-0.17.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b204c1f0dfeb820c7982fe40924cd147bc11b1c598d27acddb1ba64a5c653f1b", size = 3405030, upload-time = "2025-10-25T13:15:03.491Z" }, + { url = "https://files.pythonhosted.org/packages/0e/14/acdf8d9a3f4c0f6c32ed13facfe52366628b075435277f53c9e56105b090/lief-0.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d9c07ca8e8c2b0d72281fb5e52568b95e893a7d6fb0a84c4af3c58cc10b73892", size = 3591310, upload-time = "2025-10-25T13:15:06.349Z" }, + { url = "https://files.pythonhosted.org/packages/db/a8/bd51d2a7c1bcf2dff9f7336b07466db9e8af1c924a7def5770450a624082/lief-0.17.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9657541b8e7a43cd0fc52300cacc1a5e1d2023eec4a1929554493932e630e7b1", size = 3958475, upload-time = "2025-10-25T13:15:08.514Z" }, + { url = "https://files.pythonhosted.org/packages/c3/97/d4b9f0015b2b489687053df503224789dd881c2998817811e520e19811e7/lief-0.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:479bc9464e8c4af1eaeb0faa77d0dd02d57b6d7bd146a73d494443b618ff5043", size = 3705518, upload-time = "2025-10-25T13:15:11.072Z" }, + { url = "https://files.pythonhosted.org/packages/11/bd/9cee7f6233f100ce424d7b5827418ddd2aac2190a2069d34296147f8070d/lief-0.17.1-cp312-cp312-win32.whl", hash = "sha256:86c130a0ec2fc3ebb0a230491fd176aa2f26dc4f87a261932046fe3f6c4fd8a6", size = 3459261, upload-time = "2025-10-25T13:15:12.902Z" }, + { url = "https://files.pythonhosted.org/packages/09/48/e5c04d04c59db2ef370562f52d24e1f03ca0c190c32b4dc3ac01f73ef731/lief-0.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:72cc7c8b2f9f2e1ad19d6b771dc9865f4e3656fd99a057e9b01c02207c9c7e25", size = 3637722, upload-time = "2025-10-25T13:15:15.142Z" }, + { url = "https://files.pythonhosted.org/packages/52/b6/9e5945608391c18c675ba2db3dbbfd09a59d1d5006cc8edfbbf4ccad3432/lief-0.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:9f0b267b0771af1ef9de64e437391282e57009e68d509146ae76de1588da0cbf", size = 3473218, upload-time = "2025-10-25T13:15:16.862Z" }, + { url = "https://files.pythonhosted.org/packages/cd/b3/e9726aca37abf36125cf1503678cf7dc628a38b82cdbaffa81175afa09d3/lief-0.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dfde4ee44c34affe1fbe25177553deff0d38152d5e98fcf5409ea7f2133e4643", size = 3000642, upload-time = "2025-10-25T13:15:18.483Z" }, + { url = "https://files.pythonhosted.org/packages/b4/3a/6d70ed31450b065425f8c70de546a062ca2fbc70aab378d872e5f127f93c/lief-0.17.1-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:68fff6f4cb32b0cd674cb292e8c9e00bd60d5ccf978da345772c591247ebacd3", size = 3106802, upload-time = "2025-10-25T13:15:20.219Z" }, + { url = "https://files.pythonhosted.org/packages/31/e1/4ebf6fc0722a5792eaad27912443f9f2dd89b932f1a16cc815dcefc997f3/lief-0.17.1-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:311a9e785c7681a09ea4b288ce12fe667d353ede1a65e44bda580f209064d7ab", size = 3674232, upload-time = "2025-10-25T13:15:22.376Z" }, + { url = "https://files.pythonhosted.org/packages/b3/f5/e9855762fa36d6525984fc5864e4cc0a060277ce9539d9eb1c9efd3ee52e/lief-0.17.1-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:ed967b1155fa55f6d4f7ce6e250c17ae6ab59bf1dc21088f665c6d1b0582eecf", size = 3501766, upload-time = "2025-10-25T13:15:23.894Z" }, + { url = "https://files.pythonhosted.org/packages/db/ef/3b78416a66ddb602030b7b7baed297eb4f55acd31c3fb9d42bbda3ff129d/lief-0.17.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:72d52cceb92886e14c79b0fa62c4ae1fbe70888377b32a708d7f38c3c27447fa", size = 3404670, upload-time = "2025-10-25T13:15:25.961Z" }, + { url = "https://files.pythonhosted.org/packages/dc/28/dc2e94a10fc5d23ca54b94017c117e5dd7231163d4c7a70fc9b3db24d2ea/lief-0.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:69eb6fa86c785d17eabbfc780bf319b0a989303f74772f351be2ac4a65f442c4", size = 3592704, upload-time = "2025-10-25T13:15:28.82Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e2/1cf5e401618eaa4295b9763cbed218364889d273e9934b00dc9f2fed2782/lief-0.17.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a8421dfd29056675516b1c7f8e84a4e5d0c12fc71f832263feecbade44b70610", size = 3958662, upload-time = "2025-10-25T13:15:30.752Z" }, + { url = "https://files.pythonhosted.org/packages/0e/3d/58b00ada92385f4f1b8aceed5489564552cbfa277f98d751143bd0ae94ad/lief-0.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f9dff0644ed434d679411e31d9b000792f28bdec9f12e699b9adc239edc4d81e", size = 3705611, upload-time = "2025-10-25T13:15:32.629Z" }, + { url = "https://files.pythonhosted.org/packages/99/e6/96ef47c1d1cecb6ea00ba72589d1e1de49a59981c461da020a2a4d028338/lief-0.17.1-cp313-cp313-win32.whl", hash = "sha256:571b830523037efbfb9fcd58707c715ae417e2514e5abb3b03dc67b4c50f47fe", size = 3459075, upload-time = "2025-10-25T13:15:34.723Z" }, + { url = "https://files.pythonhosted.org/packages/29/b6/90960127904fe6120ac76376db72fab88431235e2fce591d2b763de20546/lief-0.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:7ada5acf4d83b5252c5fa46da3ff20c396bcdfa7aeb800c5dd28f8faa6827184", size = 3637804, upload-time = "2025-10-25T13:15:36.823Z" }, + { url = "https://files.pythonhosted.org/packages/2e/7a/4c4b500dfc57e31d4fbe48f9fbb52970265c78d0c8508366957e6d679126/lief-0.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:52fb1bff33cc8e2d3da8a2b426e9f8e5bdb334e351e70bc21b87db374fd4a837", size = 3472733, upload-time = "2025-10-25T13:15:38.957Z" }, + { url = "https://files.pythonhosted.org/packages/61/37/5061da652816cfdecb0380a0e9722c8c12586d0182e8704d93b414461619/lief-0.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:94505d03c63f68a2f740b72e87ae4fc80eff7c5e63c7d09582535888ef676d5c", size = 3000314, upload-time = "2025-10-25T13:15:40.56Z" }, + { url = "https://files.pythonhosted.org/packages/1e/bb/0486aaebdaea9971b96d7c3aa1f7d566243ad9511a1fb735d08b8b2717ed/lief-0.17.1-cp314-cp314-macosx_11_0_x86_64.whl", hash = "sha256:9198f88d3e168dda0c2891f6c343fc2936f8245139ea09683d2726a1b920bdb5", size = 3108946, upload-time = "2025-10-25T13:15:42.014Z" }, + { url = "https://files.pythonhosted.org/packages/97/33/7b2c4c281ef50c73b930fcc62c6b0d7b59f2e7626384b0f481bd47969ac0/lief-0.17.1-cp314-cp314-manylinux2014_aarch64.whl", hash = "sha256:54de530262d7dad925523102bfdbc2ead26aa5ba8819e539ac7373a4ba8868c5", size = 3674227, upload-time = "2025-10-25T13:15:43.851Z" }, + { url = "https://files.pythonhosted.org/packages/e8/1b/856c810b58ae0ec1bb70528a7bd0de948ee305aac65279983f9c90b36bb9/lief-0.17.1-cp314-cp314-manylinux_2_28_i686.whl", hash = "sha256:203019139646cd2be6a43e0f216b1463af2f6cb00c4c8006ccf6c62cb2aa2eda", size = 3501777, upload-time = "2025-10-25T13:15:45.262Z" }, + { url = "https://files.pythonhosted.org/packages/40/db/44ace7c9ee3fb04a4a9fbdb238e3254d5a6e18a4b062d7e7825407ae4d5b/lief-0.17.1-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:50864f0c57a5702849c662d01c80b1ec030a92867beffb977d264f46fd658c06", size = 3406898, upload-time = "2025-10-25T13:15:47.651Z" }, + { url = "https://files.pythonhosted.org/packages/3e/2e/af4858319460901f2383633e16c027361a8af5b5997b8f56fdf06656916a/lief-0.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:616b8eb51f9205a1bc90945487040522573306bfd7a4d0b8f838b61ff060b1a2", size = 3591598, upload-time = "2025-10-25T13:15:49.485Z" }, + { url = "https://files.pythonhosted.org/packages/4a/ba/3d243e001450aafbfbe9271e7065662de2cee4a621109227fa06331d7c11/lief-0.17.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:770c9fc5852050f3ff5f08d9a702d228114aa27a8385f1d72e2497b73fdecb27", size = 3958244, upload-time = "2025-10-25T13:15:51.288Z" }, + { url = "https://files.pythonhosted.org/packages/be/14/328481f91545f6efa22ca04ef6bc6078447d199c42c752b07ae0c65eda96/lief-0.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f73391aaf3848b81ed130928cba06a239d225de590be1e067701e05fd0b33aae", size = 3707511, upload-time = "2025-10-25T13:15:53.053Z" }, + { url = "https://files.pythonhosted.org/packages/8f/e5/a5452e109c6a55deaa73a49c34130980d99debabe3f37b8d86b67b2fb382/lief-0.17.1-cp314-cp314-win32.whl", hash = "sha256:f610d5937df9de86962733673f1a2b5255931c50b004e614728e83cbb0620057", size = 3459541, upload-time = "2025-10-25T13:15:54.877Z" }, + { url = "https://files.pythonhosted.org/packages/7f/d0/b4c959a340dd391df1f6b4c2958920f9272bc45b6b45f8af657b9377e09b/lief-0.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3dadb33cad8cf01d78a5eb12fb660ed1a06619f0baade38b606fd151e87436e2", size = 3637731, upload-time = "2025-10-25T13:15:56.644Z" }, +] + [[package]] name = "litecli" version = "1.17.0" @@ -648,6 +708,7 @@ dependencies = [ { name = "flask" }, { name = "frida" }, { name = "frida-tools" }, + { name = "lief" }, { name = "litecli" }, { name = "packaging" }, { name = "prompt-toolkit" }, @@ -665,6 +726,7 @@ requires-dist = [ { name = "flask", specifier = ">=3.0.0" }, { name = "frida", specifier = ">=16.0.0" }, { name = "frida-tools", specifier = ">=10.0.0" }, + { name = "lief", specifier = ">=0.17.1" }, { name = "litecli", specifier = ">=1.3.0" }, { name = "packaging", specifier = ">=23.0" }, { name = "prompt-toolkit", specifier = ">=3.0.30,<4.0.0" },