├── apknife ├── __init__.py ├── apknife.egg-info │ ├── SOURCES.txt │ ├── dependency_links.txt │ ├── top_level.txt │ ├── entry_points.txt │ ├── requires.txt │ └── PKG-INFO ├── .gitignore ├── modules │ ├── .gitignore │ ├── tools │ │ └── baksmali.jar │ ├── signer.py │ ├── analyzer.py │ ├── builder.py │ ├── __init__.py │ ├── .asc │ ├── api_finder.py │ ├── smali_tools.py │ ├── manifest_decoder.py │ ├── commands.json │ ├── catch_rat.py │ ├── dex_extractor.py │ ├── java_extractor.py │ ├── extract_sensitive.py │ ├── vulnerability_scanner.py │ ├── permission_scanner.py │ ├── extractor.py │ ├── apk_modifier.py │ ├── manifest_editor.py │ ├── protection_scanner.py │ └── interactive_mode.py ├── assets │ ├── cover.png │ ├── .apknife_history │ └── banner.txt ├── commands.json └── apknife.py ├── tests ├── pytest.ini └── test_example.py ├── tools └── baksmali.jar ├── requirements.txt ├── MAINFEST.in ├── updateAPKnife.sh ├── .gitignore ├── .github └── workflows │ ├── deploy.yml │ └── python-package.yml ├── LICENSE ├── update_pypi.sh ├── pyproject.toml ├── setup.py ├── secuirty.sh ├── setup.sh ├── safety_report.txt ├── apknife.py ├── README.md └── bandit_report.txt /apknife/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apknife/apknife.egg-info/SOURCES.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apknife/apknife.egg-info/dependency_links.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /apknife/apknife.egg-info/top_level.txt: -------------------------------------------------------------------------------- 1 | apknife 2 | -------------------------------------------------------------------------------- /apknife/.gitignore: -------------------------------------------------------------------------------- 1 | apknife.egg-info 2 | __pycache__ 3 | -------------------------------------------------------------------------------- /apknife/modules/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .apknife_history 3 | -------------------------------------------------------------------------------- /tests/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | minversion = 8.3 3 | addopts = -ra -q 4 | testpaths = tests 5 | -------------------------------------------------------------------------------- /tools/baksmali.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elrashedy1992/APKnife/HEAD/tools/baksmali.jar -------------------------------------------------------------------------------- /apknife/apknife.egg-info/entry_points.txt: -------------------------------------------------------------------------------- 1 | [console_scripts] 2 | apknife = apknife.apknife:main 3 | -------------------------------------------------------------------------------- /tests/test_example.py: -------------------------------------------------------------------------------- 1 | # tests/test_sample.py 2 | def test_example(): 3 | assert 1 + 1 == 2 4 | -------------------------------------------------------------------------------- /apknife/assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elrashedy1992/APKnife/HEAD/apknife/assets/cover.png -------------------------------------------------------------------------------- /apknife/modules/tools/baksmali.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elrashedy1992/APKnife/HEAD/apknife/modules/tools/baksmali.jar -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Pillow==11.1.0 2 | Requests==2.32.3 3 | androguard==4.1.3 4 | defusedxml==0.7.1 5 | lxml==5.3.1 6 | prompt_toolkit==3.0.50 7 | tqdm==4.67.1 8 | -------------------------------------------------------------------------------- /apknife/assets/.apknife_history: -------------------------------------------------------------------------------- 1 | 2 | # 2025-03-02 06:33:38.629239 3 | +ls 4 | 5 | # 2025-03-02 06:34:40.986007 6 | +echo "hello" 7 | 8 | # 2025-03-02 06:35:05.879721 9 | +exit 10 | -------------------------------------------------------------------------------- /MAINFEST.in: -------------------------------------------------------------------------------- 1 | include setup.py 2 | include pyproject.toml 3 | include requirements.txt 4 | include commands.json 5 | recursive-include apknife *.py *.json *.txt *.png 6 | recursive-include apknife/modules *.py 7 | recursive-include apknife/assets * 8 | recursive-include apknife/modules/tools *.jar 9 | -------------------------------------------------------------------------------- /apknife/assets/banner.txt: -------------------------------------------------------------------------------- 1 | ___ _ __ _ 2 | / _ \__ _(_)/ _| ___| |_ _ __ 3 | | | | / _` | | |_ / _ \ __| '__| 4 | | |_| | (_| | | _| __/ |_| | 5 | \___/ \__,_|_|_| \___|\__|_| v2.0 6 | ------------------------------------- 7 | APK modification tool (without apktool) 8 | Created by mr_nightmare 9 | -------------------------------------------------------------------------------- /apknife/modules/signer.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | 4 | def sign_apk(apk_path): 5 | try: 6 | subprocess.run(["apksigner", "sign", "--ks", "my-release-key.jks", apk_path]) 7 | print(f"✅ Signed APK: {apk_path}") 8 | except Exception as e: 9 | print(f"❌ Error signing APK: {e}") 10 | -------------------------------------------------------------------------------- /apknife/modules/analyzer.py: -------------------------------------------------------------------------------- 1 | from androguard.core.apk import APK 2 | 3 | 4 | def analyze_apk(apk_path): 5 | 6 | try: 7 | apk = APK(apk_path) 8 | print(f"📊 APK Package: {apk.get_package()}") 9 | print(f"📜 Permissions: {', '.join(apk.get_permissions())}") 10 | except Exception as e: 11 | print(f"❌ Error analyzing APK: {e}") 12 | -------------------------------------------------------------------------------- /updateAPKnife.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "🔄 Checking for updates..." 4 | git fetch origin main 5 | 6 | LOCAL=$(git rev-parse @) 7 | REMOTE=$(git rev-parse @{u}) 8 | 9 | if [ "$LOCAL" != "$REMOTE" ]; then 10 | echo "🚀 New update found! Updating APKnife..." 11 | git pull origin main 12 | echo "✅ Update completed successfully!" 13 | else 14 | echo "👍 APKnife is already up to date." 15 | fi 16 | -------------------------------------------------------------------------------- /apknife/modules/builder.py: -------------------------------------------------------------------------------- 1 | import os 2 | import zipfile 3 | 4 | 5 | def build_apk(input_dir, output_apk): 6 | 7 | with zipfile.ZipFile(output_apk, "w", zipfile.ZIP_DEFLATED) as apk: 8 | for root, _, files in os.walk(input_dir): 9 | for file in files: 10 | file_path = os.path.join(root, file) 11 | apk.write(file_path, os.path.relpath(file_path, input_dir)) 12 | print(f"✅ APK built: {output_apk}") 13 | -------------------------------------------------------------------------------- /apknife/modules/__init__.py: -------------------------------------------------------------------------------- 1 | from .analyzer import * 2 | from .api_finder import * 3 | from .builder import * 4 | from .catch_rat import * 5 | from .extract_sensitive import * 6 | from .extractor import * 7 | from .java_extractor import * 8 | from .manifest_editor import * 9 | from .permission_scanner import * 10 | from .signer import * 11 | from .smali_tools import * 12 | from .vulnerability_scanner import * 13 | from .dex_extractor import * 14 | from .manifest_decoder import * 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore virtual environment files 2 | venv/ 3 | .env 4 | 5 | # Ignore Python temporary files 6 | __pycache__/ 7 | *.pyc 8 | *.pyo 9 | *.pyd 10 | 11 | # Ignore output and build directories 12 | build/ 13 | dist/ 14 | *.egg-info/ 15 | 16 | # Ignore local configuration files 17 | *.local 18 | *.log 19 | 20 | # Ignore IDE files 21 | .vscode/ 22 | .idea/ 23 | 24 | # Ignore system files 25 | .DS_Store 26 | Thumbs.db 27 | 28 | # Ignore specific files 29 | secrets.txt 30 | config.ini 31 | .apknife_history 32 | automation.sh 33 | apknife.egg-info 34 | 35 | hater.apk 36 | apknife/apknife.egg-info/ 37 | bandit_report.txt 38 | safety_report.txt 39 | update_pypi.sh 40 | -------------------------------------------------------------------------------- /apknife/modules/.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP SIGNATURE----- 2 | 3 | iQGzBAABCgAdFiEERUywazwaZASQbRRWCFDN7BQf1LkFAmfCY84ACgkQCFDN7BQf 4 | 1LkGagwAsQqYEY99IN0XSA1ES6cHRfzA7oq1mlVBKC+7N/S/s3fB2Hh2jF+yfWlB 5 | 5RDyYnZ2c9pTBuiE8rEpupE9VdqRNFWIrVLrS4Fd/l2MPGXuHohBmW8eqA76cqbW 6 | IlA+UrN8xGQFRpljenr8zmC+ufVedNgzwKdMkLoWHFb0IFdKlcM9XYLI4DRXhToT 7 | OIcXx1oXEGHZa/qxkHZxgbeqJa8oJ1g4THdTgbj1d9VEH7t/u3LvBQCVKxEr50mF 8 | MCMmvLBcYuIlSg7t/WKUcTpLWT9b4bk+WDV8RcAqQkd8Oo21l5oFM/prVp5bFEYo 9 | MKce3u4k+YWcowdfTq4+A8DR7m47udxZwkXbokfXKcLVx2fSRT4AyuW8IQ7lj1Wq 10 | 7vBazDeUxFddV1E3K1dJrB3jpDawowvnEKrus54vcwg5MpWji6wjwnVkXC4g3WbE 11 | GCTOHXEm32cm26n8F7ziAnZ1mFi7IhNzGTCcYkIQA8WZBsZhOIzPN7IcxMNIaeQT 12 | 3uYHe8KB 13 | =pvWc 14 | -----END PGP SIGNATURE----- 15 | -------------------------------------------------------------------------------- /apknife/modules/api_finder.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | API_LIST = [ 5 | "getDeviceId", 6 | "getSubscriberId", 7 | "getSimSerialNumber", 8 | "getNetworkOperator", 9 | "getInstalledPackages", 10 | ] 11 | 12 | 13 | def find_api_calls(smali_dir): 14 | print("🔍 Searching for API calls...") 15 | for root, _, files in os.walk(smali_dir): 16 | for file in files: 17 | if file.endswith(".smali"): 18 | with open(os.path.join(root, file), "r", encoding="utf-8") as f: 19 | content = f.read() 20 | for api in API_LIST: 21 | if api in content: 22 | print(f"⚠️ Found {api} in {file}") 23 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to PyPI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: 🔄 Checkout code from GitHub 14 | uses: actions/checkout@v3 15 | 16 | - name: ⚙️ Set up Python 17 | uses: actions/setup-python@v4 18 | with: 19 | python-version: '3.x' 20 | 21 | - name: 📦 Install necessary tools 22 | run: | 23 | pip install --upgrade setuptools wheel twine 24 | 25 | - name: 🏗️ Build the package 26 | run: | 27 | python setup.py sdist bdist_wheel 28 | 29 | - name: 🚀 Upload package to PyPI 30 | env: 31 | PYPI_USERNAME: __token__ 32 | PYPI_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} 33 | run: | 34 | twine upload dist/* -u "$PYPI_USERNAME" -p "$PYPI_PASSWORD" 35 | -------------------------------------------------------------------------------- /apknife/modules/smali_tools.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | 4 | 5 | def decompile_apk(apk_dir, output_dir): 6 | 7 | if not os.path.exists(output_dir): 8 | os.makedirs(output_dir) 9 | 10 | cmd = f"java -jar tools/baksmali.jar d {apk_dir} -o {output_dir}" 11 | try: 12 | subprocess.run(cmd, shell=True, check=True) 13 | print(f"✅ Smali code extracted to {output_dir}") 14 | except subprocess.CalledProcessError: 15 | print("❌ Error during Smali decompilation") 16 | 17 | 18 | def find_oncreate(smali_dir): 19 | for root, _, files in os.walk(smali_dir): 20 | for file in files: 21 | if file.endswith(".smali"): 22 | with open(os.path.join(root, file), "r", encoding="utf-8") as f: 23 | content = f.read() 24 | if "onCreate" in content: 25 | print(f"✅ Found onCreate in {file}") 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Mr_nightmare 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /update_pypi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Check if a version argument is provided 4 | if [ -z "$1" ]; then 5 | echo "Usage: ./update_pypi.sh " 6 | exit 1 7 | fi 8 | 9 | NEW_VERSION="$1" 10 | 11 | # Project settings 12 | PROJECT_NAME="your_project" # Replace with your actual project name on PyPI 13 | VERSION_FILE_SETUP="setup.py" 14 | VERSION_FILE_TOML="pyproject.toml" 15 | DIST_DIR="dist" 16 | 17 | echo "[✔] Updating version to: $NEW_VERSION" 18 | 19 | # Update `setup.py` 20 | sed -i "s/version=\"[^\"]*\"/version=\"$NEW_VERSION\"/" "$VERSION_FILE_SETUP" 21 | 22 | # Update `pyproject.toml` if it exists 23 | if [ -f "$VERSION_FILE_TOML" ]; then 24 | sed -i "s/version = \"[^\"]*\"/version = \"$NEW_VERSION\"/" "$VERSION_FILE_TOML" 25 | fi 26 | 27 | # Clean previous builds 28 | rm -rf "$DIST_DIR" 29 | 30 | # Build the package 31 | echo "[✔] Building package..." 32 | python3 -m pip install --upgrade build twine 33 | python3 -m build 34 | 35 | # Upload the package to PyPI 36 | echo "[✔] Uploading package to PyPI..." 37 | python3 -m twine upload dist/* 38 | 39 | # Verify the new version on PyPI 40 | echo "[✔] Verifying the updated version on PyPI..." 41 | pip install --upgrade "$PROJECT_NAME" 42 | 43 | echo "[✔] Update successful! New version: $NEW_VERSION" 44 | -------------------------------------------------------------------------------- /apknife/apknife.egg-info/requires.txt: -------------------------------------------------------------------------------- 1 | alembic==1.14.1 2 | androguard==4.1.3 3 | apkInspector==1.3.2 4 | asn1crypto==1.5.1 5 | asttokens==3.0.0 6 | banal==1.0.6 7 | certifi==2025.1.31 8 | cffi==1.17.1 9 | charset-normalizer==3.4.1 10 | click==8.1.8 11 | colorama==0.4.6 12 | contourpy==1.3.1 13 | cryptography==44.0.2 14 | cycler==0.12.1 15 | dataset==1.6.2 16 | decorator==5.2.1 17 | executing==2.2.0 18 | fonttools==4.56.0 19 | frida==16.6.6 20 | future==1.0.0 21 | greenlet==3.1.1 22 | idna==3.10 23 | ipython==9.0.1 24 | ipython_pygments_lexers==1.1.1 25 | jedi==0.19.2 26 | kiwisolver==1.4.8 27 | loguru==0.7.3 28 | lxml==5.3.1 29 | Mako==1.3.9 30 | MarkupSafe==3.0.2 31 | matplotlib==3.10.1 32 | matplotlib-inline==0.1.7 33 | mutf8==1.0.6 34 | networkx==3.4.2 35 | numpy==2.2.3 36 | packaging==24.2 37 | parso==0.8.4 38 | pexpect==4.9.0 39 | pillow==11.1.0 40 | prompt_toolkit==3.0.50 41 | ptyprocess==0.7.0 42 | pure_eval==0.2.3 43 | pycparser==2.22 44 | pydot==3.0.4 45 | Pygments==2.19.1 46 | pyparsing==3.2.1 47 | python-dateutil==2.9.0.post0 48 | PyYAML==6.0.2 49 | requests==2.32.3 50 | setuptools==75.8.2 51 | six==1.17.0 52 | SQLAlchemy==1.4.54 53 | stack-data==0.6.3 54 | tqdm==4.67.1 55 | traitlets==5.14.3 56 | typing_extensions==4.12.2 57 | urllib3==2.3.0 58 | wcwidth==0.2.13 59 | wheel==0.45.1 60 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "apknife" 7 | version = "1.2.0" 8 | description = "APKnife is an advanced tool for APK analysis, modification, and security auditing. Whether you're a security researcher, penetration tester, or Android developer, APKnife provides powerful features for reverse engineering, decompiling, modifying, and analyzing APK files." 9 | authors = [{ name = "Mr_nightmare", email = "hmjany18@gmail.com" }] 10 | readme = "README.md" 11 | requires-python = ">=3.6" 12 | license = { text = "MIT" } 13 | 14 | dependencies = [ 15 | "androguard==4.1.3", 16 | "defusedxml==0.7.1", 17 | "lxml==5.3.1", 18 | "Pillow==11.1.0", 19 | "prompt_toolkit==3.0.50", 20 | "requests==2.32.3", 21 | "setuptools==75.8.2", 22 | "tqdm==4.67.1", 23 | ] 24 | 25 | [tool.setuptools] 26 | include-package-data = true 27 | packages = { find = { include = ["apknife*"], exclude = ["tests*", "docs*", "examples*", "kaboosvenv"] } } 28 | license-files = ["LICENSE"] # ✅ تم نقلها هنا 29 | 30 | [tool.setuptools.package-data] 31 | "apknife.modules.tools" = ["baksmali.jar"] 32 | 33 | [project.urls] 34 | Homepage = "https://github.com/elrashedy1992/APKnife" 35 | Documentation = "https://github.com/elrashedy1992/APKnife/wiki" 36 | Source = "https://github.com/elrashedy1992/APKnife" 37 | 38 | [project.scripts] 39 | apknife = "apknife.apknife:main" 40 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: Python package 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | python-version: ["3.9", "3.10", "3.11"] 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v3 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | python -m pip install flake8 pytest 31 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 32 | - name: Lint with flake8 33 | run: | 34 | # stop the build if there are Python syntax errors or undefined names 35 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 36 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 37 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 38 | - name: Test with pytest 39 | run: | 40 | pytest 41 | -------------------------------------------------------------------------------- /apknife/modules/manifest_decoder.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from xml.dom import minidom 3 | from lxml import etree 4 | from androguard.core.apk import APK 5 | 6 | # ANSI color codes for terminal output styling 7 | RED = "\033[91m" 8 | GREEN = "\033[92m" 9 | RESET = "\033[0m" 10 | 11 | def decode_manifest(apk_path, output_path=None): 12 | try: 13 | # Load the APK file 14 | apk = APK(apk_path) 15 | 16 | # Get the AndroidManifest.xml content as an lxml.etree._Element object 17 | manifest = apk.get_android_manifest_xml() 18 | 19 | # Convert the lxml.etree._Element object to a string 20 | manifest_str = etree.tostring(manifest, encoding="utf-8", pretty_print=True).decode("utf-8") 21 | 22 | # Use minidom to further prettify the XML 23 | parsed_xml = minidom.parseString(manifest_str) 24 | pretty_xml = parsed_xml.toprettyxml(indent=" ") 25 | 26 | # Save the decoded manifest to a file if output path is provided 27 | if output_path: 28 | with open(output_path, 'w', encoding='utf-8') as f: 29 | f.write(pretty_xml) 30 | logging.info(f"{GREEN}[*] Decoded AndroidManifest.xml saved to: {output_path}{RESET}") 31 | else: 32 | logging.info(f"{GREEN}[*] Decoded AndroidManifest.xml:{RESET}") 33 | print(pretty_xml) 34 | 35 | return pretty_xml 36 | 37 | except Exception as e: 38 | logging.error(f"{RED}[!] Error decoding AndroidManifest.xml: {e}{RESET}") 39 | return None 40 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | import os 3 | 4 | long_description = "" 5 | if os.path.exists("README.md"): 6 | with open("README.md", "r", encoding="utf-8") as f: 7 | long_description = f.read() 8 | 9 | requirements = [] 10 | if os.path.exists("requirements.txt"): 11 | with open("requirements.txt", "r", encoding="utf-8") as f: 12 | requirements = [line.strip() for line in f.readlines() if line.strip()] 13 | 14 | setup( 15 | name="apknife", 16 | version="1.2.0", 17 | description="APKnife is an advanced tool for APK analysis, modification, and security auditing.", 18 | long_description=long_description, 19 | long_description_content_type="text/markdown", 20 | author="Mr_nightmare", 21 | author_email="hmjany18@gmail.com", 22 | url="https://github.com/elrashedy1992/APKnife", 23 | project_urls={ 24 | "Homepage": "https://github.com/elrashedy1992/APKnife", 25 | "Documentation": "https://github.com/elrashedy1992/APKnife/wiki", 26 | "Source": "https://github.com/elrashedy1992/APKnife", 27 | }, 28 | license="MIT", 29 | license_files=["LICENSE"], 30 | python_requires=">=3.6", 31 | packages=find_packages(), 32 | include_package_data=True, 33 | package_data={ 34 | "apknife.modules.tools": ["baksmali.jar"] 35 | }, 36 | install_requires=requirements if requirements else [], 37 | entry_points={ 38 | "console_scripts": [ 39 | "apknife=apknife.apknife:main", 40 | ], 41 | }, 42 | classifiers=[ 43 | "License :: OSI Approved :: MIT License", 44 | "Programming Language :: Python :: 3", 45 | "Operating System :: OS Independent", 46 | "Topic :: Security", 47 | "Development Status :: 5 - Production/Stable", 48 | ], 49 | zip_safe=False, 50 | ) 51 | -------------------------------------------------------------------------------- /apknife/commands.json: -------------------------------------------------------------------------------- 1 | { 2 | "extract": "Extracts an APK file (-i input.apk -o output_folder)", 3 | "build": "Builds an APK from a modified directory (-i folder -o output.apk)", 4 | "sign": "Signs an APK file (-i input.apk)", 5 | "analyze": "Analyzes an APK for vulnerabilities (-i input.apk)", 6 | "edit-manifest": "Modifies the AndroidManifest.xml of an APK (-i input.apk)", 7 | "smali": "Decompiles APK into smali code (-i input.apk -o output_folder)", 8 | "decode_manifest": "Decodes binary XML files in an APK (-i input.apk)", 9 | "find-oncreate": "Finds the onCreate methods inside smali code (-i input.apk)", 10 | "find-api": "Finds API calls used in an APK (-i input.apk or -i smali_file(recommended))", 11 | "scan-vulnerabilities": "Scans APK for security vulnerabilities (-i input.apk)", 12 | "scan-permissions": "Lists all permissions requested by an APK (-i input.apk)", 13 | "catch_rat": "Detects potential RAT (Remote Access Trojan) in APK (-i input.apk)", 14 | "extract-java": "Extracts Java source code from an APK (-i input.apk -o output_folder -c [optional])", 15 | "extract-sensitive": "Extracts sensitive information from APK (-i input.apk -o output.json)", 16 | "modify-apk --icon": "Modifies the APK icon (-i input.apk)", 17 | "modify-apk --name": "Changes the application name (-i input.apk)", 18 | "modify-apk --package": "Modifies the package name (-i input.apk -o new_package_name)", 19 | "extract-dex": "Extract DEX files from an APK without fully decompiling it", 20 | "waf": "Scan the app for protection mechanisms (e.g., Firewall, ProGuard, etc.)", 21 | "update-commands": "Reloads the commands from the external file", 22 | "list-commands": "Displays the current list of available commands", 23 | "help": "Displays this help menu", 24 | "exit": "Exits the interactive mode" 25 | } 26 | -------------------------------------------------------------------------------- /apknife/modules/commands.json: -------------------------------------------------------------------------------- 1 | { 2 | "extract": "Extracts an APK file (-i input.apk -o output_folder)", 3 | "build": "Builds an APK from a modified directory (-i folder -o output.apk)", 4 | "sign": "Signs an APK file (-i input.apk)", 5 | "analyze": "Analyzes an APK for vulnerabilities (-i input.apk)", 6 | "edit-manifest": "Modifies the AndroidManifest.xml of an APK (-i input.apk)", 7 | "smali": "Decompiles APK into smali code (-i input.apk -o output_folder)", 8 | "decode_manifest": "Decodes binary XML files in an APK (-i input.apk)", 9 | "find-oncreate": "Finds the onCreate methods inside smali code (-i input.apk)", 10 | "find-api": "Finds API calls used in an APK (-i input.apk or -i smali_file(recommended))", 11 | "scan-vulnerabilities": "Scans APK for security vulnerabilities (-i input.apk)", 12 | "scan-permissions": "Lists all permissions requested by an APK (-i input.apk)", 13 | "catch_rat": "Detects potential RAT (Remote Access Trojan) in APK (-i input.apk)", 14 | "extract-java": "Extracts Java source code from an APK (-i input.apk -o output_folder -c [optional])", 15 | "extract-sensitive": "Extracts sensitive information from APK (-i input.apk -o output.json)", 16 | "modify-apk --icon": "Modifies the APK icon (-i input.apk)", 17 | "modify-apk --name": "Changes the application name (-i input.apk)", 18 | "modify-apk --package": "Modifies the package name (-i input.apk -o new_package_name)", 19 | "extract-dex": "Extract DEX files from an APK without fully decompiling it", 20 | "waf": "Scan the app for protection mechanisms (e.g., Firewall, ProGuard, etc.)", 21 | "update-commands": "Reloads the commands from the external file", 22 | "list-commands": "Displays the current list of available commands", 23 | "help": "Displays this help menu", 24 | "exit": "Exits the interactive mode" 25 | } 26 | -------------------------------------------------------------------------------- /apknife/modules/catch_rat.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | import subprocess 4 | import zipfile 5 | import requests 6 | 7 | def extract_ips_from_apk(apk_path): 8 | """Extract all suspicious IPs from the APK without extracting it.""" 9 | 10 | ip_pattern = re.compile(r"\b(?:\d{1,3}\.){3}\d{1,3}\b") 11 | found_ips = set() 12 | 13 | try: 14 | with zipfile.ZipFile(apk_path, "r") as apk: 15 | for file_name in apk.namelist(): 16 | # ابحث في جميع الملفات بغض النظر عن الامتداد 17 | with apk.open(file_name) as file: 18 | try: 19 | content = file.read().decode("utf-8", errors="ignore") 20 | matches = ip_pattern.findall(content) 21 | found_ips.update(matches) 22 | except Exception: 23 | pass # تجاهل الأخطاء عند قراءة الملفات الثنائية 24 | except zipfile.BadZipFile: 25 | print("❌ Not a valid APK file.") 26 | return [] 27 | 28 | return list(found_ips) 29 | 30 | def get_ip_info(ip): 31 | """Fetch IP information using ipinfo.io.""" 32 | url = f"https://ipinfo.io/{ip}/json" 33 | try: 34 | response = requests.get(url) 35 | data = response.json() 36 | print(f"\n📡 IP: {ip} Information:") 37 | print(json.dumps(data, indent=4)) 38 | return data 39 | except requests.RequestException as e: 40 | print(f"❌ Failed to fetch IP info: {e}") 41 | return None 42 | 43 | def run_whois(ip): 44 | """Run WHOIS lookup for the given IP address.""" 45 | try: 46 | result = subprocess.run(["whois", ip], capture_output=True, text=True) 47 | print(f"\n🔍 WHOIS Information for {ip}:") 48 | print(result.stdout) 49 | except FileNotFoundError: 50 | print("❌ WHOIS command not found. Please install it first.") 51 | except Exception as e: 52 | print(f"❌ Failed to run WHOIS: {e}") 53 | 54 | def analyze_apk_ips(apk_path): 55 | """Extract and analyze suspicious IPs from APK.""" 56 | ips = extract_ips_from_apk(apk_path) 57 | 58 | if not ips: 59 | print("✅ No suspicious IPs found in APK.") 60 | return 61 | 62 | print("\n🔎 Found Suspicious IPs:") 63 | for ip in ips: 64 | print(f" - {ip}") 65 | get_ip_info(ip) 66 | run_whois(ip) 67 | -------------------------------------------------------------------------------- /apknife/modules/dex_extractor.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import traceback 4 | from androguard.core.apk import APK 5 | 6 | # Configure logging for clear debugging messages 7 | logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") 8 | 9 | def extract_dex(apk_path, output_dir=None): 10 | """ 11 | Extract DEX files from an APK without fully decompiling it. 12 | 13 | :param apk_path: Path to the APK file. 14 | :param output_dir: Directory to save the extracted DEX files (default: "extracted_dex"). 15 | :return: List of paths to the extracted DEX files. 16 | """ 17 | try: 18 | if not os.path.isfile(apk_path): 19 | logging.error(f"File '{apk_path}' does not exist!") 20 | return [] 21 | 22 | # Set output directory 23 | if not output_dir: 24 | output_dir = os.path.join(os.getcwd(), "extracted_dex") 25 | os.makedirs(output_dir, exist_ok=True) 26 | 27 | logging.info(f"Loading APK: {apk_path}") 28 | apk = APK(apk_path) 29 | 30 | # Validate APK 31 | if not apk.is_valid_APK(): 32 | logging.error(f"Invalid APK file: {apk_path}") 33 | return [] 34 | 35 | # Attempt to extract all DEX files 36 | dex_files = apk.get_all_dex() 37 | if not dex_files: 38 | logging.warning("No DEX files found using 'get_all_dex()'! Trying 'get_dex()' instead...") 39 | dex_data = apk.get_dex() 40 | if dex_data: 41 | dex_files = [dex_data] # Convert single DEX data to a list 42 | else: 43 | logging.error("Failed to extract DEX files! The APK may be encrypted or protected.") 44 | return [] 45 | 46 | # Save extracted DEX files 47 | extracted_files = [] 48 | for i, dex in enumerate(dex_files): 49 | dex_filename = f"classes{'' if i == 0 else i + 1}.dex" 50 | dex_path = os.path.join(output_dir, dex_filename) 51 | with open(dex_path, "wb") as f: 52 | f.write(dex) 53 | extracted_files.append(dex_path) 54 | logging.info(f"Extracted {dex_filename} to {dex_path}") 55 | 56 | return extracted_files 57 | 58 | except Exception as e: 59 | logging.error(f"Error while extracting DEX files: {e}") 60 | traceback.print_exc() 61 | return [] 62 | -------------------------------------------------------------------------------- /apknife/modules/java_extractor.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import shutil 4 | import subprocess 5 | import zipfile 6 | 7 | # Configure logging to save errors and outputs 8 | logging.basicConfig( 9 | level=logging.INFO, 10 | format="%(asctime)s - %(levelname)s - %(message)s", 11 | filename="log.txt", 12 | filemode="a", 13 | ) 14 | 15 | 16 | def is_jadx_installed(): 17 | """Check if JADX is installed on the system.""" 18 | return shutil.which("jadx") is not None 19 | 20 | 21 | def extract_java(apk_path, output_dir="extracted_java", compress=False): 22 | """Extract Java files from an APK using JADX.""" 23 | 24 | if not os.path.exists(apk_path): 25 | logging.error(f"[!] APK file not found: {apk_path}") 26 | return 27 | 28 | if not is_jadx_installed(): 29 | logging.error( 30 | "[!] JADX is not installed! Please install it using: sudo apt install jadx or download it from https://github.com/skylot/jadx" 31 | ) 32 | return 33 | 34 | if not os.path.exists(output_dir): 35 | os.makedirs(output_dir) 36 | 37 | logging.info(f"[+] Extracting Java files from {apk_path} to {output_dir}") 38 | 39 | # Unzip APK to extract DEX files 40 | with zipfile.ZipFile(apk_path, "r") as zip_ref: 41 | zip_ref.extractall(output_dir) 42 | 43 | dex_files = [f for f in os.listdir(output_dir) if f.endswith(".dex")] 44 | if not dex_files: 45 | logging.warning("[!] No DEX files found in the APK!") 46 | return 47 | 48 | # Create an output directory for Java sources 49 | jadx_output = os.path.join(output_dir, "java_sources") 50 | os.makedirs(jadx_output, exist_ok=True) 51 | 52 | # Run JADX to process all DEX files at once 53 | dex_paths = [os.path.join(output_dir, dex) for dex in dex_files] 54 | logging.info(f"[+] Converting all DEX files to Java at once...") 55 | 56 | try: 57 | subprocess.run(["jadx", "-d", jadx_output] + dex_paths, check=True) 58 | logging.info("[✔] Java files extracted successfully!") 59 | except subprocess.CalledProcessError as e: 60 | logging.error(f"[!] JADX execution failed: {e}") 61 | 62 | # Compress output if requested 63 | if compress: 64 | compress_output(jadx_output) 65 | 66 | 67 | def compress_output(output_dir): 68 | """Compress extracted Java files into a ZIP archive.""" 69 | zip_file = os.path.join(output_dir, "java_sources.zip") 70 | logging.info(f"[+] Compressing files into {zip_file}") 71 | 72 | with zipfile.ZipFile(zip_file, "w", zipfile.ZIP_DEFLATED) as zipf: 73 | for root, _, files in os.walk(output_dir): 74 | for file in files: 75 | file_path = os.path.join(root, file) 76 | zipf.write(file_path, os.path.relpath(file_path, output_dir)) 77 | 78 | logging.info(f"[✔] Compressed files saved in {zip_file}") 79 | -------------------------------------------------------------------------------- /secuirty.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Define colors for output 4 | GREEN='\033[0;32m' 5 | YELLOW='\033[1;33m' 6 | RED='\033[0;31m' 7 | NC='\033[0m' 8 | 9 | # Function to install required tools 10 | function install_tools() { 11 | echo -e "${YELLOW}📦 Installing required tools...${NC}" 12 | pip install bandit safety defusedxml 13 | echo -e "${GREEN}✅ Tools installed.${NC}" 14 | } 15 | 16 | # Function to run Bandit security checks 17 | function run_bandit() { 18 | echo -e "${YELLOW}🔍 Running Bandit security checks...${NC}" 19 | bandit -r apknife/ -f txt -o bandit_report.txt 20 | if [ $? -ne 0 ]; then 21 | echo -e "${RED}❌ Bandit found security issues.${NC}" 22 | fix_bandit_issues 23 | else 24 | echo -e "${GREEN}✅ No security issues found by Bandit.${NC}" 25 | fi 26 | } 27 | 28 | # Function to fix Bandit issues 29 | function fix_bandit_issues() { 30 | echo -e "${YELLOW}🔄 Fixing Bandit issues...${NC}" 31 | 32 | # Replace xml.etree.ElementTree with defusedxml 33 | if grep -q "xml.etree.ElementTree" apknife/modules/vulnerability_scanner.py; then 34 | echo -e "${YELLOW}🔧 Replacing xml.etree.ElementTree with defusedxml...${NC}" 35 | sed -i 's/xml.etree.ElementTree/defusedxml.ElementTree/g' apknife/modules/vulnerability_scanner.py 36 | echo -e "${GREEN}✅ XML parsing is now secure.${NC}" 37 | fi 38 | 39 | # Add more fixes here as needed 40 | echo -e "${GREEN}✅ Bandit issues fixed.${NC}" 41 | } 42 | 43 | # Function to run Safety checks 44 | function run_safety() { 45 | echo -e "${YELLOW}🔍 Running Safety checks...${NC}" 46 | safety check --full-report > safety_report.txt 47 | if [ $? -ne 0 ]; then 48 | echo -e "${RED}❌ Safety found vulnerable dependencies.${NC}" 49 | fix_safety_issues 50 | else 51 | echo -e "${GREEN}✅ No vulnerable dependencies found by Safety.${NC}" 52 | fi 53 | } 54 | 55 | # Function to fix Safety issues 56 | function fix_safety_issues() { 57 | echo -e "${YELLOW}🔄 Fixing Safety issues...${NC}" 58 | 59 | # Upgrade vulnerable dependencies 60 | if grep -q "Vulnerability found" safety_report.txt; then 61 | echo -e "${YELLOW}🔧 Upgrading vulnerable dependencies...${NC}" 62 | pip install --upgrade $(grep "Vulnerability found" safety_report.txt | awk '{print $2}') 63 | echo -e "${GREEN}✅ Vulnerable dependencies upgraded.${NC}" 64 | fi 65 | 66 | echo -e "${GREEN}✅ Safety issues fixed.${NC}" 67 | } 68 | 69 | # Function to run tests 70 | function run_tests() { 71 | echo -e "${YELLOW}🧪 Running tests...${NC}" 72 | pytest tests/ 73 | if [ $? -ne 0 ]; then 74 | echo -e "${RED}❌ Some tests failed! Please fix them before proceeding.${NC}" 75 | exit 1 76 | fi 77 | echo -e "${GREEN}✅ All tests passed.${NC}" 78 | } 79 | 80 | # Main function 81 | function main() { 82 | install_tools 83 | run_bandit 84 | run_safety 85 | run_tests 86 | echo -e "${GREEN}✅ All security checks and fixes completed successfully!${NC}" 87 | } 88 | 89 | # Run the script 90 | main 91 | -------------------------------------------------------------------------------- /apknife/modules/extract_sensitive.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import json 3 | import os 4 | import re 5 | import subprocess 6 | import zipfile 7 | from concurrent.futures import ThreadPoolExecutor 8 | 9 | from androguard.misc import AnalyzeAPK 10 | from tqdm import tqdm 11 | 12 | # أنماط البحث عن البيانات الحساسة 13 | PATTERNS = { 14 | "API Keys": r"(?i)(google_api_key|aws_secret_access_key|firebase_api_key|auth_key)=[\"']?([A-Za-z0-9_\-]+)[\"']?", 15 | "Passwords": r"(?i)(password|pass|pwd|secret)=[\"']?([A-Za-z0-9@#$%^&+=]{6,})[\"']?", 16 | "JWT Tokens": r"eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.?[a-zA-Z0-9_-]*", 17 | "IP Addresses": r"\b(?:\d{1,3}\.){3}\d{1,3}\b", 18 | "URLs": r"https?://[^\s]+", 19 | "RSA Keys": r"-----BEGIN RSA PRIVATE KEY-----[\s\S]+?-----END RSA PRIVATE KEY-----", 20 | } 21 | 22 | 23 | def analyze_dex_files(apk_file): 24 | """تحليل ملفات DEX داخل APK""" 25 | print("[+] Analyzing DEX files for potential vulnerabilities...") 26 | a, d, dx = AnalyzeAPK(apk_file) 27 | 28 | for cls in dx.get_classes(): 29 | print(f" - {cls}") 30 | 31 | 32 | def scan_shared_preferences(apk_path): 33 | 34 | with zipfile.ZipFile(apk_path, "r") as apk: 35 | for file in apk.namelist(): 36 | if "shared_prefs" in file and file.endswith(".xml"): 37 | with apk.open(file) as f: 38 | content = f.read().decode(errors="ignore") 39 | if "password" in content or "api_key" in content: 40 | print(f"[!] Sensitive data found in: {file}") 41 | 42 | 43 | def process_file(file, apk, extracted_data): 44 | 45 | try: 46 | with apk.open(file) as f: 47 | content = f.read().decode(errors="ignore") 48 | 49 | for category, pattern in PATTERNS.items(): 50 | matches = re.findall(pattern, content) 51 | if matches: 52 | extracted_data.setdefault(category, []).extend(matches) 53 | except Exception: 54 | pass 55 | 56 | 57 | def save_report_json(extracted_data, output_file): 58 | 59 | with open(output_file, "w", encoding="utf-8") as report: 60 | json.dump(extracted_data, report, indent=4, ensure_ascii=False) 61 | print(f"[✔] Report saved in: {output_file}") 62 | 63 | 64 | def extract_sensitive_data(apk_file, output_file="sensitive_report.json"): 65 | 66 | if not os.path.exists(apk_file): 67 | print("[!] File not found:", apk_file) 68 | return 69 | 70 | print("[*] Extracting sensitive data from:", apk_file) 71 | extracted_data = {} 72 | 73 | with zipfile.ZipFile(apk_file, "r") as apk: 74 | file_list = apk.namelist() 75 | 76 | with ThreadPoolExecutor() as executor: 77 | for file in tqdm(file_list, desc="🔎 Scanning files", unit="file"): 78 | executor.submit(process_file, file, apk, extracted_data) 79 | 80 | analyze_dex_files(apk_file) 81 | 82 | scan_shared_preferences(apk_file) 83 | 84 | if not extracted_data: 85 | print("[✓] No sensitive data found in the application.") 86 | return 87 | 88 | print("\n[+] Sensitive data found!") 89 | save_report_json(extracted_data, output_file) 90 | -------------------------------------------------------------------------------- /apknife/modules/vulnerability_scanner.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import subprocess 4 | import defusedxml.ElementTree as ET 5 | 6 | from androguard.misc import AnalyzeAPK 7 | 8 | 9 | def decompile_apk(apk_path, output_dir): 10 | """Decompile APK using APKTool""" 11 | if not os.path.exists(output_dir): 12 | os.makedirs(output_dir) 13 | 14 | command = f"apktool d -f {apk_path} -o {output_dir}" 15 | subprocess.run(command, shell=True, check=True) 16 | logging.info(f"[+] APK decompiled to: {output_dir}") 17 | 18 | 19 | def extract_source_code(apk_path, output_dir): 20 | """Extract source code using Jadx""" 21 | if not os.path.exists(output_dir): 22 | os.makedirs(output_dir) 23 | 24 | command = f"jadx -d {output_dir} {apk_path}" 25 | subprocess.run(command, shell=True, check=True) 26 | logging.info(f"[+] Source code extracted to: {output_dir}") 27 | 28 | 29 | def analyze_permissions(apk_path): 30 | """Analyze APK permissions using Androguard""" 31 | a, _, _ = AnalyzeAPK(apk_path) 32 | permissions = a.get_permissions() 33 | 34 | logging.info("[+] Requested Permissions:") 35 | for perm in permissions: 36 | logging.info(f" - {perm}") 37 | 38 | 39 | def search_for_vulnerabilities(source_code_dir): 40 | """Search for common vulnerabilities in the source code""" 41 | common_vulns = [ 42 | "Runtime.exec(", 43 | "getSharedPreferences(", 44 | "HttpURLConnection", 45 | "SSLSocketFactory", 46 | "setAllowBackup(true)", 47 | "setDebug(true)", 48 | ] 49 | 50 | logging.info("[+] Searching for common vulnerabilities:") 51 | for root, _, files in os.walk(source_code_dir): 52 | for file in files: 53 | if file.endswith(".java"): 54 | file_path = os.path.join(root, file) 55 | with open(file_path, "r", encoding="utf-8") as f: 56 | lines = f.readlines() 57 | for line_num, line in enumerate(lines, 1): 58 | for vuln in common_vulns: 59 | if vuln in line: 60 | logging.warning( 61 | f" - {file_path}:{line_num} - {line.strip()}" 62 | ) 63 | 64 | 65 | def analyze_android_manifest(decompiled_dir): 66 | """Analyze AndroidManifest.xml for insecure settings""" 67 | manifest_path = os.path.join(decompiled_dir, "AndroidManifest.xml") 68 | if not os.path.exists(manifest_path): 69 | logging.error("[-] AndroidManifest.xml file not found!") 70 | return 71 | 72 | tree = ET.parse(manifest_path) 73 | root = tree.getroot() 74 | 75 | logging.info("[+] Analyzing AndroidManifest.xml:") 76 | 77 | # Look for insecure settings 78 | insecure_settings = { 79 | "android:allowBackup": "true", 80 | "android:debuggable": "true", 81 | "android:usesCleartextTraffic": "true", 82 | } 83 | 84 | for element in root.iter(): 85 | for attr, value in insecure_settings.items(): 86 | if element.get(attr) == value: 87 | logging.warning(f" - {element.tag}: {attr}={value} (Insecure)") 88 | 89 | 90 | def scan_apk(apk_path): 91 | """Perform a complete APK security scan""" 92 | decompiled_dir = "decompiled_apk" 93 | source_code_dir = "source_code" 94 | 95 | decompile_apk(apk_path, decompiled_dir) 96 | extract_source_code(apk_path, source_code_dir) 97 | analyze_permissions(apk_path) 98 | analyze_android_manifest(decompiled_dir) 99 | search_for_vulnerabilities(source_code_dir) 100 | 101 | logging.info("[+] Scan completed successfully!") 102 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Colors 4 | CYAN='\033[1;36m' 5 | YELLOW='\033[1;33m' 6 | RED='\033[1;31m' 7 | GREEN='\033[1;32m' 8 | RESET='\033[0m' 9 | 10 | # Clear the screen for a dramatic start 11 | clear 12 | 13 | # Cyberpunk ASCII Banner 14 | echo -e "${CYAN}" 15 | cat << "EOF" 16 | █████╗ ██████╗ ██╗ ██╗███╗ ██╗██╗███████╗██████╗ 17 | ██╔══██╗██╔══██╗██║ ██║████╗ ██║██║██╔════╝██╔══██╗ 18 | ███████║██████╔╝███████║██╔██╗ ██║██║███████╗██████╔╝ 19 | ██╔══██║██╔═══╝ ██╔══██║██║╚██╗██║██║╚════██║██╔═══╝ 20 | ██║ ██║██║ ██║ ██║██║ ╚████║██║███████║██║ 21 | ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝╚══════╝╚═╝ 22 | EOF 23 | echo -e "${YELLOW} [ APKnife Auto-Installer ] ${RESET}" 24 | echo -e "${GREEN} Script by: Mr_Nightmare 🔥 ${RESET}" 25 | echo "" 26 | 27 | sleep 2 28 | 29 | # Root Check 30 | if [[ $EUID -ne 0 ]]; then 31 | echo -e "${RED}[⚠️] Please run this script as root!${RESET}" 32 | exit 1 33 | fi 34 | 35 | # Detect Operating System 36 | OS="" 37 | if [[ -f /etc/os-release ]]; then 38 | . /etc/os-release 39 | OS=$ID 40 | elif [[ $(uname) == "Darwin" ]]; then 41 | OS="macos" 42 | elif [[ $(uname) == "Linux" ]]; then 43 | OS="linux" 44 | elif [[ $(uname) == "Android" ]]; then 45 | OS="termux" 46 | else 47 | echo -e "${RED}[❌] Unsupported Operating System!${RESET}" 48 | exit 1 49 | fi 50 | 51 | echo -e "${CYAN}[💻] Detected OS: $OS ${RESET}" 52 | sleep 1 53 | 54 | # Function to Install Dependencies 55 | install_dependencies() { 56 | echo -e "${YELLOW}[📦] Installing dependencies...${RESET}" 57 | sleep 1 58 | 59 | case "$OS" in 60 | ubuntu|debian) 61 | apt update && apt install -y python3 python3-pip unzip openjdk-17-jdk 62 | ;; 63 | arch|manjaro) 64 | pacman -Syu --noconfirm python python-pip unzip jdk-openjdk 65 | ;; 66 | termux) 67 | pkg update && pkg install -y python python-pip unzip openjdk-17 68 | ;; 69 | macos) 70 | brew install python unzip openjdk 71 | ;; 72 | *) 73 | echo -e "${RED}[❌] Unsupported OS! Exiting...${RESET}" 74 | exit 1 75 | ;; 76 | esac 77 | } 78 | 79 | install_dependencies 80 | 81 | # Ensure pip is installed and updated 82 | if ! command -v pip3 &> /dev/null; then 83 | echo -e "${RED}[⚠️] pip3 not found! Installing...${RESET}" 84 | python3 -m ensurepip 85 | fi 86 | pip3 install --upgrade pip 87 | 88 | # Create and Activate Virtual Environment (Optional but Recommended) 89 | if [[ ! -d "env" ]]; then 90 | echo -e "${CYAN}[🐍] Creating Python Virtual Environment...${RESET}" 91 | python3 -m venv env 92 | fi 93 | source env/bin/activate 94 | 95 | # Install Python Libraries 96 | echo -e "${YELLOW}[🚀] Installing Required Python Libraries...${RESET}" 97 | pip3 install androguard requests lxml 98 | 99 | # Setting Up APK Signer (For Linux) 100 | if [[ "$OS" != "macos" ]]; then 101 | echo -e "${CYAN}[🔏] Setting up APK Signer...${RESET}" 102 | if ! command -v apksigner &> /dev/null; then 103 | echo -e "${RED}[⚠️] apksigner not found! Downloading...${RESET}" 104 | mkdir -p ~/Android/Sdk/cmdline-tools 105 | cd ~/Android/Sdk/cmdline-tools || exit 106 | wget https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip -O sdk-tools.zip 107 | unzip sdk-tools.zip && rm sdk-tools.zip 108 | export PATH=$PATH:~/Android/Sdk/cmdline-tools/bin 109 | echo 'export PATH=$PATH:~/Android/Sdk/cmdline-tools/bin' >> ~/.bashrc 110 | source ~/.bashrc 111 | fi 112 | fi 113 | 114 | # Make APKnife Executable 115 | chmod +x APKnife/apknife.py 116 | 117 | # Final Message 118 | echo "" 119 | echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" 120 | echo -e "${YELLOW} ✅ APKnife Installation Complete! 🔥 ${RESET}" 121 | echo -e "${CYAN} ➡️ Run: python3 APKnife/apknife.py --help ${RESET}" 122 | echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" 123 | echo -e "${RED} [🔰] Created by: Mr_Nightmare ${RESET}" 124 | echo "" 125 | -------------------------------------------------------------------------------- /safety_report.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | +===========================================================================================================================================================================================+ 4 | 5 | 6 | DEPRECATED: this command (`check`) has been DEPRECATED, and will be unsupported beyond 01 June 2024. 7 | 8 | 9 | We highly encourage switching to the new `scan` command which is easier to use, more powerful, and can be set up to mimic the deprecated command if required. 10 | 11 | 12 | +===========================================================================================================================================================================================+ 13 | 14 | 15 | +==============================================================================+ 16 | 17 | /$$$$$$ /$$ 18 | /$$__ $$ | $$ 19 | /$$$$$$$ /$$$$$$ | $$ \__//$$$$$$ /$$$$$$ /$$ /$$ 20 | /$$_____/ |____ $$| $$$$ /$$__ $$|_ $$_/ | $$ | $$ 21 | | $$$$$$ /$$$$$$$| $$_/ | $$$$$$$$ | $$ | $$ | $$ 22 | \____ $$ /$$__ $$| $$ | $$_____/ | $$ /$$| $$ | $$ 23 | /$$$$$$$/| $$$$$$$| $$ | $$$$$$$ | $$$$/| $$$$$$$ 24 | |_______/ \_______/|__/ \_______/ \___/ \____ $$ 25 | /$$ | $$ 26 | | $$$$$$/ 27 | by safetycli.com \______/ 28 | 29 | +==============================================================================+ 30 | 31 | REPORT 32 | 33 | Safety v3.3.1 is scanning for Vulnerabilities... 34 |  Scanning dependencies in your environment: 35 | 36 | -> /root/kaboos/lib/python3.13/site-packages 37 | -> /usr/lib/python313.zip 38 | -> /root/kaboos/lib/python3.13/site-packages/setuptools/_vendor 39 | -> /usr/lib/python3.13 40 | -> /root/kaboos/bin 41 | -> /usr/lib/python3.13/lib-dynload 42 | 43 | Using open-source vulnerability database 44 |  Found and scanned 97 packages 45 | Timestamp 2025-03-07 08:36:24 46 |  2 vulnerabilities reported 47 |  0 vulnerabilities ignored 48 | 49 | +==============================================================================+ 50 | VULNERABILITIES REPORTED 51 | +==============================================================================+ 52 | 53 | -> Vulnerability found in sqlalchemy version 1.4.54 54 |  Vulnerability ID: 51668 55 |  Affected spec: <2.0.0b1 56 |  ADVISORY: Sqlalchemy 2.0.0b1 avoids leaking cleartext passwords to 57 | the open for careless uses of str(engine.URL()) in logs and 58 | prints.https://github.com/sqlalchemy/sqlalchemy/pull/8563 59 |  PVE-2022-51668 60 |  For more information about this vulnerability, visit 61 | https://data.safetycli.com/v/51668/97c 62 | To ignore this vulnerability, use PyUp vulnerability id 51668 in safety’s 63 | ignore command-line argument or add the ignore to your safety policy file. 64 | 65 | 66 | -> Vulnerability found in pip version 24.3.1 67 |  Vulnerability ID: 75180 68 |  Affected spec: <25.0 69 |  ADVISORY: Pip solves a security vulnerability that previously 70 | allowed maliciously crafted wheel files to execute unauthorized code 71 | during installation. 72 |  PVE-2025-75180 73 |  For more information about this vulnerability, visit 74 | https://data.safetycli.com/v/75180/97c 75 | To ignore this vulnerability, use PyUp vulnerability id 75180 in safety’s 76 | ignore command-line argument or add the ignore to your safety policy file. 77 | 78 | 79 | +==============================================================================+ 80 | REMEDIATIONS 81 | 82 | 2 vulnerabilities were reported in 2 packages. For detailed remediation & 83 | fix recommendations, upgrade to a commercial license. 84 | 85 | +==============================================================================+ 86 | 87 | Scan was completed. 2 vulnerabilities were reported. 88 | 89 | +==============================================================================+ 90 | 91 | 92 | +===========================================================================================================================================================================================+ 93 | 94 | 95 | DEPRECATED: this command (`check`) has been DEPRECATED, and will be unsupported beyond 01 June 2024. 96 | 97 | 98 | We highly encourage switching to the new `scan` command which is easier to use, more powerful, and can be set up to mimic the deprecated command if required. 99 | 100 | 101 | +===========================================================================================================================================================================================+ 102 | 103 | 104 | -------------------------------------------------------------------------------- /apknife/modules/permission_scanner.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import xml.etree.ElementTree as ET 3 | import zipfile 4 | 5 | from androguard.core.axml import AXMLPrinter 6 | 7 | logging.basicConfig(level=logging.INFO, format="%(message)s") 8 | 9 | PERMISSION_CATEGORIES = { 10 | "normal": { 11 | "android.permission.ACCESS_WIFI_STATE": "Allows applications to access Wi-Fi networks.", 12 | "android.permission.ACCESS_NETWORK_STATE": "Allows applications to access networks.", 13 | "android.permission.SET_WALLPAPER": "Allows setting the wallpaper.", 14 | "android.permission.CHANGE_WIFI_STATE": "Allows changing Wi-Fi state.", 15 | }, 16 | "dangerous": { 17 | "android.permission.ACCESS_FINE_LOCATION": "Precise location access using GPS.", 18 | "android.permission.ACCESS_COARSE_LOCATION": "Approximate location access using network sources.", 19 | "android.permission.CALL_PHONE": "Initiate a phone call without user interaction.", 20 | "android.permission.READ_CONTACTS": "Read user contacts data.", 21 | "android.permission.WRITE_CONTACTS": "Write user contacts data.", 22 | "android.permission.RECORD_AUDIO": "Record audio using the microphone.", 23 | "android.permission.CAMERA": "Access the camera for photos and videos.", 24 | "android.permission.READ_SMS": "Read SMS messages.", 25 | "android.permission.SEND_SMS": "Send SMS messages.", 26 | "android.permission.RECEIVE_SMS": "Receive SMS messages.", 27 | "android.permission.READ_CALL_LOG": "Read the user's call log.", 28 | "android.permission.WRITE_CALL_LOG": "Modify the user's call log.", 29 | "android.permission.READ_PHONE_STATE": "Read phone state information.", 30 | }, 31 | "critical": { 32 | "android.permission.WRITE_SETTINGS": "Modify system settings, which can be dangerous.", 33 | "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS": "Ignore battery optimizations, potentially draining battery.", 34 | "android.permission.RECEIVE_BOOT_COMPLETED": "Start after boot, potentially for persistent background execution.", 35 | "android.permission.INTERNET": "Access the internet, often used for external communication.", 36 | "android.permission.WRITE_EXTERNAL_STORAGE": "Write to external storage, potentially exposing user data.", 37 | }, 38 | } 39 | 40 | 41 | def extract_manifest(apk_path): 42 | """Extracts and decodes AndroidManifest.xml from the APK file.""" 43 | try: 44 | with zipfile.ZipFile(apk_path, "r") as apk: 45 | manifest_data = apk.read("AndroidManifest.xml") 46 | decoded_manifest = AXMLPrinter(manifest_data).get_xml() 47 | return decoded_manifest 48 | except Exception as e: 49 | logging.error(f"❌ Failed to extract AndroidManifest.xml: {e}") 50 | return None 51 | 52 | 53 | def parse_permissions(manifest_data): 54 | """Parses permissions from decoded AndroidManifest.xml.""" 55 | try: 56 | root = ET.fromstring(manifest_data) 57 | permissions = { 58 | elem.attrib["{http://schemas.android.com/apk/res/android}name"] 59 | for elem in root.findall(".//uses-permission") 60 | } 61 | return list(permissions) 62 | except Exception as e: 63 | logging.error(f"❌ Failed to parse permissions: {e}") 64 | return [] 65 | 66 | 67 | def classify_permissions(permissions): 68 | """Classifies permissions into normal, dangerous, critical, and unknown.""" 69 | categorized = {"normal": [], "dangerous": [], "critical": [], "unknown": []} 70 | 71 | for perm in permissions: 72 | found = False 73 | for category, perms in PERMISSION_CATEGORIES.items(): 74 | if perm in perms: 75 | categorized[category].append( 76 | (perm, perms[perm]) 77 | ) # Store with description 78 | found = True 79 | break 80 | if not found: 81 | categorized["unknown"].append( 82 | (perm, "Unknown permission - may require further analysis.") 83 | ) 84 | 85 | return categorized 86 | 87 | 88 | def display_permissions(permissions): 89 | """Displays classified permissions with descriptions.""" 90 | categorized = classify_permissions(permissions) 91 | 92 | print("\n✅ **Permissions Found:**\n") 93 | 94 | if categorized["normal"]: 95 | print("🔵 **Normal Permissions:**") 96 | for perm, desc in categorized["normal"]: 97 | print(f" - {perm}: {desc}") 98 | 99 | if categorized["dangerous"]: 100 | print("\n🟠 **Dangerous Permissions:**") 101 | for perm, desc in categorized["dangerous"]: 102 | print(f" - {perm}: {desc}") 103 | 104 | if categorized["critical"]: 105 | print("\n🔴 **Critical Permissions:**") 106 | for perm, desc in categorized["critical"]: 107 | print(f" - {perm}: {desc}") 108 | 109 | if categorized["unknown"]: 110 | print("\n⚠️ **Unknown Permissions:** (Might need further analysis)") 111 | for perm, desc in categorized["unknown"]: 112 | print(f" - {perm}: {desc}") 113 | 114 | 115 | def scan_permissions(apk_path): 116 | """Scans APK for permissions and analyzes their risks.""" 117 | print(f"\n🔍 Scanning permissions in: {apk_path}") 118 | 119 | manifest_data = extract_manifest(apk_path) 120 | if not manifest_data: 121 | return 122 | 123 | permissions = parse_permissions(manifest_data) 124 | if not permissions: 125 | logging.warning("⚠️ No permissions found in AndroidManifest.xml.") 126 | return 127 | 128 | display_permissions(permissions) 129 | -------------------------------------------------------------------------------- /apknife/modules/extractor.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import logging 3 | import os 4 | import re 5 | import shutil 6 | import subprocess 7 | import tempfile 8 | 9 | logging.basicConfig( 10 | level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" 11 | ) 12 | 13 | 14 | def check_dependency(tool: str) -> bool: 15 | """Check if a required tool is installed.""" 16 | result = subprocess.run( 17 | f"command -v {tool}", shell=True, capture_output=True, text=True 18 | ) 19 | if result.returncode != 0: 20 | logging.error(f"❌ Missing dependency: {tool}. Please install it.") 21 | return False 22 | return True 23 | 24 | 25 | def extract_apk(apk_path: str, output_dir: str): 26 | """Extract APK contents using apktool.""" 27 | if not os.path.isfile(apk_path): 28 | logging.error("APK file does not exist.") 29 | return [] 30 | 31 | try: 32 | with tempfile.TemporaryDirectory() as temp_dir: 33 | # Extract APK using apktool 34 | command = f"apktool d -f -r {apk_path} -o {temp_dir}" 35 | subprocess.run(command, shell=True, check=True) 36 | 37 | # Handle advanced protections like obfuscation 38 | handle_advanced_protections(temp_dir) 39 | 40 | if os.path.exists(output_dir): 41 | shutil.rmtree(output_dir) 42 | shutil.move(temp_dir, output_dir) 43 | 44 | logging.info(f"✅ APK extracted to {output_dir}") 45 | return output_dir 46 | 47 | except subprocess.CalledProcessError as e: 48 | logging.error(f"❌ Error extracting APK: {e}") 49 | except Exception as e: 50 | logging.error(f"❌ General error extracting APK: {e}") 51 | 52 | return None 53 | 54 | 55 | def handle_advanced_protections(temp_dir: str): 56 | """Handle APK protections like obfuscation and anti-debugging.""" 57 | 58 | if ( 59 | not check_dependency("d2j-dex2jar") 60 | or not check_dependency("jadx") 61 | or not check_dependency("baksmali") 62 | ): 63 | return 64 | 65 | dex_files = [ 66 | os.path.join(temp_dir, f) for f in os.listdir(temp_dir) if f.endswith(".dex") 67 | ] 68 | 69 | for dex_file in dex_files: 70 | jar_file = dex_file.replace(".dex", ".jar") 71 | try: 72 | # Convert DEX to JAR 73 | subprocess.run(["d2j-dex2jar", dex_file, "-o", jar_file], check=True) 74 | logging.info(f"🔹 Converted {dex_file} to {jar_file}") 75 | 76 | # Decompile JAR to Java code using JADX 77 | jadx_out = jar_file.replace(".jar", "_jadx") 78 | subprocess.run(["jadx", "-d", jadx_out, jar_file], check=True) 79 | logging.info(f"🔹 Decompiled {jar_file} with JADX to {jadx_out}") 80 | 81 | except subprocess.CalledProcessError as e: 82 | logging.error(f"❌ Error processing {dex_file}: {e}") 83 | 84 | 85 | def extract_native_libraries(output_dir: str): 86 | """Extract and analyze native libraries (.so files).""" 87 | so_files = [] 88 | for root, _, files in os.walk(output_dir): 89 | for file in files: 90 | if file.endswith(".so"): 91 | so_files.append(os.path.join(root, file)) 92 | 93 | for so_file in so_files: 94 | logging.info(f"🔹 Found native library: {so_file}") 95 | 96 | # Disassemble .so file 97 | disasm_file = so_file + ".disasm" 98 | try: 99 | subprocess.run( 100 | ["objdump", "-d", so_file, "-M", "intel", "-o", disasm_file], check=True 101 | ) 102 | logging.info(f"✅ Disassembled {so_file} -> {disasm_file}") 103 | 104 | # Radare2 analysis 105 | subprocess.run(["r2", "-c", '"aaa; afl; pdf @ main"', so_file], check=True) 106 | except subprocess.CalledProcessError as e: 107 | logging.error(f"❌ Error analyzing {so_file}: {e}") 108 | 109 | 110 | def bypass_anti_debug(output_dir: str): 111 | """Patch anti-debugging mechanisms inside the APK.""" 112 | smali_files = [] 113 | for root, _, files in os.walk(output_dir): 114 | for file in files: 115 | if file.endswith(".smali"): 116 | smali_files.append(os.path.join(root, file)) 117 | 118 | for smali_file in smali_files: 119 | with open(smali_file, "r") as f: 120 | content = f.read() 121 | 122 | # Remove ptrace anti-debugging 123 | if "ptrace" in content: 124 | patched_content = content.replace("ptrace", "// ptrace removed") 125 | with open(smali_file, "w") as f: 126 | f.write(patched_content) 127 | logging.info(f"✅ Patched anti-debugging in {smali_file}") 128 | 129 | 130 | def detect_and_decrypt_strings(output_dir: str): 131 | """Detect and decrypt Base64 and XOR-encoded strings inside the APK.""" 132 | for root, _, files in os.walk(output_dir): 133 | for file in files: 134 | if file.endswith(".smali") or file.endswith(".xml"): 135 | file_path = os.path.join(root, file) 136 | 137 | with open(file_path, "r") as f: 138 | content = f.read() 139 | 140 | # Detect Base64 encoded strings 141 | base64_matches = re.findall(r'"([A-Za-z0-9+/=]{16,})"', content) 142 | for match in base64_matches: 143 | try: 144 | decoded = base64.b64decode(match).decode("utf-8") 145 | logging.info( 146 | f"🔹 Found Base64 encoded string: {match} -> {decoded}" 147 | ) 148 | except Exception: 149 | pass # Ignore decoding errors 150 | 151 | 152 | # Example usage 153 | if __name__ == "__main__": 154 | apk_path = "path/to/apkfile.apk" 155 | output_dir = "path/to/output_dir" 156 | 157 | extracted_folder = extract_apk(apk_path, output_dir) 158 | if extracted_folder: 159 | extract_native_libraries(extracted_folder) 160 | bypass_anti_debug(extracted_folder) 161 | detect_and_decrypt_strings(extracted_folder) 162 | -------------------------------------------------------------------------------- /apknife/modules/apk_modifier.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import subprocess 4 | import sys 5 | import re 6 | from PIL import Image 7 | 8 | class APKModifier: 9 | def __init__(self, apk_path=None, new_name=None, icon_path=None, new_package=None): 10 | self.apk_path = apk_path 11 | self.new_name = new_name 12 | self.icon_path = icon_path 13 | self.new_package = new_package 14 | self.decompiled_dir = "decompiled_apk" 15 | self.output_apk = "modified.apk" 16 | self.icon_sizes = { 17 | "mipmap-mdpi": (48, 48), 18 | "mipmap-hdpi": (72, 72), 19 | "mipmap-xhdpi": (96, 96), 20 | "mipmap-xxhdpi": (144, 144), 21 | "mipmap-xxxhdpi": (192, 192), 22 | } 23 | 24 | def decompile_if_needed(self): 25 | """Decompile the APK if it's not already decompiled.""" 26 | if not os.path.isdir(self.apk_path): 27 | print("[*] APK detected, decompiling...") 28 | if os.path.exists(self.decompiled_dir): 29 | shutil.rmtree(self.decompiled_dir) 30 | subprocess.run(["apktool", "d", self.apk_path, "-o", self.decompiled_dir], check=True) 31 | self.apk_path = self.decompiled_dir 32 | else: 33 | print("[*] Using existing decompiled directory.") 34 | 35 | def modify_app_name(self): 36 | """Modify the app name inside AndroidManifest.xml""" 37 | if not self.new_name: 38 | return 39 | 40 | print(f"[*] Changing app name to: {self.new_name}") 41 | manifest_path = os.path.join(self.apk_path, "AndroidManifest.xml") 42 | 43 | with open(manifest_path, "r", encoding="utf-8") as file: 44 | content = file.read() 45 | 46 | content = re.sub(r'android:label="([^"]+)"', f'android:label="{self.new_name}"', content) 47 | 48 | with open(manifest_path, "w", encoding="utf-8") as file: 49 | file.write(content) 50 | 51 | def modify_icon(self): 52 | """Replace the app icon with automatic resizing and format conversion.""" 53 | if not self.icon_path: 54 | return 55 | 56 | print(f"[*] Changing app icon using: {self.icon_path}") 57 | try: 58 | icon = Image.open(self.icon_path).convert("RGBA") # Ensuring transparency support 59 | except Exception as e: 60 | print(f"[!] Error loading icon: {e}") 61 | return 62 | 63 | # Convert to PNG if necessary 64 | if not self.icon_path.lower().endswith(".png"): 65 | self.icon_path = self.icon_path.rsplit(".", 1)[0] + ".png" 66 | icon.save(self.icon_path, "PNG") 67 | print(f"[*] Converted icon to PNG: {self.icon_path}") 68 | 69 | for res_dir, size in self.icon_sizes.items(): 70 | icon_resized = icon.resize(size, Image.ANTIALIAS) 71 | icon_path = os.path.join(self.apk_path, "res", res_dir, "ic_launcher.png") 72 | if os.path.exists(icon_path): 73 | icon_resized.save(icon_path, "PNG") 74 | print(f"[*] Icon resized and saved to: {icon_path}") 75 | 76 | def modify_package_name(self): 77 | """Modify Package Name in AndroidManifest.xml and smali files.""" 78 | if not self.new_package: 79 | return 80 | 81 | print(f"[*] Changing package name to: {self.new_package}") 82 | manifest_path = os.path.join(self.apk_path, "AndroidManifest.xml") 83 | 84 | with open(manifest_path, "r", encoding="utf-8") as file: 85 | content = file.read() 86 | 87 | # Extract the current package name 88 | old_package_match = re.search(r'package="([^"]+)"', content) 89 | if not old_package_match: 90 | print("[!] Package name not found!") 91 | return 92 | 93 | old_package = old_package_match.group(1) 94 | content = content.replace(old_package, self.new_package) 95 | 96 | with open(manifest_path, "w", encoding="utf-8") as file: 97 | file.write(content) 98 | 99 | # Update package name in smali files 100 | smali_dir = os.path.join(self.apk_path, "smali") 101 | if os.path.exists(smali_dir): 102 | old_package_path = old_package.replace(".", "/") 103 | new_package_path = self.new_package.replace(".", "/") 104 | for root, dirs, files in os.walk(smali_dir): 105 | for file in files: 106 | if file.endswith(".smali"): 107 | smali_file = os.path.join(root, file) 108 | with open(smali_file, "r", encoding="utf-8") as f: 109 | smali_content = f.read() 110 | smali_content = smali_content.replace(old_package_path, new_package_path) 111 | with open(smali_file, "w", encoding="utf-8") as f: 112 | f.write(smali_content) 113 | 114 | # Rename package directories in smali 115 | old_package_dir = os.path.join(smali_dir, *old_package.split(".")) 116 | new_package_dir = os.path.join(smali_dir, *self.new_package.split(".")) 117 | if os.path.exists(old_package_dir): 118 | shutil.move(old_package_dir, new_package_dir) 119 | 120 | def recompile_if_needed(self): 121 | """Recompile the APK if it was decompiled.""" 122 | if os.path.isdir(self.apk_path): 123 | print("[*] Recompiling modified APK...") 124 | subprocess.run(["apktool", "b", self.apk_path, "-o", self.output_apk], check=True) 125 | print(f"[✔] Modified APK saved as: {self.output_apk}") 126 | 127 | def run(self): 128 | self.decompile_if_needed() 129 | self.modify_app_name() 130 | self.modify_icon() 131 | self.modify_package_name() 132 | self.recompile_if_needed() 133 | print("[✔] Process completed!") 134 | 135 | # Run from command line 136 | if __name__ == "__main__": 137 | if len(sys.argv) < 2: 138 | print("Usage: python modifier.py [--name ] [--icon ] [--package ]") 139 | sys.exit(1) 140 | 141 | apk_path = sys.argv[1] 142 | new_name = None 143 | icon_path = None 144 | new_package = None 145 | 146 | if "--name" in sys.argv: 147 | new_name = sys.argv[sys.argv.index("--name") + 1] 148 | 149 | if "--icon" in sys.argv: 150 | icon_path = sys.argv[sys.argv.index("--icon") + 1] 151 | 152 | if "--package" in sys.argv: 153 | new_package = sys.argv[sys.argv.index("--package") + 1] 154 | 155 | modifier = APKModifier(apk_path, new_name, icon_path, new_package) 156 | modifier.run() 157 | -------------------------------------------------------------------------------- /apknife/modules/manifest_editor.py: -------------------------------------------------------------------------------- 1 | import curses 2 | import os 3 | import tempfile 4 | import xml.etree.ElementTree as ET 5 | import zipfile 6 | 7 | from androguard.core.axml import AXMLPrinter 8 | 9 | 10 | def extract_manifest_from_apk(apk_path, output_dir): 11 | """Extracts AndroidManifest.xml from an APK file.""" 12 | with zipfile.ZipFile(apk_path, "r") as zip_ref: 13 | if "AndroidManifest.xml" in zip_ref.namelist(): 14 | zip_ref.extract("AndroidManifest.xml", output_dir) 15 | return os.path.join(output_dir, "AndroidManifest.xml") 16 | else: 17 | raise FileNotFoundError("AndroidManifest.xml is missing inside the APK.") 18 | 19 | 20 | def decode_binary_xml(binary_xml_path): 21 | """Converts AndroidManifest.xml from binary (AXML) to readable XML.""" 22 | with open(binary_xml_path, "rb") as f: 23 | binary_xml = f.read() 24 | axml = AXMLPrinter(binary_xml) 25 | xml_data = axml.get_xml() 26 | 27 | if xml_data is None: 28 | raise ValueError("Failed to decode AXML. The file may be corrupted.") 29 | 30 | return xml_data.decode("utf-8") 31 | 32 | 33 | def save_backup(original_path): 34 | """Creates a backup of the original APK before modification.""" 35 | backup_path = original_path + ".backup" 36 | if not os.path.exists(backup_path): 37 | os.rename(original_path, backup_path) 38 | return backup_path 39 | 40 | 41 | def encode_binary_xml(xml_str, output_path): 42 | """Converts readable XML back to AXML format.""" 43 | with open(output_path, "wb") as f: 44 | f.write(xml_str.encode("utf-8")) 45 | 46 | 47 | def update_apk_with_manifest(apk_path, manifest_path): 48 | """Updates an APK with the modified AndroidManifest.xml.""" 49 | backup_path = save_backup(apk_path) 50 | 51 | with zipfile.ZipFile(backup_path, "r") as zip_ref: 52 | zip_ref.extractall("temp_apk") 53 | 54 | with zipfile.ZipFile(apk_path, "w") as zip_ref: 55 | for root, _, files in os.walk("temp_apk"): 56 | for file in files: 57 | file_path = os.path.join(root, file) 58 | arcname = os.path.relpath(file_path, "temp_apk") 59 | zip_ref.write(file_path, arcname) 60 | 61 | print("✅ APK successfully updated!") 62 | 63 | 64 | def start_editor(stdscr, manifest_path): 65 | """A curses-based editor for modifying AndroidManifest.xml.""" 66 | try: 67 | curses.curs_set(1) 68 | curses.start_color() 69 | curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK) 70 | 71 | stdscr.clear() 72 | stdscr.refresh() 73 | 74 | with open(manifest_path, "r", encoding="utf-8") as f: 75 | lines = f.readlines() 76 | 77 | cursor_x, cursor_y = 0, 0 78 | max_y, max_x = stdscr.getmaxyx() 79 | 80 | instructions = "↑↓←→ Move - ENTER New Line - BACKSPACE Delete - S Save - Q Exit" 81 | 82 | while True: 83 | stdscr.clear() 84 | stdscr.addstr(0, 0, instructions, curses.A_BOLD) 85 | 86 | for i, line in enumerate(lines[: max_y - 2]): 87 | stdscr.addstr(i + 1, 0, line[: max_x - 1]) 88 | 89 | stdscr.move(cursor_y + 1, cursor_x) 90 | stdscr.refresh() 91 | 92 | key = stdscr.getch() 93 | 94 | if key == curses.KEY_UP and cursor_y > 0: 95 | cursor_y -= 1 96 | elif key == curses.KEY_DOWN and cursor_y < len(lines) - 1: 97 | cursor_y += 1 98 | elif key == curses.KEY_LEFT and cursor_x > 0: 99 | cursor_x -= 1 100 | elif key == curses.KEY_RIGHT and cursor_x < len(lines[cursor_y]) - 1: 101 | cursor_x += 1 102 | elif key == ord("\n"): 103 | lines.insert(cursor_y + 1, "\n") 104 | cursor_y += 1 105 | cursor_x = 0 106 | elif key == 127 or key == curses.KEY_BACKSPACE: 107 | if cursor_x > 0: 108 | lines[cursor_y] = ( 109 | lines[cursor_y][: cursor_x - 1] + lines[cursor_y][cursor_x:] 110 | ) 111 | cursor_x -= 1 112 | elif cursor_y > 0: 113 | lines[cursor_y - 1] += lines[cursor_y] 114 | del lines[cursor_y] 115 | cursor_y -= 1 116 | cursor_x = len(lines[cursor_y]) 117 | elif key == ord("s"): 118 | try: 119 | with open(manifest_path, "w", encoding="utf-8") as f: 120 | f.writelines(lines) 121 | stdscr.addstr( 122 | max_y - 1, 0, "✅ Changes saved! Press any key to continue." 123 | ) 124 | stdscr.refresh() 125 | stdscr.getch() 126 | except Exception as e: 127 | stdscr.addstr( 128 | max_y - 1, 0, f"❌ Save error: {e}", curses.color_pair(1) 129 | ) 130 | stdscr.refresh() 131 | stdscr.getch() 132 | elif key == ord("q"): 133 | break 134 | elif 32 <= key <= 126: 135 | lines[cursor_y] = ( 136 | lines[cursor_y][:cursor_x] + chr(key) + lines[cursor_y][cursor_x:] 137 | ) 138 | cursor_x += 1 139 | except Exception as e: 140 | stdscr.addstr(max_y - 1, 0, f"❌ Error: {e}", curses.color_pair(1)) 141 | stdscr.refresh() 142 | stdscr.getch() 143 | 144 | 145 | def edit_manifest(file_path): 146 | """Edits AndroidManifest.xml inside an APK or a decompiled app folder.""" 147 | if not os.path.exists(file_path): 148 | print(f"❌ File not found: {file_path}") 149 | return 150 | 151 | with tempfile.TemporaryDirectory() as temp_dir: 152 | try: 153 | if file_path.endswith(".apk"): 154 | # Extract manifest from APK 155 | manifest_path = extract_manifest_from_apk(file_path, temp_dir) 156 | print("✅ Extracted AndroidManifest.xml successfully.") 157 | 158 | # Decode AXML 159 | decoded_xml = decode_binary_xml(manifest_path) 160 | decoded_xml_path = os.path.join(temp_dir, "decoded_AndroidManifest.xml") 161 | 162 | with open(decoded_xml_path, "w", encoding="utf-8") as f: 163 | f.write(decoded_xml) 164 | print("✅ Converted AndroidManifest.xml to readable text.") 165 | 166 | else: 167 | # If it's a folder, assume the manifest is already extracted 168 | manifest_path = os.path.join(file_path, "AndroidManifest.xml") 169 | if not os.path.exists(manifest_path): 170 | raise FileNotFoundError( 171 | "AndroidManifest.xml not found in the given folder." 172 | ) 173 | 174 | decoded_xml_path = manifest_path # Directly edit it 175 | 176 | # Open editor 177 | curses.wrapper(lambda stdscr: start_editor(stdscr, decoded_xml_path)) 178 | 179 | # Read modified XML 180 | with open(decoded_xml_path, "r", encoding="utf-8") as f: 181 | modified_xml = f.read() 182 | 183 | # Convert back to AXML 184 | encoded_xml_path = os.path.join(temp_dir, "encoded_AndroidManifest.xml") 185 | encode_binary_xml(modified_xml, encoded_xml_path) 186 | print("✅ Converted AndroidManifest.xml back to AXML successfully.") 187 | 188 | # If it's an APK, update it 189 | if file_path.endswith(".apk"): 190 | update_apk_with_manifest(file_path, encoded_xml_path) 191 | print("✅ Updated APK with modified AndroidManifest.xml.") 192 | 193 | except FileNotFoundError as e: 194 | print(f"❌ Error: {e}") 195 | except Exception as e: 196 | print(f"❌ Unexpected error: {e}") 197 | 198 | 199 | # Example Usage: 200 | # edit_manifest("/path/to/app.apk") -> Modify APK Manifest 201 | # edit_manifest("/path/to/decompiled_app/") -> Modify extracted manifest 202 | -------------------------------------------------------------------------------- /apknife.py: -------------------------------------------------------------------------------- 1 | import json 2 | import argparse 3 | import logging 4 | import os 5 | import subprocess 6 | import sys 7 | import time 8 | from prompt_toolkit import PromptSession 9 | from prompt_toolkit.auto_suggest import AutoSuggestFromHistory 10 | from prompt_toolkit.completion import WordCompleter 11 | from prompt_toolkit.history import FileHistory 12 | from prompt_toolkit.styles import Style 13 | from apknife.modules import ( 14 | analyzer, api_finder, builder, catch_rat, extract_sensitive, extractor, 15 | java_extractor, manifest_editor, permission_scanner, signer, smali_tools, 16 | vulnerability_scanner, dex_extractor, manifest_decoder 17 | ) 18 | from apknife.modules.interactive_mode import interactive_shell 19 | from apknife.modules.apk_modifier import APKModifier 20 | 21 | # ANSI color codes for terminal output styling 22 | RED = "\033[91m" 23 | GREEN = "\033[92m" 24 | YELLOW = "\033[93m" 25 | BLUE = "\033[94m" 26 | PURPLE = "\033[95m" 27 | CYAN = "\033[96m" 28 | WHITE = "\033[97m" 29 | RESET = "\033[0m" 30 | 31 | # Logging setup 32 | logging.basicConfig(level=logging.INFO, format="%(message)s") 33 | 34 | # Banner ASCII Art 35 | BANNER = f"""{RED} 36 | APKnife: The Cyber Blade of APK Domination 37 | @@@@@@ @@@@@@@ @@@ @@@ @@@ @@@ @@@ @@@@@@@@ @@@@@@@@ 38 | @@@@@@@@ @@@@@@@@ @@@ @@@ @@@@ @@@ @@@ @@@@@@@@ @@@@@@@@ 39 | @@! @@@ @@! @@@ @@! !@@ @@!@!@@@ @@! @@! @@! 40 | !@! @!@ !@! @!@ !@! @!! !@!!@!@! !@! !@! !@! 41 | @!@!@!@! @!@@!@! @!@@!@! @!@ !!@! !!@ @!!!:! @!!!:! 42 | !!!@!!!! !!@!!! !!@!!! !@! !!! !!! !!!!!: !!!!!: 43 | !!: !!! !!: !!: :!! !!: !!! !!: !!: !!: 44 | :!: !:! :!: :!: !:! :!: !:! :!: :!: :!: 45 | :: ::: :: :: ::: :: :: :: :: :: :::: 46 | : : : : : ::: :: : : : : :: ::: 47 | 48 | {RESET}{CYAN} APKnife – The Double-Edged Blade of APK Analysis 🔪🧸 49 | {YELLOW} Fear the Blade, Trust the Power! 🎨 50 | {WHITE} Where Hacking Meets Art! 🖌️ 51 | """ 52 | 53 | # Animated loading effect 54 | def loading_effect(text, delay=0.1): 55 | for char in text: 56 | sys.stdout.write(char) 57 | sys.stdout.flush() 58 | time.sleep(delay) 59 | print() 60 | 61 | # Display the banner 62 | def display_banner(): 63 | print(BANNER) 64 | loading_effect(f"{PURPLE}⚙️ Loading the blade...", 0.05) 65 | loading_effect(f"{BLUE}🔪 Sharpening edges...", 0.07) 66 | loading_effect(f"{GREEN}🟢 Ready to cut!", 0.1) 67 | print(RESET) 68 | 69 | # Load commands from external file 70 | def load_commands(): 71 | if not os.path.exists("commands.json"): 72 | logging.warning(f"{YELLOW}[!] commands.json not found. Creating a default one...{RESET}") 73 | default_commands = { 74 | "help": "Displays this help menu", 75 | "exit": "Exits the interactive mode", 76 | "update-commands": "Reloads the commands from the external file", 77 | "list-commands": "Displays the current list of available commands", 78 | "extract-dex": "Extract DEX files from an APK without fully decompiling it", 79 | "decode_manifest": "Decode AndroidManifest.xml without fully decompiling the APK", 80 | "waf": "Scan the app for protection mechanisms (e.g., Firewall, ProGuard, etc.)", 81 | "analyze": "Analyze the APK for security vulnerabilities", 82 | "build": "Rebuild an APK from extracted files", 83 | "sign": "Sign an APK file", 84 | "edit-manifest": "Edit the AndroidManifest.xml of an APK", 85 | "smali": "Decompile an APK to Smali code", 86 | "find-oncreate": "Find onCreate methods in an APK", 87 | "find-api": "Find API calls in an APK", 88 | "scan-vulnerabilities": "Scan an APK for vulnerabilities", 89 | "scan-permissions": "Scan and list permissions used by an APK", 90 | "catch_rat": "Analyze an APK for Remote Access Trojan (RAT) indicators", 91 | "extract-java": "Extract Java source code from an APK", 92 | "extract-sensitive": "Extract sensitive data from an APK", 93 | "modify-apk": "Modify an APK's metadata (name, icon, package name)", 94 | "extract-dex": "Extract DEX files from an APK", 95 | } 96 | with open("commands.json", "w") as file: 97 | json.dump(default_commands, file, indent=4) 98 | return default_commands 99 | 100 | try: 101 | with open("commands.json", "r") as file: 102 | return json.load(file) 103 | except json.JSONDecodeError: 104 | logging.error(f"{RED}[!] Invalid JSON format in commands file!{RESET}") 105 | return {} 106 | 107 | def main(): 108 | parser = argparse.ArgumentParser( 109 | description="APKnife: Advanced APK analysis & modification tool" 110 | ) 111 | 112 | parser.add_argument( 113 | "command", 114 | choices=[ 115 | "extract", "build", "sign", "analyze", "edit-manifest", "smali", 116 | "decode_manifest", "find-oncreate", "find-api", "scan-vulnerabilities", 117 | "scan-permissions", "catch_rat", "extract-java", "interactive", 118 | "extract-sensitive", "modify-apk", "extract-dex", "waf" # Added the new command 119 | ], 120 | help="Command to execute", 121 | ) 122 | 123 | parser.add_argument("-i", "--input", help="Input APK file") 124 | parser.add_argument("-o", "--output", help="Output file/directory") 125 | parser.add_argument( 126 | "-c", "--compress", action="store_true", 127 | help="Compress extracted Java files into a ZIP archive", 128 | ) 129 | 130 | # Additional arguments for APK modification 131 | parser.add_argument("--name", help="New app name") 132 | parser.add_argument("--icon", help="New app icon (resized automatically)") 133 | parser.add_argument("--package", help="New package name") 134 | 135 | args = parser.parse_args() 136 | 137 | # Ensure input file is provided for required commands 138 | if args.command != "interactive" and not args.input: 139 | logging.error(f"{RED}[!] You must specify an input file using `-i`{RESET}") 140 | sys.exit(1) 141 | 142 | # Execute the selected command 143 | try: 144 | if args.command == "interactive": 145 | display_banner() 146 | COMMANDS = load_commands() 147 | interactive_shell(COMMANDS) # Pass COMMANDS to interactive_shell 148 | elif args.command == "extract": 149 | extractor.extract_apk(args.input, args.output) 150 | elif args.command == "build": 151 | builder.build_apk(args.input, args.output) 152 | elif args.command == "sign": 153 | signer.sign_apk(args.input) 154 | elif args.command == "analyze": 155 | analyzer.analyze_apk(args.input) 156 | elif args.command == "edit-manifest": 157 | manifest_editor.edit_manifest(args.input) 158 | elif args.command == "smali": 159 | smali_tools.decompile_apk(args.input, args.output) 160 | elif args.command == "decode_manifest": 161 | manifest_decoder.decode_manifest(args.input, args.output) 162 | elif args.command == "find-oncreate": 163 | smali_tools.find_oncreate(args.input) 164 | elif args.command == "find-api": 165 | api_finder.find_api_calls(args.input) 166 | elif args.command == "scan-vulnerabilities": 167 | vulnerability_scanner.scan_apk(args.input) 168 | elif args.command == "scan-permissions": 169 | permission_scanner.scan_permissions(args.input) 170 | elif args.command == "catch_rat": 171 | catch_rat.analyze_apk_ips(args.input) 172 | elif args.command == "extract-java": 173 | java_extractor.extract_java(args.input, args.output, args.compress) 174 | elif args.command == "extract-sensitive": 175 | if not args.output: 176 | args.output = "sensitive_report.json" 177 | extract_sensitive.extract_sensitive_data(args.input, args.output) 178 | elif args.command == "modify-apk": 179 | logging.info(f"{GREEN}[*] Modifying APK: {args.input}{RESET}") 180 | modifier = APKModifier(args.input, args.name, args.icon, args.package) 181 | modifier.run() 182 | elif args.command == "extract-dex": 183 | logging.info(f"{GREEN}[*] Extracting DEX files from: {args.input}{RESET}") 184 | dex_files = dex_extractor.extract_dex(args.input, args.output) 185 | if dex_files: 186 | logging.info(f"{GREEN}[*] DEX files extracted successfully: {dex_files}{RESET}") 187 | else: 188 | logging.error(f"{RED}[!] Failed to extract DEX files{RESET}") 189 | elif args.command == "waf": 190 | from apknife.modules.protection_scanner import scan_apk_protections 191 | protections = scan_apk_protections(args.input) 192 | for protection, status in protections.items(): 193 | print(f"{protection}: {'✅' if status else '❌'}") 194 | else: 195 | logging.error(f"{RED}[!] Unknown command!{RESET}") 196 | 197 | except Exception as e: 198 | logging.error(f"{RED}[!] Error executing `{args.command}`: {e}{RESET}") 199 | sys.exit(1) 200 | 201 | if __name__ == "__main__": 202 | main() 203 | -------------------------------------------------------------------------------- /apknife/apknife.py: -------------------------------------------------------------------------------- 1 | import json 2 | import argparse 3 | import logging 4 | import os 5 | import subprocess 6 | import sys 7 | import time 8 | from prompt_toolkit import PromptSession 9 | from prompt_toolkit.auto_suggest import AutoSuggestFromHistory 10 | from prompt_toolkit.completion import WordCompleter 11 | from prompt_toolkit.history import FileHistory 12 | from prompt_toolkit.styles import Style 13 | from apknife.modules import ( 14 | analyzer, api_finder, builder, catch_rat, extract_sensitive, extractor, 15 | java_extractor, manifest_editor, permission_scanner, signer, smali_tools, 16 | vulnerability_scanner, dex_extractor, manifest_decoder 17 | ) 18 | from apknife.modules.interactive_mode import interactive_shell 19 | from apknife.modules.apk_modifier import APKModifier 20 | 21 | # ANSI color codes for terminal output styling 22 | RED = "\033[91m" 23 | GREEN = "\033[92m" 24 | YELLOW = "\033[93m" 25 | BLUE = "\033[94m" 26 | PURPLE = "\033[95m" 27 | CYAN = "\033[96m" 28 | WHITE = "\033[97m" 29 | RESET = "\033[0m" 30 | 31 | # Logging setup 32 | logging.basicConfig(level=logging.INFO, format="%(message)s") 33 | 34 | # Banner ASCII Art 35 | BANNER = f"""{RED} 36 | APKnife: The Cyber Blade of APK Domination 37 | @@@@@@ @@@@@@@ @@@ @@@ @@@ @@@ @@@ @@@@@@@@ @@@@@@@@ 38 | @@@@@@@@ @@@@@@@@ @@@ @@@ @@@@ @@@ @@@ @@@@@@@@ @@@@@@@@ 39 | @@! @@@ @@! @@@ @@! !@@ @@!@!@@@ @@! @@! @@! 40 | !@! @!@ !@! @!@ !@! @!! !@!!@!@! !@! !@! !@! 41 | @!@!@!@! @!@@!@! @!@@!@! @!@ !!@! !!@ @!!!:! @!!!:! 42 | !!!@!!!! !!@!!! !!@!!! !@! !!! !!! !!!!!: !!!!!: 43 | !!: !!! !!: !!: :!! !!: !!! !!: !!: !!: 44 | :!: !:! :!: :!: !:! :!: !:! :!: :!: :!: 45 | :: ::: :: :: ::: :: :: :: :: :: :::: 46 | : : : : : ::: :: : : : : :: ::: 47 | 48 | {RESET}{CYAN} APKnife – The Double-Edged Blade of APK Analysis 🔪🧸 49 | {YELLOW} Fear the Blade, Trust the Power! 🎨 50 | {WHITE} Where Hacking Meets Art! 🖌️ 51 | """ 52 | 53 | # Animated loading effect 54 | def loading_effect(text, delay=0.1): 55 | for char in text: 56 | sys.stdout.write(char) 57 | sys.stdout.flush() 58 | time.sleep(delay) 59 | print() 60 | 61 | # Display the banner 62 | def display_banner(): 63 | print(BANNER) 64 | loading_effect(f"{PURPLE}⚙️ Loading the blade...", 0.05) 65 | loading_effect(f"{BLUE}🔪 Sharpening edges...", 0.07) 66 | loading_effect(f"{GREEN}🟢 Ready to cut!", 0.1) 67 | print(RESET) 68 | 69 | # Load commands from external file 70 | def load_commands(): 71 | if not os.path.exists("commands.json"): 72 | logging.warning(f"{YELLOW}[!] commands.json not found. Creating a default one...{RESET}") 73 | default_commands = { 74 | "help": "Displays this help menu", 75 | "exit": "Exits the interactive mode", 76 | "update-commands": "Reloads the commands from the external file", 77 | "list-commands": "Displays the current list of available commands", 78 | "extract-dex": "Extract DEX files from an APK without fully decompiling it", 79 | "decode_manifest": "Decode AndroidManifest.xml without fully decompiling the APK", 80 | "waf": "Scan the app for protection mechanisms (e.g., Firewall, ProGuard, etc.)", 81 | "analyze": "Analyze the APK for security vulnerabilities", 82 | "build": "Rebuild an APK from extracted files", 83 | "sign": "Sign an APK file", 84 | "edit-manifest": "Edit the AndroidManifest.xml of an APK", 85 | "smali": "Decompile an APK to Smali code", 86 | "find-oncreate": "Find onCreate methods in an APK", 87 | "find-api": "Find API calls in an APK", 88 | "scan-vulnerabilities": "Scan an APK for vulnerabilities", 89 | "scan-permissions": "Scan and list permissions used by an APK", 90 | "catch_rat": "Analyze an APK for Remote Access Trojan (RAT) indicators", 91 | "extract-java": "Extract Java source code from an APK", 92 | "extract-sensitive": "Extract sensitive data from an APK", 93 | "modify-apk": "Modify an APK's metadata (name, icon, package name)", 94 | "extract-dex": "Extract DEX files from an APK", 95 | } 96 | with open("commands.json", "w") as file: 97 | json.dump(default_commands, file, indent=4) 98 | return default_commands 99 | 100 | try: 101 | with open("commands.json", "r") as file: 102 | return json.load(file) 103 | except json.JSONDecodeError: 104 | logging.error(f"{RED}[!] Invalid JSON format in commands file!{RESET}") 105 | return {} 106 | 107 | def main(): 108 | parser = argparse.ArgumentParser( 109 | description="APKnife: Advanced APK analysis & modification tool" 110 | ) 111 | 112 | parser.add_argument( 113 | "command", 114 | choices=[ 115 | "extract", "build", "sign", "analyze", "edit-manifest", "smali", 116 | "decode_manifest", "find-oncreate", "find-api", "scan-vulnerabilities", 117 | "scan-permissions", "catch_rat", "extract-java", "interactive", 118 | "extract-sensitive", "modify-apk", "extract-dex", "waf" # Added the new command 119 | ], 120 | help="Command to execute", 121 | ) 122 | 123 | parser.add_argument("-i", "--input", help="Input APK file") 124 | parser.add_argument("-o", "--output", help="Output file/directory") 125 | parser.add_argument( 126 | "-c", "--compress", action="store_true", 127 | help="Compress extracted Java files into a ZIP archive", 128 | ) 129 | 130 | # Additional arguments for APK modification 131 | parser.add_argument("--name", help="New app name") 132 | parser.add_argument("--icon", help="New app icon (resized automatically)") 133 | parser.add_argument("--package", help="New package name") 134 | 135 | args = parser.parse_args() 136 | 137 | # Ensure input file is provided for required commands 138 | if args.command != "interactive" and not args.input: 139 | logging.error(f"{RED}[!] You must specify an input file using `-i`{RESET}") 140 | sys.exit(1) 141 | 142 | # Execute the selected command 143 | try: 144 | if args.command == "interactive": 145 | display_banner() 146 | COMMANDS = load_commands() 147 | interactive_shell(COMMANDS) # Pass COMMANDS to interactive_shell 148 | elif args.command == "extract": 149 | extractor.extract_apk(args.input, args.output) 150 | elif args.command == "build": 151 | builder.build_apk(args.input, args.output) 152 | elif args.command == "sign": 153 | signer.sign_apk(args.input) 154 | elif args.command == "analyze": 155 | analyzer.analyze_apk(args.input) 156 | elif args.command == "edit-manifest": 157 | manifest_editor.edit_manifest(args.input) 158 | elif args.command == "smali": 159 | smali_tools.decompile_apk(args.input, args.output) 160 | elif args.command == "decode_manifest": 161 | manifest_decoder.decode_manifest(args.input, args.output) 162 | elif args.command == "find-oncreate": 163 | smali_tools.find_oncreate(args.input) 164 | elif args.command == "find-api": 165 | api_finder.find_api_calls(args.input) 166 | elif args.command == "scan-vulnerabilities": 167 | vulnerability_scanner.scan_apk(args.input) 168 | elif args.command == "scan-permissions": 169 | permission_scanner.scan_permissions(args.input) 170 | elif args.command == "catch_rat": 171 | catch_rat.analyze_apk_ips(args.input) 172 | elif args.command == "extract-java": 173 | java_extractor.extract_java(args.input, args.output, args.compress) 174 | elif args.command == "extract-sensitive": 175 | if not args.output: 176 | args.output = "sensitive_report.json" 177 | extract_sensitive.extract_sensitive_data(args.input, args.output) 178 | elif args.command == "modify-apk": 179 | logging.info(f"{GREEN}[*] Modifying APK: {args.input}{RESET}") 180 | modifier = APKModifier(args.input, args.name, args.icon, args.package) 181 | modifier.run() 182 | elif args.command == "extract-dex": 183 | logging.info(f"{GREEN}[*] Extracting DEX files from: {args.input}{RESET}") 184 | dex_files = dex_extractor.extract_dex(args.input, args.output) 185 | if dex_files: 186 | logging.info(f"{GREEN}[*] DEX files extracted successfully: {dex_files}{RESET}") 187 | else: 188 | logging.error(f"{RED}[!] Failed to extract DEX files{RESET}") 189 | elif args.command == "waf": 190 | from apknife.modules.protection_scanner import scan_apk_protections 191 | protections = scan_apk_protections(args.input) 192 | for protection, status in protections.items(): 193 | print(f"{protection}: {'✅' if status else '❌'}") 194 | else: 195 | logging.error(f"{RED}[!] Unknown command!{RESET}") 196 | 197 | except Exception as e: 198 | logging.error(f"{RED}[!] Error executing `{args.command}`: {e}{RESET}") 199 | sys.exit(1) 200 | 201 | if __name__ == "__main__": 202 | main() 203 | -------------------------------------------------------------------------------- /apknife/apknife.egg-info/PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 2.2 2 | Name: apknife 3 | Version: 1.0.11 4 | Summary: APKnife is an advanced tool for APK analysis, modification, and security auditing. Whether you're a security researcher, penetration tester, or Android developer, APKnife provides powerful features for reverse engineering, decompiling, modifying, and analyzing APK files. 5 | Author-email: Mr_nightmare 6 | License: MIT 7 | Project-URL: Homepage, https://github.com/elrashedy1992/APKnife 8 | Project-URL: Documentation, https://github.com/elrashedy1992/APKnife/wiki 9 | Project-URL: Source, https://github.com/elrashedy1992/APKnife 10 | Requires-Python: >=3.6 11 | Description-Content-Type: text/markdown 12 | License-File: LICENSE 13 | Requires-Dist: alembic==1.14.1 14 | Requires-Dist: androguard==4.1.3 15 | Requires-Dist: apkInspector==1.3.2 16 | Requires-Dist: asn1crypto==1.5.1 17 | Requires-Dist: asttokens==3.0.0 18 | Requires-Dist: banal==1.0.6 19 | Requires-Dist: certifi==2025.1.31 20 | Requires-Dist: cffi==1.17.1 21 | Requires-Dist: charset-normalizer==3.4.1 22 | Requires-Dist: click==8.1.8 23 | Requires-Dist: colorama==0.4.6 24 | Requires-Dist: contourpy==1.3.1 25 | Requires-Dist: cryptography==44.0.2 26 | Requires-Dist: cycler==0.12.1 27 | Requires-Dist: dataset==1.6.2 28 | Requires-Dist: decorator==5.2.1 29 | Requires-Dist: executing==2.2.0 30 | Requires-Dist: fonttools==4.56.0 31 | Requires-Dist: frida==16.6.6 32 | Requires-Dist: future==1.0.0 33 | Requires-Dist: greenlet==3.1.1 34 | Requires-Dist: idna==3.10 35 | Requires-Dist: ipython==9.0.1 36 | Requires-Dist: ipython_pygments_lexers==1.1.1 37 | Requires-Dist: jedi==0.19.2 38 | Requires-Dist: kiwisolver==1.4.8 39 | Requires-Dist: loguru==0.7.3 40 | Requires-Dist: lxml==5.3.1 41 | Requires-Dist: Mako==1.3.9 42 | Requires-Dist: MarkupSafe==3.0.2 43 | Requires-Dist: matplotlib==3.10.1 44 | Requires-Dist: matplotlib-inline==0.1.7 45 | Requires-Dist: mutf8==1.0.6 46 | Requires-Dist: networkx==3.4.2 47 | Requires-Dist: numpy==2.2.3 48 | Requires-Dist: packaging==24.2 49 | Requires-Dist: parso==0.8.4 50 | Requires-Dist: pexpect==4.9.0 51 | Requires-Dist: pillow==11.1.0 52 | Requires-Dist: prompt_toolkit==3.0.50 53 | Requires-Dist: ptyprocess==0.7.0 54 | Requires-Dist: pure_eval==0.2.3 55 | Requires-Dist: pycparser==2.22 56 | Requires-Dist: pydot==3.0.4 57 | Requires-Dist: Pygments==2.19.1 58 | Requires-Dist: pyparsing==3.2.1 59 | Requires-Dist: python-dateutil==2.9.0.post0 60 | Requires-Dist: PyYAML==6.0.2 61 | Requires-Dist: requests==2.32.3 62 | Requires-Dist: setuptools==75.8.2 63 | Requires-Dist: six==1.17.0 64 | Requires-Dist: SQLAlchemy==1.4.54 65 | Requires-Dist: stack-data==0.6.3 66 | Requires-Dist: tqdm==4.67.1 67 | Requires-Dist: traitlets==5.14.3 68 | Requires-Dist: typing_extensions==4.12.2 69 | Requires-Dist: urllib3==2.3.0 70 | Requires-Dist: wcwidth==0.2.13 71 | Requires-Dist: wheel==0.45.1 72 | 73 |

74 | APKnife Cover 75 |

76 | --- 77 | 78 | APKnife – The Double-Edged Blade of APK Analysis 🔪 79 | 80 | Fear the Blade, Trust the Power! 81 | 82 | APKnife is an advanced tool for APK analysis, modification, and security auditing. Whether you're a security researcher, penetration tester, or Android developer, APKnife provides powerful features for reverse engineering, decompiling, modifying, and analyzing APK files. 83 | 84 | 85 | --- 86 | 87 | 🚀 Features & Capabilities 88 | 89 | ✅ Extract & decompile APKs into readable formats 90 | ✅ Modify & repackage APKs effortlessly 91 | ✅ Analyze APKs for security vulnerabilities 92 | ✅ Edit AndroidManifest.xml & Smali code 93 | ✅ Extract Java source code from an APK 94 | ✅ Detect Remote Access Trojans (RATs) & malware 95 | ✅ Decode binary XML files & scan for API calls 96 | ✅ Change APK metadata (icon, name, package name) 97 | ✅ Identify security risks like excessive permissions 98 | ✅ Sign APKs for smooth installation 99 | 100 | 101 | --- 102 | 103 | 🔧 Installation 104 | 105 | 📌 Prerequisites 106 | 107 | Ensure you have the following installed on your system: 108 | 109 | Python 3.12 110 | 111 | Java (JDK 8 or later) 112 | 113 | apktool 114 | 115 | zipalign 116 | 117 | keytool 118 | 119 | 120 | 🛠 Setting Up a Python Virtual Environment 121 | 122 | Before installing apknife, it's recommended to set up a Python virtual environment to avoid package conflicts. 123 | 124 | 1️⃣ Create a Python Virtual Environment: 125 | 126 | python3 -m venv venv 127 | source venv/bin/activate # On Linux/macOS 128 | 129 | venv\Scripts\activate # On Windows 130 | 131 | 2️⃣ Install Required Packages 132 | 133 | Once the virtual environment is activated, install APKnife: 134 | 135 | pip install apknife 136 | 137 | 138 | --- 139 | 140 | 📥 Installing Rust (Required for APKnife) 141 | 142 | apknife requires Rust for building. Follow the installation steps based on your OS: 143 | 144 | 🐧 On Linux: 145 | ``` 146 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 147 | ``` 148 | Then follow the on-screen instructions. 149 | 150 | 🍏 On macOS (Using Homebrew): 151 | 152 | brew install rust 153 | 154 | 🖥️ On Windows: 155 | 156 | 1. Visit rustup.rs and install Rust. 157 | 158 | 159 | 2. Verify installation: 160 | 161 | 162 | ``` 163 | rustc --version 164 | ``` 165 | 166 | --- 167 | 168 | ⚠️ Troubleshooting Common Issues 169 | 170 | ❌ Issue Installing Rust on Termux 171 | 172 | Ensure Termux is up to date: 173 | 174 | pkg update && pkg upgrade 175 | 176 | Install required build tools: 177 | 178 | pkg install clang make python rust 179 | 180 | ❌ Issues Installing APKnife 181 | 182 | Rust not installed properly? Ensure it's correctly installed via rustup or your package manager. 183 | 184 | Python conflicts? If there are issues with virtual environments, reset it: 185 | 186 | ``` 187 | rm -rf venv 188 | python3 -m venv venv 189 | source venv/bin/activate 190 | ``` 191 | ✅ Verifying Installed Versions 192 | 193 | python --version 194 | rustc --version 195 | 196 | 197 | --- 198 | 199 | 🌍 Setting Up Rust Environment Variables 200 | 201 | 🐧 On Linux/macOS 202 | ``` 203 | nano ~/.bashrc # For bash 204 | ``` 205 | ``` 206 | nano ~/.zshrc # For zsh 207 | ``` 208 | Add this line at the end: 209 | 210 | ``` 211 | export PATH="$HOME/.cargo/bin:$PATH" 212 | ``` 213 | Apply changes: 214 | ``` 215 | source ~/.bashrc # For bash 216 | ``` 217 | ``` 218 | source ~/.zshrc # For zsh 219 | ``` 220 | 🖥️ On Windows 221 | 222 | 1. Open "Environment Variables" from the Start menu. 223 | 224 | 225 | 2. Edit Path under System Variables and add: 226 | 227 | 228 | ``` 229 | C:\Users\\.cargo\bin 230 | ``` 231 | 3. Click OK and restart your terminal. 232 | 233 | 234 | 235 | Verify the setup: 236 | ``` 237 | cargo --version 238 | ``` 239 | ``` 240 | rustc --version 241 | ``` 242 | 243 | 244 | --- 245 | 246 | 📥 Cloning the Repository & Installing Dependencies 247 | ``` 248 | git clone https://github.com/elrashedy1992/APKnife.git 249 | cd APKnife 250 | pip install -r requirements.txt 251 | ``` 252 | 253 | 254 | --- 255 | 256 | 📜 Usage 257 | 258 | 🖥️ Interactive Mode 259 | --- 260 | To enter interactive mode, run: 261 | ``` 262 | python3 apknife.py interactive 263 | ``` 264 | This will launch a command-line interface for executing APKnife commands. 265 | 266 | 267 | --- 268 | 269 | 🛠️ Available Commands 270 | 271 | 🟢 Extract APK Contents 272 | ``` 273 | python3 apknife.py extract -i target.apk -o extracted/ 274 | ``` 275 | 🟢 Modify & Rebuild APK 276 | ``` 277 | python3 apknife.py build -i 278 | extracted/ -o modified.apk 279 | ``` 280 | 🟢 Sign APK 281 | ``` 282 | python3 apknife.py sign -i modified.apk 283 | ``` 284 | 🟢 Analyze APK for Vulnerabilities 285 | ``` 286 | python3 apknife.py scan_vulnerabilities -i target.apk 287 | ``` 288 | 🟢 Detect Remote Access Trojans (RATs) 289 | ``` 290 | python3 apknife.py catch_rat -i malicious.apk 291 | ``` 292 | 🟢 Extract Java Source Code 293 | ``` 294 | python3 apknife.py extract-java -i target.apk -o src_folder 295 | ``` 296 | 🟢 Change APK Name 297 | ``` 298 | python3 apknife.py modify-apk --name -i app.apk 299 | ``` 300 | 🟢 Change APK Icon 301 | ``` 302 | python3 apknife.py modify-apk --icon new_icon.png -i app.apk 303 | ``` 304 | 🟢 Modify Package Name 305 | ``` 306 | python3 apknife.py modify-apk --package com.example.example -i app.apk 307 | ``` 308 | 309 | ``` 310 | python3 apknife.py modify-apk --name new_name --package new.package.name --icon anysize.any -o modified_apk.apk 311 | ``` 312 | 🟢 Scan APK Permissions 313 | ``` 314 | python3 apknife.py scan_permissions -i target.apk 315 | ``` 316 | 👇help menu👇 317 | 318 | --- 319 | python3 apknife.py -h 320 | usage: apknife.py [-h] [-i INPUT] [-o OUTPUT] [-c] [--name NAME] [--icon ICON] 321 | [--package PACKAGE] 322 | {extract,build,sign,analyze,edit-manifest,smali,decode-xml,find-oncreate,find-api,scan-vulnerabilities,scan-permissions,catch_rat,extract-java,interactive,extract-sensitive,modify-apk} 323 | 324 | APKnife: Advanced APK analysis & modification tool 325 | 326 | positional arguments: 327 | {extract,build,sign,analyze,edit-manifest,smali,decode-xml,find-oncreate,find-api,scan-vulnerabilities,scan-permissions,catch_rat,extract-java,interactive,extract-sensitive,modify-apk} 328 | Command to execute 329 | 330 | options: 331 | -h, --help show this help message and exit 332 | -i, --input INPUT Input APK file 333 | -o, --output OUTPUT Output file/directory 334 | -c, --compress Compress extracted Java files into a ZIP archive 335 | --name NAME New app name 336 | --icon ICON New app icon (resized automatically) 337 | --package PACKAGE New package name 338 | 339 | 340 | ⚠️ Legal Disclaimer 341 | 342 | This tool is designed for educational and security research purposes only. Unauthorized use of APKnife on third-party applications without permission is illegal. The developers are not responsible for any misuse. 343 | 344 | 345 | --- 346 | 347 | 📜 License 348 | 349 | APKnife is released under the MIT License – You are free to modify and distribute it for legal use. 350 | 351 | 352 | --- 353 | 354 | 💡 Contributions & Support 355 | 356 | 🚀 Contributions are welcome! Fork the repo, submit pull requests, and report issues. Let's make APKnife even better! 357 | 358 | -------------------------------------------------------------------------------- /apknife/modules/protection_scanner.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | from androguard.core.apk import APK 4 | 5 | class ProtectionScanner: 6 | def __init__(self, apk_path): 7 | self.apk_path = apk_path 8 | self.apk = APK(apk_path) 9 | 10 | def check_proguard(self): 11 | """ 12 | Check if ProGuard is used for code obfuscation. 13 | """ 14 | for file in self.apk.get_files(): 15 | if "proguard" in file.lower() or "mapping" in file.lower(): 16 | return True 17 | return False 18 | 19 | def check_firewall(self): 20 | """ 21 | Check if the app uses a firewall. 22 | """ 23 | manifest = self.apk.get_android_manifest_xml() 24 | if "NetworkSecurityConfig" in manifest: 25 | return True 26 | return False 27 | 28 | def check_anti_tampering(self): 29 | """ 30 | Check if the app has anti-tampering mechanisms. 31 | """ 32 | for file in self.apk.get_files(): 33 | if "tamper" in file.lower() or "integrity" in file.lower(): 34 | return True 35 | return False 36 | 37 | def check_ssl_pinning(self): 38 | """ 39 | Check if the app uses SSL pinning. 40 | """ 41 | for file in self.apk.get_files(): 42 | if "ssl" in file.lower() and "pinning" in file.lower(): 43 | return True 44 | return False 45 | 46 | def check_root_detection(self): 47 | """ 48 | Check if the app has root detection mechanisms. 49 | """ 50 | for file in self.apk.get_files(): 51 | if "root" in file.lower() and "detection" in file.lower(): 52 | return True 53 | return False 54 | 55 | def check_data_encryption(self): 56 | """ 57 | Check if the app uses data encryption. 58 | """ 59 | for file in self.apk.get_files(): 60 | if "encrypt" in file.lower() or "crypto" in file.lower(): 61 | return True 62 | return False 63 | 64 | def check_runtime_protection(self): 65 | """ 66 | Check if the app has runtime protection mechanisms. 67 | """ 68 | for file in self.apk.get_files(): 69 | if "runtime" in file.lower() and "protection" in file.lower(): 70 | return True 71 | return False 72 | 73 | def scan_protections(self): 74 | """ 75 | Scan the app for various protection mechanisms. 76 | """ 77 | protections = { 78 | "ProGuard": self.check_proguard(), 79 | "Firewall": self.check_firewall(), 80 | "Anti-Tampering": self.check_anti_tampering(), 81 | "SSL Pinning": self.check_ssl_pinning(), 82 | "Root Detection": self.check_root_detection(), 83 | "Data Encryption": self.check_data_encryption(), 84 | "Runtime Protection": self.check_runtime_protection(), 85 | } 86 | return protections 87 | 88 | def get_protection_guidance(self, protection, status): 89 | """ 90 | Provide guidance on the risks, exploitation, and mitigation for each protection. 91 | """ 92 | guidance = { 93 | "ProGuard": { 94 | "risk": "Without ProGuard, the app's code is easily reverse-engineered, exposing sensitive logic and data.", 95 | "exploitation": "Attackers can decompile the app to understand its logic, find vulnerabilities, or extract sensitive information.", 96 | "mitigation": "Enable ProGuard or R8 in your build.gradle file to obfuscate the code.", 97 | "testing": "Use tools like JADX or APKTool to decompile the app and verify obfuscation.", 98 | }, 99 | "Firewall": { 100 | "risk": "Without a firewall, the app is vulnerable to network-based attacks like MITM (Man-in-the-Middle).", 101 | "exploitation": "Attackers can intercept and manipulate network traffic to steal data or inject malicious content.", 102 | "mitigation": "Implement Network Security Config and use HTTPS with strong ciphers.", 103 | "testing": "Use tools like Burp Suite or Frida to test network security.", 104 | }, 105 | "Anti-Tampering": { 106 | "risk": "Without anti-tampering, the app can be modified or repackaged with malicious code.", 107 | "exploitation": "Attackers can modify the APK to bypass security checks or inject malware.", 108 | "mitigation": "Implement integrity checks using checksums or digital signatures.", 109 | "testing": "Use tools like Apktool to repackage the app and test integrity checks.", 110 | }, 111 | "SSL Pinning": { 112 | "risk": "Without SSL pinning, the app is vulnerable to MITM attacks even with HTTPS.", 113 | "exploitation": "Attackers can use self-signed certificates to intercept HTTPS traffic.", 114 | "mitigation": "Implement SSL pinning using libraries like OkHttp or TrustKit.", 115 | "testing": "Use tools like Frida or Objection to bypass SSL pinning and test its effectiveness.", 116 | }, 117 | "Root Detection": { 118 | "risk": "Without root detection, the app is vulnerable to attacks from rooted devices.", 119 | "exploitation": "Attackers can use rooted devices to bypass security mechanisms or access sensitive data.", 120 | "mitigation": "Implement root detection using libraries like SafetyNet or RootBeer.", 121 | "testing": "Use rooted devices or emulators to test root detection mechanisms.", 122 | }, 123 | "Data Encryption": { 124 | "risk": "Without data encryption, sensitive data stored on the device is easily accessible.", 125 | "exploitation": "Attackers can extract sensitive data from the app's storage or databases.", 126 | "mitigation": "Use strong encryption algorithms like AES for sensitive data storage.", 127 | "testing": "Use tools like SQLite Browser or Frida to inspect stored data.", 128 | }, 129 | "Runtime Protection": { 130 | "risk": "Without runtime protection, the app is vulnerable to dynamic analysis and tampering.", 131 | "exploitation": "Attackers can use tools like Frida to manipulate the app's behavior at runtime.", 132 | "mitigation": "Implement runtime integrity checks and anti-debugging mechanisms.", 133 | "testing": "Use tools like Frida or Xposed to test runtime protections.", 134 | }, 135 | } 136 | return guidance.get(protection, {}) 137 | 138 | def scan_apk_protections(apk_path): 139 | """ 140 | Scan an APK for protection mechanisms and provide guidance. 141 | """ 142 | try: 143 | scanner = ProtectionScanner(apk_path) 144 | protections = scanner.scan_protections() 145 | for protection, status in protections.items(): 146 | print(f"{protection}: {'✅' if status else '❌'}") 147 | if not status: 148 | guidance = scanner.get_protection_guidance(protection, status) 149 | print(f" Risk: {guidance['risk']}") 150 | print(f" Exploitation: {guidance['exploitation']}") 151 | print(f" Mitigation: {guidance['mitigation']}") 152 | print(f" Testing: {guidance['testing']}") 153 | print() 154 | except Exception as e: 155 | print(f"[!] Error scanning APK: {e}") 156 | 157 | def execute_shell_command(command): 158 | """ 159 | Execute a shell command and return the output. 160 | """ 161 | try: 162 | # Handle 'cd' command separately 163 | if command.startswith("cd "): 164 | new_dir = command.split(" ", 1)[1].strip() 165 | try: 166 | os.chdir(new_dir) 167 | return f"[+] Changed directory to: {os.getcwd()}" 168 | except FileNotFoundError: 169 | return f"[!] Directory not found: {new_dir}" 170 | except Exception as e: 171 | return f"[!] Error changing directory: {e}" 172 | 173 | # Execute other shell commands 174 | result = subprocess.run(command, shell=True, check=True, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 175 | return result.stdout 176 | except subprocess.CalledProcessError as e: 177 | return f"[!] Error: {e.stderr}" 178 | 179 | def interactive_shell(): 180 | """ 181 | Start an interactive shell for APKnife. 182 | """ 183 | from prompt_toolkit import PromptSession 184 | from prompt_toolkit.auto_suggest import AutoSuggestFromHistory 185 | from prompt_toolkit.completion import WordCompleter 186 | from prompt_toolkit.history import FileHistory 187 | 188 | # Define available commands 189 | commands = { 190 | "scan": "Scan an APK for protection mechanisms.", 191 | "exit": "Exit the interactive shell.", 192 | "help": "Show this help message.", 193 | } 194 | 195 | # Create a completer for command suggestions 196 | completer = WordCompleter(list(commands.keys()), ignore_case=True) 197 | 198 | # Create a prompt session 199 | session = PromptSession( 200 | history=FileHistory(".apknife_history"), 201 | auto_suggest=AutoSuggestFromHistory(), 202 | completer=completer, 203 | ) 204 | 205 | print("APKnife Interactive Shell. Type 'help' for a list of commands.") 206 | 207 | while True: 208 | try: 209 | # Read user input 210 | user_input = session.prompt("APKnife> ").strip() 211 | 212 | if not user_input: 213 | continue 214 | 215 | # Split input into command and arguments 216 | parts = user_input.split() 217 | command = parts[0] 218 | args = parts[1:] 219 | 220 | # Handle commands 221 | if command == "exit": 222 | print("Exiting APKnife. Goodbye!") 223 | break 224 | elif command == "help": 225 | print("\nAvailable Commands:") 226 | for cmd, desc in commands.items(): 227 | print(f" {cmd.ljust(10)} - {desc}") 228 | print() 229 | elif command == "scan": 230 | if not args: 231 | print("[!] Please provide the path to the APK file.") 232 | continue 233 | apk_path = args[0] 234 | if not os.path.exists(apk_path): 235 | print(f"[!] APK file not found: {apk_path}") 236 | continue 237 | scan_apk_protections(apk_path) 238 | else: 239 | # Execute as a shell command 240 | output = execute_shell_command(user_input) 241 | print(output) 242 | 243 | except KeyboardInterrupt: 244 | print("\nType 'exit' to quit.") 245 | except Exception as e: 246 | print(f"[!] Error: {e}") 247 | 248 | if __name__ == "__main__": 249 | interactive_shell() 250 | -------------------------------------------------------------------------------- /apknife/modules/interactive_mode.py: -------------------------------------------------------------------------------- 1 | import json 2 | import logging 3 | import os 4 | import subprocess 5 | import argparse 6 | from prompt_toolkit import PromptSession 7 | from prompt_toolkit.auto_suggest import AutoSuggestFromHistory 8 | from prompt_toolkit.completion import WordCompleter 9 | from prompt_toolkit.history import FileHistory 10 | from prompt_toolkit.styles import Style 11 | 12 | # ANSI color codes for terminal output styling 13 | RED = "\033[91m" 14 | GREEN = "\033[92m" 15 | YELLOW = "\033[93m" 16 | BLUE = "\033[94m" 17 | PURPLE = "\033[95m" 18 | CYAN = "\033[96m" 19 | WHITE = "\033[97m" 20 | RESET = "\033[0m" 21 | 22 | # Logging setup 23 | logging.basicConfig(level=logging.INFO, format="%(message)s") 24 | 25 | # Define a style for the prompt 26 | style = Style.from_dict( 27 | { 28 | "prompt": "fg:ansicyan", 29 | "input": "fg:ansigreen", 30 | } 31 | ) 32 | 33 | # Load commands from external file 34 | def load_commands(): 35 | if not os.path.exists("commands.json"): 36 | logging.warning(f"{YELLOW}[!] commands.json not found. Creating a default one...{RESET}") 37 | default_commands = { 38 | "help": "Displays this help menu", 39 | "exit": "Exits the interactive mode", 40 | "update-commands": "Reloads the commands from the external file", 41 | "list-commands": "Displays the current list of available commands", 42 | "extract-dex": "Extract DEX files from an APK", 43 | "decode_manifest": "Decode AndroidManifest.xml", 44 | "waf": "Scan the app for protection mechanisms", 45 | "analyze": "Analyze the APK", 46 | "build": "Build the APK", 47 | "sign": "Sign the APK", 48 | "edit-manifest": "Edit the AndroidManifest.xml", 49 | "smali": "Decompile the APK to Smali", 50 | "find-oncreate": "Find onCreate methods in the APK", 51 | "find-api": "Find API calls in the APK", 52 | "scan-vulnerabilities": "Scan the APK for vulnerabilities", 53 | "scan-permissions": "Scan the APK for permissions", 54 | "catch_rat": "Analyze the APK for RATs", 55 | "extract-java": "Extract Java code from the APK", 56 | "extract-sensitive": "Extract sensitive data from the APK", 57 | "modify-apk": "Modify the APK", 58 | } 59 | with open("commands.json", "w") as file: 60 | json.dump(default_commands, file, indent=4) 61 | return default_commands 62 | 63 | try: 64 | with open("commands.json", "r") as file: 65 | return json.load(file) 66 | except json.JSONDecodeError: 67 | logging.error(f"{RED}[!] Invalid JSON format in commands file!{RESET}") 68 | return {} 69 | 70 | # Execute shell commands 71 | def execute_shell_command(command): 72 | try: 73 | # Handle 'cd' command separately 74 | if command.startswith("cd "): 75 | new_dir = command.split(" ", 1)[1].strip() 76 | try: 77 | os.chdir(new_dir) 78 | return f"{GREEN}[+] Changed directory to: {os.getcwd()}{RESET}" 79 | except FileNotFoundError: 80 | return f"{RED}[!] Directory not found: {new_dir}{RESET}" 81 | except Exception as e: 82 | return f"{RED}[!] Error changing directory: {e}{RESET}" 83 | 84 | # Execute other shell commands 85 | result = subprocess.run(command, shell=True, check=True, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 86 | return result.stdout 87 | except subprocess.CalledProcessError as e: 88 | return f"{RED}[!] Error: {e.stderr}{RESET}" 89 | 90 | # Get common shell commands 91 | def get_shell_commands(): 92 | return ["ls", "cd", "mkdir", "rm", "cp", "mv", "pwd", "cat", "echo", "grep", "find", "chmod", "ps", "kill"] 93 | 94 | # Parse arguments for APKnife commands 95 | def parse_arguments(command, args): 96 | parser = argparse.ArgumentParser(description=f"APKnife {command} command") 97 | parser.add_argument("-i", "--input", help="Input APK file", required=True) 98 | parser.add_argument("-o", "--output", help="Output file/directory") 99 | parser.add_argument("-c", "--compress", action="store_true", help="Compress output") 100 | parser.add_argument("--name", help="New app name") 101 | parser.add_argument("--icon", help="New app icon") 102 | parser.add_argument("--package", help="New package name") 103 | try: 104 | return parser.parse_args(args) 105 | except SystemExit: 106 | return None 107 | 108 | # Execute APKnife commands 109 | def execute_command(command, args): 110 | try: 111 | parsed_args = parse_arguments(command, args) 112 | if not parsed_args: 113 | return f"{RED}[!] Invalid arguments for command: {command}{RESET}" 114 | 115 | if command == "extract-dex": 116 | from apknife.modules.dex_extractor import extract_dex 117 | dex_files = extract_dex(parsed_args.input, parsed_args.output) 118 | if dex_files: 119 | return f"{GREEN}[*] DEX files extracted successfully: {dex_files}{RESET}" 120 | else: 121 | return f"{RED}[!] Failed to extract DEX files{RESET}" 122 | elif command == "decode_manifest": 123 | from apknife.modules.manifest_decoder import decode_manifest 124 | decode_manifest(parsed_args.input, parsed_args.output) 125 | return f"{GREEN}[*] Manifest decoded successfully{RESET}" 126 | elif command == "waf": 127 | from apknife.modules.protection_scanner import scan_apk_protections 128 | protections = scan_apk_protections(parsed_args.input) 129 | output = "" 130 | for protection, status in protections.items(): 131 | output += f"{protection}: {'✅' if status else '❌'}\n" 132 | return output 133 | elif command == "analyze": 134 | from apknife.modules.analyzer import analyze_apk 135 | analyze_apk(parsed_args.input) 136 | return f"{GREEN}[*] APK analyzed successfully{RESET}" 137 | elif command == "build": 138 | from apknife.modules.builder import build_apk 139 | build_apk(parsed_args.input, parsed_args.output) 140 | return f"{GREEN}[*] APK built successfully{RESET}" 141 | elif command == "sign": 142 | from apknife.modules.signer import sign_apk 143 | sign_apk(parsed_args.input) 144 | return f"{GREEN}[*] APK signed successfully{RESET}" 145 | elif command == "edit-manifest": 146 | from apknife.modules.manifest_editor import edit_manifest 147 | edit_manifest(parsed_args.input) 148 | return f"{GREEN}[*] Manifest edited successfully{RESET}" 149 | elif command == "smali": 150 | from apknife.modules.smali_tools import decompile_apk 151 | decompile_apk(parsed_args.input, parsed_args.output) 152 | return f"{GREEN}[*] APK decompiled to Smali successfully{RESET}" 153 | elif command == "find-oncreate": 154 | from apknife.modules.smali_tools import find_oncreate 155 | find_oncreate(parsed_args.input) 156 | return f"{GREEN}[*] onCreate methods found{RESET}" 157 | elif command == "find-api": 158 | from apknife.modules.api_finder import find_api_calls 159 | find_api_calls(parsed_args.input) 160 | return f"{GREEN}[*] API calls found{RESET}" 161 | elif command == "scan-vulnerabilities": 162 | from apknife.modules.vulnerability_scanner import scan_apk 163 | scan_apk(parsed_args.input) 164 | return f"{GREEN}[*] Vulnerabilities scanned{RESET}" 165 | elif command == "scan-permissions": 166 | from apknife.modules.permission_scanner import scan_permissions 167 | scan_permissions(parsed_args.input) 168 | return f"{GREEN}[*] Permissions scanned{RESET}" 169 | elif command == "catch_rat": 170 | from apknife.modules.catch_rat import analyze_apk_ips 171 | analyze_apk_ips(parsed_args.input) 172 | return f"{GREEN}[*] RAT analysis completed{RESET}" 173 | elif command == "extract-java": 174 | from apknife.modules.java_extractor import extract_java 175 | extract_java(parsed_args.input, parsed_args.output, parsed_args.compress) 176 | return f"{GREEN}[*] Java code extracted{RESET}" 177 | elif command == "extract-sensitive": 178 | from apknife.modules.extract_sensitive import extract_sensitive_data 179 | extract_sensitive_data(parsed_args.input, parsed_args.output or "sensitive_report.json") 180 | return f"{GREEN}[*] Sensitive data extracted{RESET}" 181 | elif command == "modify-apk": 182 | from apknife.modules.apk_modifier import APKModifier 183 | modifier = APKModifier(parsed_args.input, parsed_args.name, parsed_args.icon, parsed_args.package) 184 | modifier.run() 185 | return f"{GREEN}[*] APK modified successfully{RESET}" 186 | else: 187 | return f"{RED}[!] Unknown command: {command}{RESET}" 188 | except Exception as e: 189 | return f"{RED}[!] Error executing command: {e}{RESET}" 190 | 191 | # Interactive shell 192 | def interactive_shell(COMMANDS): 193 | shell_commands = get_shell_commands() 194 | completer = WordCompleter(list(COMMANDS.keys()) + shell_commands, ignore_case=True) 195 | session = PromptSession( 196 | history=FileHistory(".apknife_history"), 197 | auto_suggest=AutoSuggestFromHistory(), 198 | completer=completer, 199 | style=style, 200 | ) 201 | 202 | while True: 203 | try: 204 | text = session.prompt("APKnife> ") 205 | if text.strip() == "exit": 206 | break 207 | 208 | args = text.split() 209 | if not args: 210 | continue 211 | 212 | command = args[0] 213 | 214 | # Handle APKnife commands 215 | if command in COMMANDS: 216 | if command == "help": 217 | print(f"\n{YELLOW}Available Commands:{RESET}") 218 | for cmd, desc in COMMANDS.items(): 219 | print(f" {GREEN}{cmd.ljust(20)}{RESET} - {WHITE}{desc}{RESET}") 220 | print() 221 | continue 222 | 223 | if command == "update-commands": 224 | COMMANDS = load_commands() 225 | completer = WordCompleter(COMMANDS.keys(), ignore_case=True) 226 | logging.info(f"{GREEN}[+] Commands updated successfully!{RESET}") 227 | continue 228 | 229 | if command == "list-commands": 230 | print(f"\n{YELLOW}Current Commands:{RESET}") 231 | for cmd, desc in COMMANDS.items(): 232 | print(f" {GREEN}{cmd.ljust(20)}{RESET} - {WHITE}{desc}{RESET}") 233 | print() 234 | continue 235 | 236 | # Execute APKnife commands 237 | output = execute_command(command, args[1:]) 238 | print(output) 239 | 240 | # Handle shell commands 241 | else: 242 | output = execute_shell_command(text) 243 | print(output) 244 | 245 | except KeyboardInterrupt: 246 | continue 247 | except EOFError: 248 | break 249 | 250 | # Main function 251 | def main(): 252 | COMMANDS = load_commands() 253 | interactive_shell(COMMANDS) 254 | 255 | if __name__ == "__main__": 256 | main() 257 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # APKnife: The Cyber Blade of APK Domination 🔪🧸 3 | 4 |

5 | APKnife Cover 6 |

7 | 8 | APKnife – The Double-Edged Blade of APK Analysis 🔪 9 | 10 | Fear the Blade, Trust the Power! 11 | 12 | APKnife is an advanced tool for APK analysis, modification, and security auditing. Whether you're a security researcher, penetration tester, or Android developer, APKnife provides powerful features for reverse engineering, decompiling, modifying, and analyzing APK files. 13 | 14 | --- 15 | 16 | ## 🚀 Features & Capabilities 17 | 18 | - ✅ Extract & decompile APKs into readable formats 19 | - ✅ Modify & repackage APKs effortlessly 20 | - ✅ Analyze APKs for security vulnerabilities 21 | - ✅ Edit AndroidManifest.xml & Smali code 22 | - ✅ Extract Java source code from an APK 23 | - ✅ Detect Remote Access Trojans (RATs) & malware 24 | - ✅ Decode binary XML files & scan for API calls 25 | - ✅ Change APK metadata (icon, name, package name) 26 | - ✅ Identify security risks like excessive permissions 27 | - ✅ Sign APKs for smooth installation 28 | 29 | --- 30 | 31 | ## 🔧 Installation 32 | 33 | ### 📌 Prerequisites 34 | 35 | Ensure you have the following installed on your system: 36 | 37 | - **Python 3.12** 38 | - **Java (JDK 8 or later)** 39 | - **apktool** 40 | - **zipalign** 41 | - **keytool** 42 | 43 | ### 🛠 Setting Up a Python Virtual Environment 44 | 45 | Before installing APKnife, it's recommended to set up a Python virtual environment to avoid package conflicts. 46 | 47 | 1️⃣ **Create a Python Virtual Environment:** 48 | 49 | ``` 50 | python3 -m venv venv 51 | source venv/bin/activate # On Linux/macOS 52 | venv\Scripts\activate # On Windows 53 | ``` 54 | 55 | 2️⃣ **Install Required Packages** 56 | 57 | Once the virtual environment is activated, install APKnife: 58 | 59 | ``` 60 | pip install apknife 61 | ``` 62 | 63 | --- 64 | 65 | ## 📥 Installing Rust (Required for APKnife) 66 | 67 | APKnife requires Rust for building. Follow the installation steps based on your OS: 68 | 69 | ### 🐧 On Linux: 70 | 71 | ``` 72 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 73 | ``` 74 | 75 | Then follow the on-screen instructions. 76 | 77 | ### 🍏 On macOS (Using Homebrew): 78 | 79 | ``` 80 | brew install rust 81 | ``` 82 | 83 | ### 🖥️ On Windows: 84 | 85 | 1. Visit [rustup.rs](https://rustup.rs/) and install Rust. 86 | 2. Verify installation: 87 | 88 | ``` 89 | rustc --version 90 | ``` 91 | 92 | --- 93 | 94 | ## ⚠️ Troubleshooting Common Issues 95 | 96 | ### ❌ Issue Installing Rust on Termux 97 | 98 | Ensure Termux is up to date: 99 | 100 | ``` 101 | pkg update && pkg upgrade 102 | ``` 103 | 104 | Install required build tools: 105 | 106 | ``` 107 | pkg install clang make python rust 108 | ``` 109 | 110 | ### ❌ Issues Installing APKnife 111 | 112 | - **Rust not installed properly?** Ensure it's correctly installed via rustup or your package manager. 113 | - **Python conflicts?** If there are issues with virtual environments, reset it: 114 | 115 | ``` 116 | rm -rf venv 117 | python3 -m venv venv 118 | source venv/bin/activate 119 | ``` 120 | 121 | ### ✅ Verifying Installed Versions 122 | 123 | ``` 124 | python --version 125 | rustc --version 126 | ``` 127 | 128 | --- 129 | 130 | ## 🌍 Setting Up Rust Environment Variables 131 | 132 | ### 🐧 On Linux/macOS 133 | 134 | ``` 135 | nano ~/.bashrc # For bash 136 | nano ~/.zshrc # For zsh 137 | ``` 138 | 139 | Add this line at the end: 140 | 141 | ``` 142 | export PATH="$HOME/.cargo/bin:$PATH" 143 | ``` 144 | 145 | Apply changes: 146 | 147 | ``` 148 | source ~/.bashrc # For bash 149 | source ~/.zshrc # For zsh 150 | ``` 151 | 152 | ### 🖥️ On Windows 153 | 154 | 1. Open "Environment Variables" from the Start menu. 155 | 2. Edit Path under System Variables and add: 156 | 157 | ``` 158 | C:\Users\\.cargo\bin 159 | ``` 160 | 161 | 3. Click OK and restart your terminal. 162 | 163 | Verify the setup: 164 | 165 | ``` 166 | cargo --version 167 | rustc --version 168 | ``` 169 | 170 | --- 171 | 172 | ## 📥 Installing APKnife 173 | 174 | ```bash 175 | pip install apknife 176 | ``` 177 | 178 | --- 179 | 180 | ## 📜 Usage 181 | 182 | ### 🖥️ Interactive Mode 183 | 184 | To enter interactive mode, run: 185 | 186 | ```bash 187 | python3 apknife.py interactive 188 | ``` 189 | 190 | This will launch a command-line interface for executing APKnife commands. 191 | 192 | --- 193 | 194 | ## 🛠️ Available Commands 195 | 196 | ### 🟢 Extract APK Contents 197 | 198 | ```bash 199 | python3 apknife.py extract -i target.apk -o extracted/ 200 | ``` 201 | 202 | ### 🟢 Modify & Rebuild APK 203 | 204 | ```bash 205 | python3 apknife.py build -i extracted/ -o modified.apk 206 | ``` 207 | 208 | ### 🟢 Sign APK 209 | 210 | ```bash 211 | apknife sign -i modified.apk 212 | ``` 213 | 214 | ### 🟢 Analyze APK for Vulnerabilities 215 | 216 | ```bash 217 | apknife scan_vulnerabilities -i target.apk 218 | ``` 219 | 220 | ### 🟢 Detect Remote Access Trojans (RATs) 221 | 222 | ```bash 223 | apknife catch_rat -i malicious.apk 224 | ``` 225 | 226 | ### 🟢 Extract Java Source Code 227 | 228 | ```bash 229 | apknife extract-java -i target.apk -o src_folder 230 | ``` 231 | 232 | ### 🟢 Change APK Name 233 | 234 | ```bash 235 | apknife modify-apk --name -i app.apk 236 | ``` 237 | 238 | ### 🟢 Change APK Icon 239 | 240 | ```bash 241 | apknife modify-apk --icon new_icon.png -i app.apk 242 | ``` 243 | 244 | ### 🟢 Modify Package Name 245 | 246 | ```bash 247 | apknife modify-apk --package com.example.example -i app.apk 248 | ``` 249 | 250 | ### 🟢 Modify Multiple APK Attributes 251 | 252 | ```bash 253 | apknife modify-apk --name new_name --package new.package.name --icon anysize.any -o modified_apk.apk 254 | ``` 255 | 256 | ### 🟢 Scan APK Permissions 257 | 258 | ```bash 259 | apknife scan_permissions -i target.apk 260 | ``` 261 | 262 | --- 263 | 264 | ## 👇 Help Menu 265 | 266 | 267 | 268 | ### 🟢 **Extract APK Contents** 269 | Extract the contents of an APK file. 270 | 271 | ```bash 272 | python3 apknife.py extract -i target.apk -o extracted/ 273 | ``` 274 | 275 | - **`-i`**: Path to the input APK file. 276 | - **`-o`**: Directory to save the extracted files. 277 | 278 | --- 279 | 280 | ### 🟢 **Build APK** 281 | Rebuild an APK from extracted files. 282 | 283 | ```bash 284 | python3 apknife.py build -i extracted/ -o modified.apk 285 | ``` 286 | 287 | - **`-i`**: Directory containing the extracted files. 288 | - **`-o`**: Path to save the rebuilt APK. 289 | 290 | --- 291 | 292 | ### 🟢 **Sign APK** 293 | Sign an APK file. 294 | 295 | ```bash 296 | apknife sign -i modified.apk 297 | ``` 298 | 299 | - **`-i`**: Path to the APK file to sign. 300 | 301 | --- 302 | 303 | ### 🟢 **Analyze APK** 304 | Analyze an APK for security vulnerabilities. 305 | 306 | ```bash 307 | apknife analyze -i target.apk 308 | ``` 309 | 310 | - **`-i`**: Path to the APK file to analyze. 311 | 312 | --- 313 | 314 | ### 🟢 **Edit Manifest** 315 | Edit the `AndroidManifest.xml` of an APK. 316 | 317 | ```bash 318 | apknife edit-manifest -i target.apk 319 | ``` 320 | 321 | - **`-i`**: Path to the APK file. 322 | 323 | --- 324 | 325 | ### 🟢 **Decompile to Smali** 326 | Decompile an APK to Smali code. 327 | 328 | ```bash 329 | apknife smali -i target.apk -o smali_output/ 330 | ``` 331 | 332 | - **`-i`**: Path to the APK file. 333 | - **`-o`**: Directory to save the decompiled Smali code. 334 | 335 | --- 336 | 337 | ### 🟢 **Decode Manifest** 338 | Decode the `AndroidManifest.xml` of an APK. 339 | 340 | ```bash 341 | apknife decode_manifest -i target.apk -o output_dir/ 342 | ``` 343 | 344 | - **`-i`**: Path to the APK file. 345 | - **`-o`**: Directory to save the decoded manifest. 346 | 347 | --- 348 | 349 | ### 🟢 **Find onCreate Methods** 350 | Find `onCreate` methods in an APK. 351 | 352 | ```bash 353 | apknife find-oncreate -i target.apk 354 | ``` 355 | 356 | - **`-i`**: Path to the APK file. 357 | 358 | --- 359 | 360 | ### 🟢 **Find API Calls** 361 | Find API calls in an APK. 362 | 363 | ```bash 364 | apknife find-api -i target.apk 365 | ``` 366 | 367 | - **`-i`**: Path to the APK file. 368 | 369 | --- 370 | 371 | ### 🟢 **Scan for Vulnerabilities** 372 | Scan an APK for vulnerabilities. 373 | 374 | ```bash 375 | apknife scan-vulnerabilities -i target.apk 376 | ``` 377 | 378 | - **`-i`**: Path to the APK file. 379 | 380 | --- 381 | 382 | ### 🟢 **Scan Permissions** 383 | Scan and list permissions used by an APK. 384 | 385 | ```bash 386 | apknife scan-permissions -i target.apk 387 | ``` 388 | 389 | - **`-i`**: Path to the APK file. 390 | 391 | --- 392 | 393 | ### 🟢 **Catch RAT** 394 | Analyze an APK for Remote Access Trojan (RAT) indicators. 395 | 396 | ```bash 397 | apknife catch_rat -i malicious.apk 398 | ``` 399 | 400 | - **`-i`**: Path to the APK file. 401 | 402 | --- 403 | 404 | ### 🟢 **Extract Java Source Code** 405 | Extract Java source code from an APK. 406 | 407 | ```bash 408 | apknife extract-java -i target.apk -o src_folder/ 409 | ``` 410 | 411 | - **`-i`**: Path to the APK file. 412 | - **`-o`**: Directory to save the extracted Java source code. 413 | 414 | --- 415 | 416 | ### 🟢 **Extract Sensitive Data** 417 | Extract sensitive data from an APK. 418 | 419 | ```bash 420 | apknife extract-sensitive -i target.apk -o sensitive_data.json 421 | ``` 422 | 423 | - **`-i`**: Path to the APK file. 424 | - **`-o`**: Path to save the extracted sensitive data (JSON format). 425 | 426 | --- 427 | 428 | ### 🟢 **Modify APK Metadata** 429 | Modify an APK's metadata (name, icon, package name). 430 | 431 | ```bash 432 | apknife modify-apk -i target.apk --name "New App Name" --icon new_icon.png --package com.new.package 433 | ``` 434 | 435 | - **`-i`**: Path to the APK file. 436 | - **`--name`**: New app name. 437 | - **`--icon`**: Path to the new icon (any size/format). 438 | - **`--package`**: New package name. 439 | 440 | --- 441 | 442 | ### 🟢 **Extract DEX Files** 443 | Extract DEX files from an APK. 444 | 445 | ```bash 446 | apknife extract-dex -i target.apk -o dex_output/ 447 | ``` 448 | 449 | - **`-i`**: Path to the APK file. 450 | - **`-o`**: Directory to save the extracted DEX files. 451 | 452 | --- 453 | 454 | ### 🟢 **Scan for Protection Mechanisms** 455 | Scan an APK for protection mechanisms (e.g., Firewall, ProGuard). 456 | 457 | ```bash 458 | apknife waf -i target.apk 459 | ``` 460 | 461 | - **`-i`**: Path to the APK file. 462 | 463 | --- 464 | 465 | ### 🟢 **Interactive Mode** 466 | Launch APKnife in interactive mode. 467 | 468 | ```bash 469 | python3 apknife.py interactive 470 | ``` 471 | 472 | --- 473 | 474 | ### 🟢 **Help Menu** 475 | Display the help menu with all available commands. 476 | 477 | ``` 478 | apknife -h 479 | ``` 480 | 481 | --- 482 | 483 | ### 🟢 **Update Commands** 484 | Reload the commands from the external file. 485 | 486 | ``` 487 | apknife update-commands 488 | ``` 489 | 490 | --- 491 | 492 | ### 🟢 **List Commands** 493 | Display the current list of available commands. 494 | 495 | ``` 496 | apknife list-commands 497 | ``` 498 | 499 | --- 500 | 501 | ### 🟢 **Exit Interactive Mode** 502 | Exit the interactive shell. 503 | 504 | ``` 505 | exit 506 | ``` 507 | 508 | --- 509 | 510 | ## ⚠️ Legal Disclaimer 511 | 512 | This tool is designed for educational and security research purposes only. Unauthorized use of APKnife on third-party applications without permission is illegal. The developers are not responsible for any misuse. 513 | 514 | --- 515 | 516 | ## 📜 License 517 | 518 | APKnife is released under the MIT License – You are free to modify and distribute it for legal use. 519 | 520 | --- 521 | 522 | ## 💡 Contributions & Support 523 | 524 | 🚀 Contributions are welcome! Fork the repo, submit pull requests, and report issues. Let's make APKnife even better! 525 | 526 | --- 527 | 528 | **APKnife – The Double-Edged Blade of APK Analysis 🔪🧸** 529 | 530 | All rights reserved to **MR_nightmare**. 531 | ``` 532 | -------------------------------------------------------------------------------- /bandit_report.txt: -------------------------------------------------------------------------------- 1 | Run started:2025-03-07 08:36:09.846642 2 | 3 | Test results: 4 | >> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module. 5 | Severity: Low Confidence: High 6 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 7 | More Info: https://bandit.readthedocs.io/en/1.8.3/blacklists/blacklist_imports.html#b404-import-subprocess 8 | Location: apknife/apknife.py:5:0 9 | 4 import os 10 | 5 import subprocess 11 | 6 import sys 12 | 13 | -------------------------------------------------- 14 | >> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module. 15 | Severity: Low Confidence: High 16 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 17 | More Info: https://bandit.readthedocs.io/en/1.8.3/blacklists/blacklist_imports.html#b404-import-subprocess 18 | Location: apknife/modules/apk_modifier.py:3:0 19 | 2 import shutil 20 | 3 import subprocess 21 | 4 import sys 22 | 23 | -------------------------------------------------- 24 | >> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path 25 | Severity: Low Confidence: High 26 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 27 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b607_start_process_with_partial_path.html 28 | Location: apknife/modules/apk_modifier.py:30:12 29 | 29 shutil.rmtree(self.decompiled_dir) 30 | 30 subprocess.run(["apktool", "d", self.apk_path, "-o", self.decompiled_dir], check=True) 31 | 31 self.apk_path = self.decompiled_dir 32 | 33 | -------------------------------------------------- 34 | >> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input. 35 | Severity: Low Confidence: High 36 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 37 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b603_subprocess_without_shell_equals_true.html 38 | Location: apknife/modules/apk_modifier.py:30:12 39 | 29 shutil.rmtree(self.decompiled_dir) 40 | 30 subprocess.run(["apktool", "d", self.apk_path, "-o", self.decompiled_dir], check=True) 41 | 31 self.apk_path = self.decompiled_dir 42 | 43 | -------------------------------------------------- 44 | >> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path 45 | Severity: Low Confidence: High 46 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 47 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b607_start_process_with_partial_path.html 48 | Location: apknife/modules/apk_modifier.py:124:12 49 | 123 print("[*] Recompiling modified APK...") 50 | 124 subprocess.run(["apktool", "b", self.apk_path, "-o", self.output_apk], check=True) 51 | 125 print(f"[✔] Modified APK saved as: {self.output_apk}") 52 | 53 | -------------------------------------------------- 54 | >> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input. 55 | Severity: Low Confidence: High 56 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 57 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b603_subprocess_without_shell_equals_true.html 58 | Location: apknife/modules/apk_modifier.py:124:12 59 | 123 print("[*] Recompiling modified APK...") 60 | 124 subprocess.run(["apktool", "b", self.apk_path, "-o", self.output_apk], check=True) 61 | 125 print(f"[✔] Modified APK saved as: {self.output_apk}") 62 | 63 | -------------------------------------------------- 64 | >> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module. 65 | Severity: Low Confidence: High 66 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 67 | More Info: https://bandit.readthedocs.io/en/1.8.3/blacklists/blacklist_imports.html#b404-import-subprocess 68 | Location: apknife/modules/catch_rat.py:3:0 69 | 2 import re 70 | 3 import subprocess 71 | 4 import zipfile 72 | 73 | -------------------------------------------------- 74 | >> Issue: [B110:try_except_pass] Try, Except, Pass detected. 75 | Severity: Low Confidence: High 76 | CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html) 77 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b110_try_except_pass.html 78 | Location: apknife/modules/catch_rat.py:22:20 79 | 21 found_ips.update(matches) 80 | 22 except Exception: 81 | 23 pass # تجاهل الأخطاء عند قراءة الملفات الثنائية 82 | 24 except zipfile.BadZipFile: 83 | 84 | -------------------------------------------------- 85 | >> Issue: [B113:request_without_timeout] Call to requests without timeout 86 | Severity: Medium Confidence: Low 87 | CWE: CWE-400 (https://cwe.mitre.org/data/definitions/400.html) 88 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b113_request_without_timeout.html 89 | Location: apknife/modules/catch_rat.py:34:19 90 | 33 try: 91 | 34 response = requests.get(url) 92 | 35 data = response.json() 93 | 94 | -------------------------------------------------- 95 | >> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path 96 | Severity: Low Confidence: High 97 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 98 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b607_start_process_with_partial_path.html 99 | Location: apknife/modules/catch_rat.py:46:17 100 | 45 try: 101 | 46 result = subprocess.run(["whois", ip], capture_output=True, text=True) 102 | 47 print(f"\n🔍 WHOIS Information for {ip}:") 103 | 104 | -------------------------------------------------- 105 | >> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input. 106 | Severity: Low Confidence: High 107 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 108 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b603_subprocess_without_shell_equals_true.html 109 | Location: apknife/modules/catch_rat.py:46:17 110 | 45 try: 111 | 46 result = subprocess.run(["whois", ip], capture_output=True, text=True) 112 | 47 print(f"\n🔍 WHOIS Information for {ip}:") 113 | 114 | -------------------------------------------------- 115 | >> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module. 116 | Severity: Low Confidence: High 117 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 118 | More Info: https://bandit.readthedocs.io/en/1.8.3/blacklists/blacklist_imports.html#b404-import-subprocess 119 | Location: apknife/modules/extract_sensitive.py:5:0 120 | 4 import re 121 | 5 import subprocess 122 | 6 import zipfile 123 | 124 | -------------------------------------------------- 125 | >> Issue: [B110:try_except_pass] Try, Except, Pass detected. 126 | Severity: Low Confidence: High 127 | CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html) 128 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b110_try_except_pass.html 129 | Location: apknife/modules/extract_sensitive.py:53:4 130 | 52 extracted_data.setdefault(category, []).extend(matches) 131 | 53 except Exception: 132 | 54 pass 133 | 55 134 | 135 | -------------------------------------------------- 136 | >> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module. 137 | Severity: Low Confidence: High 138 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 139 | More Info: https://bandit.readthedocs.io/en/1.8.3/blacklists/blacklist_imports.html#b404-import-subprocess 140 | Location: apknife/modules/extractor.py:6:0 141 | 5 import shutil 142 | 6 import subprocess 143 | 7 import tempfile 144 | 145 | -------------------------------------------------- 146 | >> Issue: [B602:subprocess_popen_with_shell_equals_true] subprocess call with shell=True identified, security issue. 147 | Severity: High Confidence: High 148 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 149 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b602_subprocess_popen_with_shell_equals_true.html 150 | Location: apknife/modules/extractor.py:17:13 151 | 16 result = subprocess.run( 152 | 17 f"command -v {tool}", shell=True, capture_output=True, text=True 153 | 18 ) 154 | 19 if result.returncode != 0: 155 | 20 logging.error(f"❌ Missing dependency: {tool}. Please install it.") 156 | 157 | -------------------------------------------------- 158 | >> Issue: [B602:subprocess_popen_with_shell_equals_true] subprocess call with shell=True identified, security issue. 159 | Severity: High Confidence: High 160 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 161 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b602_subprocess_popen_with_shell_equals_true.html 162 | Location: apknife/modules/extractor.py:35:12 163 | 34 command = f"apktool d -f -r {apk_path} -o {temp_dir}" 164 | 35 subprocess.run(command, shell=True, check=True) 165 | 36 166 | 167 | -------------------------------------------------- 168 | >> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path 169 | Severity: Low Confidence: High 170 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 171 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b607_start_process_with_partial_path.html 172 | Location: apknife/modules/extractor.py:73:12 173 | 72 # Convert DEX to JAR 174 | 73 subprocess.run(["d2j-dex2jar", dex_file, "-o", jar_file], check=True) 175 | 74 logging.info(f"🔹 Converted {dex_file} to {jar_file}") 176 | 177 | -------------------------------------------------- 178 | >> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input. 179 | Severity: Low Confidence: High 180 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 181 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b603_subprocess_without_shell_equals_true.html 182 | Location: apknife/modules/extractor.py:73:12 183 | 72 # Convert DEX to JAR 184 | 73 subprocess.run(["d2j-dex2jar", dex_file, "-o", jar_file], check=True) 185 | 74 logging.info(f"🔹 Converted {dex_file} to {jar_file}") 186 | 187 | -------------------------------------------------- 188 | >> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path 189 | Severity: Low Confidence: High 190 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 191 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b607_start_process_with_partial_path.html 192 | Location: apknife/modules/extractor.py:78:12 193 | 77 jadx_out = jar_file.replace(".jar", "_jadx") 194 | 78 subprocess.run(["jadx", "-d", jadx_out, jar_file], check=True) 195 | 79 logging.info(f"🔹 Decompiled {jar_file} with JADX to {jadx_out}") 196 | 197 | -------------------------------------------------- 198 | >> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input. 199 | Severity: Low Confidence: High 200 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 201 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b603_subprocess_without_shell_equals_true.html 202 | Location: apknife/modules/extractor.py:78:12 203 | 77 jadx_out = jar_file.replace(".jar", "_jadx") 204 | 78 subprocess.run(["jadx", "-d", jadx_out, jar_file], check=True) 205 | 79 logging.info(f"🔹 Decompiled {jar_file} with JADX to {jadx_out}") 206 | 207 | -------------------------------------------------- 208 | >> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path 209 | Severity: Low Confidence: High 210 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 211 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b607_start_process_with_partial_path.html 212 | Location: apknife/modules/extractor.py:99:12 213 | 98 try: 214 | 99 subprocess.run( 215 | 100 ["objdump", "-d", so_file, "-M", "intel", "-o", disasm_file], check=True 216 | 101 ) 217 | 102 logging.info(f"✅ Disassembled {so_file} -> {disasm_file}") 218 | 219 | -------------------------------------------------- 220 | >> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input. 221 | Severity: Low Confidence: High 222 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 223 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b603_subprocess_without_shell_equals_true.html 224 | Location: apknife/modules/extractor.py:99:12 225 | 98 try: 226 | 99 subprocess.run( 227 | 100 ["objdump", "-d", so_file, "-M", "intel", "-o", disasm_file], check=True 228 | 101 ) 229 | 102 logging.info(f"✅ Disassembled {so_file} -> {disasm_file}") 230 | 231 | -------------------------------------------------- 232 | >> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path 233 | Severity: Low Confidence: High 234 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 235 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b607_start_process_with_partial_path.html 236 | Location: apknife/modules/extractor.py:105:12 237 | 104 # Radare2 analysis 238 | 105 subprocess.run(["r2", "-c", '"aaa; afl; pdf @ main"', so_file], check=True) 239 | 106 except subprocess.CalledProcessError as e: 240 | 241 | -------------------------------------------------- 242 | >> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input. 243 | Severity: Low Confidence: High 244 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 245 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b603_subprocess_without_shell_equals_true.html 246 | Location: apknife/modules/extractor.py:105:12 247 | 104 # Radare2 analysis 248 | 105 subprocess.run(["r2", "-c", '"aaa; afl; pdf @ main"', so_file], check=True) 249 | 106 except subprocess.CalledProcessError as e: 250 | 251 | -------------------------------------------------- 252 | >> Issue: [B110:try_except_pass] Try, Except, Pass detected. 253 | Severity: Low Confidence: High 254 | CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html) 255 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b110_try_except_pass.html 256 | Location: apknife/modules/extractor.py:148:20 257 | 147 ) 258 | 148 except Exception: 259 | 149 pass # Ignore decoding errors 260 | 150 261 | 262 | -------------------------------------------------- 263 | >> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module. 264 | Severity: Low Confidence: High 265 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 266 | More Info: https://bandit.readthedocs.io/en/1.8.3/blacklists/blacklist_imports.html#b404-import-subprocess 267 | Location: apknife/modules/interactive_mode.py:4:0 268 | 3 import os 269 | 4 import subprocess 270 | 5 import argparse 271 | 272 | -------------------------------------------------- 273 | >> Issue: [B602:subprocess_popen_with_shell_equals_true] subprocess call with shell=True identified, security issue. 274 | Severity: High Confidence: High 275 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 276 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b602_subprocess_popen_with_shell_equals_true.html 277 | Location: apknife/modules/interactive_mode.py:85:17 278 | 84 # Execute other shell commands 279 | 85 result = subprocess.run(command, shell=True, check=True, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 280 | 86 return result.stdout 281 | 282 | -------------------------------------------------- 283 | >> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module. 284 | Severity: Low Confidence: High 285 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 286 | More Info: https://bandit.readthedocs.io/en/1.8.3/blacklists/blacklist_imports.html#b404-import-subprocess 287 | Location: apknife/modules/java_extractor.py:4:0 288 | 3 import shutil 289 | 4 import subprocess 290 | 5 import zipfile 291 | 292 | -------------------------------------------------- 293 | >> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input. 294 | Severity: Low Confidence: High 295 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 296 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b603_subprocess_without_shell_equals_true.html 297 | Location: apknife/modules/java_extractor.py:57:8 298 | 56 try: 299 | 57 subprocess.run(["jadx", "-d", jadx_output] + dex_paths, check=True) 300 | 58 logging.info("[✔] Java files extracted successfully!") 301 | 302 | -------------------------------------------------- 303 | >> Issue: [B408:blacklist] Using minidom to parse untrusted XML data is known to be vulnerable to XML attacks. Replace minidom with the equivalent defusedxml package, or make sure defusedxml.defuse_stdlib() is called. 304 | Severity: Low Confidence: High 305 | CWE: CWE-20 (https://cwe.mitre.org/data/definitions/20.html) 306 | More Info: https://bandit.readthedocs.io/en/1.8.3/blacklists/blacklist_imports.html#b408-import-xml-minidom 307 | Location: apknife/modules/manifest_decoder.py:2:0 308 | 1 import logging 309 | 2 from xml.dom import minidom 310 | 3 from lxml import etree 311 | 312 | -------------------------------------------------- 313 | >> Issue: [B318:blacklist] Using xml.dom.minidom.parseString to parse untrusted XML data is known to be vulnerable to XML attacks. Replace xml.dom.minidom.parseString with its defusedxml equivalent function or make sure defusedxml.defuse_stdlib() is called 314 | Severity: Medium Confidence: High 315 | CWE: CWE-20 (https://cwe.mitre.org/data/definitions/20.html) 316 | More Info: https://bandit.readthedocs.io/en/1.8.3/blacklists/blacklist_calls.html#b313-b320-xml-bad-minidom 317 | Location: apknife/modules/manifest_decoder.py:23:21 318 | 22 # Use minidom to further prettify the XML 319 | 23 parsed_xml = minidom.parseString(manifest_str) 320 | 24 pretty_xml = parsed_xml.toprettyxml(indent=" ") 321 | 322 | -------------------------------------------------- 323 | >> Issue: [B405:blacklist] Using xml.etree.ElementTree to parse untrusted XML data is known to be vulnerable to XML attacks. Replace xml.etree.ElementTree with the equivalent defusedxml package, or make sure defusedxml.defuse_stdlib() is called. 324 | Severity: Low Confidence: High 325 | CWE: CWE-20 (https://cwe.mitre.org/data/definitions/20.html) 326 | More Info: https://bandit.readthedocs.io/en/1.8.3/blacklists/blacklist_imports.html#b405-import-xml-etree 327 | Location: apknife/modules/manifest_editor.py:4:0 328 | 3 import tempfile 329 | 4 import xml.etree.ElementTree as ET 330 | 5 import zipfile 331 | 332 | -------------------------------------------------- 333 | >> Issue: [B405:blacklist] Using xml.etree.ElementTree to parse untrusted XML data is known to be vulnerable to XML attacks. Replace xml.etree.ElementTree with the equivalent defusedxml package, or make sure defusedxml.defuse_stdlib() is called. 334 | Severity: Low Confidence: High 335 | CWE: CWE-20 (https://cwe.mitre.org/data/definitions/20.html) 336 | More Info: https://bandit.readthedocs.io/en/1.8.3/blacklists/blacklist_imports.html#b405-import-xml-etree 337 | Location: apknife/modules/permission_scanner.py:2:0 338 | 1 import logging 339 | 2 import xml.etree.ElementTree as ET 340 | 3 import zipfile 341 | 342 | -------------------------------------------------- 343 | >> Issue: [B314:blacklist] Using xml.etree.ElementTree.fromstring to parse untrusted XML data is known to be vulnerable to XML attacks. Replace xml.etree.ElementTree.fromstring with its defusedxml equivalent function or make sure defusedxml.defuse_stdlib() is called 344 | Severity: Medium Confidence: High 345 | CWE: CWE-20 (https://cwe.mitre.org/data/definitions/20.html) 346 | More Info: https://bandit.readthedocs.io/en/1.8.3/blacklists/blacklist_calls.html#b313-b320-xml-bad-elementtree 347 | Location: apknife/modules/permission_scanner.py:56:15 348 | 55 try: 349 | 56 root = ET.fromstring(manifest_data) 350 | 57 permissions = { 351 | 352 | -------------------------------------------------- 353 | >> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module. 354 | Severity: Low Confidence: High 355 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 356 | More Info: https://bandit.readthedocs.io/en/1.8.3/blacklists/blacklist_imports.html#b404-import-subprocess 357 | Location: apknife/modules/protection_scanner.py:2:0 358 | 1 import os 359 | 2 import subprocess 360 | 3 from androguard.core.apk import APK 361 | 362 | -------------------------------------------------- 363 | >> Issue: [B602:subprocess_popen_with_shell_equals_true] subprocess call with shell=True identified, security issue. 364 | Severity: High Confidence: High 365 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 366 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b602_subprocess_popen_with_shell_equals_true.html 367 | Location: apknife/modules/protection_scanner.py:174:17 368 | 173 # Execute other shell commands 369 | 174 result = subprocess.run(command, shell=True, check=True, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 370 | 175 return result.stdout 371 | 372 | -------------------------------------------------- 373 | >> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module. 374 | Severity: Low Confidence: High 375 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 376 | More Info: https://bandit.readthedocs.io/en/1.8.3/blacklists/blacklist_imports.html#b404-import-subprocess 377 | Location: apknife/modules/signer.py:1:0 378 | 1 import subprocess 379 | 2 380 | 3 381 | 382 | -------------------------------------------------- 383 | >> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path 384 | Severity: Low Confidence: High 385 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 386 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b607_start_process_with_partial_path.html 387 | Location: apknife/modules/signer.py:6:8 388 | 5 try: 389 | 6 subprocess.run(["apksigner", "sign", "--ks", "my-release-key.jks", apk_path]) 390 | 7 print(f"✅ Signed APK: {apk_path}") 391 | 392 | -------------------------------------------------- 393 | >> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input. 394 | Severity: Low Confidence: High 395 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 396 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b603_subprocess_without_shell_equals_true.html 397 | Location: apknife/modules/signer.py:6:8 398 | 5 try: 399 | 6 subprocess.run(["apksigner", "sign", "--ks", "my-release-key.jks", apk_path]) 400 | 7 print(f"✅ Signed APK: {apk_path}") 401 | 402 | -------------------------------------------------- 403 | >> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module. 404 | Severity: Low Confidence: High 405 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 406 | More Info: https://bandit.readthedocs.io/en/1.8.3/blacklists/blacklist_imports.html#b404-import-subprocess 407 | Location: apknife/modules/smali_tools.py:2:0 408 | 1 import os 409 | 2 import subprocess 410 | 3 411 | 412 | -------------------------------------------------- 413 | >> Issue: [B602:subprocess_popen_with_shell_equals_true] subprocess call with shell=True identified, security issue. 414 | Severity: High Confidence: High 415 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 416 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b602_subprocess_popen_with_shell_equals_true.html 417 | Location: apknife/modules/smali_tools.py:12:8 418 | 11 try: 419 | 12 subprocess.run(cmd, shell=True, check=True) 420 | 13 print(f"✅ Smali code extracted to {output_dir}") 421 | 422 | -------------------------------------------------- 423 | >> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module. 424 | Severity: Low Confidence: High 425 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 426 | More Info: https://bandit.readthedocs.io/en/1.8.3/blacklists/blacklist_imports.html#b404-import-subprocess 427 | Location: apknife/modules/vulnerability_scanner.py:3:0 428 | 2 import os 429 | 3 import subprocess 430 | 4 import defusedxml.ElementTree as ET 431 | 432 | -------------------------------------------------- 433 | >> Issue: [B602:subprocess_popen_with_shell_equals_true] subprocess call with shell=True identified, security issue. 434 | Severity: High Confidence: High 435 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 436 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b602_subprocess_popen_with_shell_equals_true.html 437 | Location: apknife/modules/vulnerability_scanner.py:15:4 438 | 14 command = f"apktool d -f {apk_path} -o {output_dir}" 439 | 15 subprocess.run(command, shell=True, check=True) 440 | 16 logging.info(f"[+] APK decompiled to: {output_dir}") 441 | 442 | -------------------------------------------------- 443 | >> Issue: [B602:subprocess_popen_with_shell_equals_true] subprocess call with shell=True identified, security issue. 444 | Severity: High Confidence: High 445 | CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) 446 | More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b602_subprocess_popen_with_shell_equals_true.html 447 | Location: apknife/modules/vulnerability_scanner.py:25:4 448 | 24 command = f"jadx -d {output_dir} {apk_path}" 449 | 25 subprocess.run(command, shell=True, check=True) 450 | 26 logging.info(f"[+] Source code extracted to: {output_dir}") 451 | 452 | -------------------------------------------------- 453 | 454 | Code scanned: 455 | Total lines of code: 1504 456 | Total lines skipped (#nosec): 0 457 | Total potential issues skipped due to specifically being disabled (e.g., #nosec BXXX): 0 458 | 459 | Run metrics: 460 | Total issues (by severity): 461 | Undefined: 0 462 | Low: 34 463 | Medium: 3 464 | High: 7 465 | Total issues (by confidence): 466 | Undefined: 0 467 | Low: 1 468 | Medium: 0 469 | High: 43 470 | Files skipped (0): 471 | --------------------------------------------------------------------------------