diff --git a/README.md b/README.md index 431970d..136e5f3 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,14 @@ # pyzule-rw / cyan -rewriting [pyzule](https://github.com/asdfzxcvbn/pyzule), but actually good this time! btw.. cyan/pyzule looks best with a monospace font !! :D +a rewrite of [pyzule](https://github.com/asdfzxcvbn/pyzule) that doesn't (completely) suck !! -this is pretty much just a hobby project that i'll work on when i'm bored. pyzule in its current state is kinda ugly, but it works and i haven't really had the motivation to work on it. there really isn't any need for improvements, anyway.. - -`cyan` will ~~target python v3.12 (finally!)~~, be pep8-compliant (hopefully..), and be type-hinted! +cyan supports **linux, macOS, WSL, and jailbroken iOS!** all either x86_64 or arm64/aarch64 !! ## features more coming soon! i'm trying to copy pyzule in order to deprecate it in favor of cyan, which is 1000x better +- generate and use [shareable .cyan files](https://github.com/asdfzxcvbn/cyan-gen) to configure IPAs! - inject deb, dylib, framework, bundle, and appex files/folders - automatically fix dependencies on CydiaSubstrate **(cyan uses [ElleKit](https://github.com/evelyneee/ellekit/)!)**, Cephei*, and Orion - copy any unknown file/folder types to app root @@ -23,8 +22,6 @@ more coming soon! i'm trying to copy pyzule in order to deprecate it in favor of ## install instructions -cyan works on linux, macOS, WSL, and jailbroken iOS! - first, make sure you have [ar](https://command-not-found.com/ar) and [tar](https://command-not-found.com/tar) installed also obviously install python, version 3.9 or greater is required (the version available on the procursus repo for iOS) @@ -35,7 +32,12 @@ also obviously install python, version 3.9 or greater is required (the version a then finally, to install or update cyan, just `pip install --force-reinstall git+https://github.com/asdfzxcvbn/pyzule-rw.git#egg=cyan` -## todo +## acknowledgements + +- [Al4ise](https://github.com/Al4ise) for the original [Azule](https://github.com/Al4ise/Azule) +- [lief-project](https://github.com/lief-project) for [LIEF](https://github.com/lief-project/LIEF) + +### todo [x] refactor: dont prepare, just copy and fix as you go @@ -51,5 +53,7 @@ then finally, to install or update cyan, just `pip install --force-reinstall git [x] feat: change app icon -[] feat: .cyan files (lol rip .pyzule files) +[x] feat: .cyan files (lol rip .pyzule files) + +:D diff --git a/cyan/__main__.py b/cyan/__main__.py index dd4e1e9..758ade4 100755 --- a/cyan/__main__.py +++ b/cyan/__main__.py @@ -21,6 +21,10 @@ def main() -> None: "-o", "--output", metavar="output", help="if unspecified, overwrites input" ) + parser.add_argument( + "-z", "--cyan", metavar="cyan", + help="the .cyan file to use" + ) parser.add_argument( "-f", metavar="file", nargs="+", diff --git a/cyan/logic.py b/cyan/logic.py index f104e98..1d0095a 100644 --- a/cyan/logic.py +++ b/cyan/logic.py @@ -38,6 +38,10 @@ def main(parser: ArgumentParser) -> None: else: sys.exit("[!] main binary is encrypted; exiting") + if args.cyan is not None: + changing = vars(args) + tbhutils.parse_cyan(changing, tmpdir) + # this goes before injection, # since user might inject their own extensions if args.remove_extensions: diff --git a/cyan/tbhutils.py b/cyan/tbhutils.py index 334d533..8e162bb 100644 --- a/cyan/tbhutils.py +++ b/cyan/tbhutils.py @@ -1,13 +1,14 @@ import os import sys +import json import shutil import zipfile import platform import subprocess from uuid import uuid4 -from typing import Optional from glob import glob, iglob from argparse import Namespace +from typing import Optional, Any def validate_inputs(args: Namespace) -> Optional[str]: @@ -57,6 +58,9 @@ def validate_inputs(args: Namespace) -> Optional[str]: if args.k is not None and not os.path.isfile(args.k): sys.exit(f"[!] {args.k} does not exist") + if args.cyan is not None and not os.path.isfile(args.cyan): + sys.exit(f"[!] {args.cyan} does not exist") + def get_app(path: str, tmpdir: str, is_ipa: bool) -> str: payload = f"{tmpdir}/Payload" @@ -179,3 +183,29 @@ def make_ipa(tmpdir: str, output: str, level: int) -> None: if weird != 0: print(f"[?] was unable to zip {weird} file(s) due to timestamps") + +def parse_cyan(args: dict[str, Any], tmpdir: str) -> None: + print("[*] parsing .cyan file..") + with zipfile.ZipFile(args["cyan"]) as zf: + DOT_PATH = f"{tmpdir}/cyan" + os.mkdir(DOT_PATH) + + with zf.open("config.json") as f: + config = json.load(f) + + if "f" in config: + NAMES = [n for n in zf.namelist() if n.startswith("inject/")] + zf.extractall(DOT_PATH, NAMES) + + # ensure not None + args["f"] = args["f"] if args["f"] is not None else {} + for e in os.scandir(f"{DOT_PATH}/inject"): + args["f"][e.name] = e.path + del config["f"] + if "k" in config: + args["k"] = zf.extract("icon.idk", DOT_PATH) + del config["k"] + + for k, v in config.items(): + args[k] = v + diff --git a/test.cyan b/test.cyan new file mode 100644 index 0000000..14a1221 Binary files /dev/null and b/test.cyan differ