Files
FridaBox/tools/fetch_frida_gadget.py
T

86 lines
3.2 KiB
Python
Raw Normal View History

2026-07-19 23:33:52 +03:30
#!/usr/bin/env python3
"""Fetch and verify the pinned official Frida Gadget Android ARM64 binary."""
from __future__ import annotations
import argparse
import hashlib
import json
import lzma
import pathlib
import shutil
import tempfile
import urllib.request
VERSION = "17.16.0"
ASSET = f"frida-gadget-{VERSION}-android-arm64.so.xz"
API_URL = f"https://api.github.com/repos/frida/frida/releases/tags/{VERSION}"
ROOT = pathlib.Path(__file__).resolve().parents[1]
DEST = ROOT / "app/src/main/jniLibs/arm64-v8a/libfrida-gadget.so"
CHECKSUM = ROOT / "tools/frida-gadget-17.16.0.sha256"
EXPECTED_SHA256 = "6bf149e5d1c5ec701e7b822cab57bb243f1c2a03318fa974fe373ee711a9ed9e"
def sha256(path: pathlib.Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
for chunk in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def release_asset_url() -> str:
request = urllib.request.Request(
API_URL,
headers={"Accept": "application/vnd.github+json", "User-Agent": "FridaBox-build"},
)
with urllib.request.urlopen(request, timeout=30) as response:
release = json.load(response)
if release.get("tag_name") != VERSION:
raise RuntimeError(f"GitHub returned unexpected release {release.get('tag_name')!r}")
for asset in release.get("assets", []):
if asset.get("name") == ASSET:
return str(asset["browser_download_url"])
raise RuntimeError(f"Release {VERSION} does not contain {ASSET}")
def fetch(force: bool = False) -> str:
if DEST.exists() and not force:
digest = sha256(DEST)
if digest != EXPECTED_SHA256:
raise RuntimeError(f"Existing Gadget SHA-256 mismatch: {digest}")
CHECKSUM.write_text(f"{digest} {DEST.name}\n", encoding="ascii")
return digest
DEST.parent.mkdir(parents=True, exist_ok=True)
with tempfile.TemporaryDirectory(prefix="fridabox-") as temp_dir:
archive = pathlib.Path(temp_dir) / ASSET
request = urllib.request.Request(release_asset_url(), headers={"User-Agent": "FridaBox-build"})
with urllib.request.urlopen(request, timeout=120) as response, archive.open("wb") as output:
shutil.copyfileobj(response, output)
temporary_output = pathlib.Path(temp_dir) / DEST.name
with lzma.open(archive, "rb") as source, temporary_output.open("wb") as output:
shutil.copyfileobj(source, output)
if temporary_output.stat().st_size < 1_000_000:
raise RuntimeError("Downloaded Gadget is unexpectedly small")
shutil.move(str(temporary_output), DEST)
digest = sha256(DEST)
if digest != EXPECTED_SHA256:
DEST.unlink(missing_ok=True)
raise RuntimeError(f"Downloaded Gadget SHA-256 mismatch: {digest}")
CHECKSUM.write_text(f"{digest} {DEST.name}\n", encoding="ascii")
return digest
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--force", action="store_true", help="replace an existing Gadget")
args = parser.parse_args()
digest = fetch(args.force)
print(f"Frida Gadget {VERSION}: {DEST}")
print(f"SHA-256: {digest}")
return 0
if __name__ == "__main__":
raise SystemExit(main())