From 0bd6393e78aea488bdf679ecb212ba928950d990 Mon Sep 17 00:00:00 2001 From: zx Date: Tue, 10 Sep 2024 17:35:42 -0400 Subject: [PATCH] chore: bundle cgen --- README.md | 28 ++-------- cgen/__main__.py | 140 +++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 7 ++- 3 files changed, 151 insertions(+), 24 deletions(-) create mode 100644 cgen/__main__.py diff --git a/README.md b/README.md index 3ce78ba..33763ee 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ cyan supports **linux, macOS, WSL, and jailbroken iOS!** all either x86_64 or ar you can open an issue to request a feature :D !! -- generate and use [shareable .cyan files](https://github.com/asdfzxcvbn/cyan-gen) to configure IPAs! +- generate and use shareable .cyan files 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 @@ -27,35 +27,19 @@ first, make sure you have [ar](https://command-not-found.com/ar) and [tar](https also obviously install python, version 3.9 or greater is required (the version available on the procursus repo for iOS) 1. install [pipx](https://github.com/pypa/pipx?tab=readme-ov-file#install-pipx) -2. install cyan: `pipx install https://github.com/asdfzxcvbn/pyzule-rw/archive/main.zip` +2. install cyan: `pipx install --force https://github.com/asdfzxcvbn/pyzule-rw/archive/main.zip` 3. follow any instructions `pipx` may have given you, like `pipx ensurepath` 4. **if you want to inject dylibs and ARE NOT ON iOS**: `pipx inject cyan lief` 5. **if you want to change app icons (iOS NOT supported)**: `pipx inject cyan Pillow` to *update*, just run step 2 again ! +## making cyan files + +cyan comes bundled with the `cgen` command, which lets you generate `.cyan` files to pass to `-z`/`--cyan` ! + ## 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 - -[x] feat: plist operations (-l and -r wont be implemented) - -[x] feat: remove watch app - -[x] feat: fakesign - -[x] feat: thin binaries - -[x] feat: plugin operations (-q, -e) - -[x] feat: change app icon - -[x] feat: .cyan files (lol rip .pyzule files) - -:D - diff --git a/cgen/__main__.py b/cgen/__main__.py new file mode 100644 index 0000000..f146333 --- /dev/null +++ b/cgen/__main__.py @@ -0,0 +1,140 @@ +import os +import sys +import json +import zipfile +import argparse + + +def main() -> None: + if sys.platform == "win32": + sys.exit("[!] windows is not supported") + + parser = argparse.ArgumentParser( + description="a tool to generate .cyan files" + ) + + parser.add_argument( + "-o", "--output", metavar="output", required=True, + help="output of the .cyan file" + ) + + parser.add_argument( + "-f", metavar="file", nargs="+", + help="a tweak to inject/item to be added to the bundle" + ) + parser.add_argument( + "-n", metavar="name", + help="modify the app's name" + ) + parser.add_argument( + "-v", metavar="version", + help="modify the app's version" + ) + parser.add_argument( + "-b", metavar="bundle id", + help="modify the app's bundle id" + ) + parser.add_argument( + "-m", metavar="minimum", + help="modify the app's minimum OS version" + ) + parser.add_argument( + "-k", metavar="icon", + help="modify the app's icon" + ) + + parser.add_argument( + "-u", "--remove-supported-devices", action="store_true", + help="remove UISupportedDevices" + ) + parser.add_argument( + "-w", "--no-watch", action="store_true", + help="remove all watch apps" + ) + parser.add_argument( + "-d", "--enable-documents", action="store_true", + help="enable documents support" + ) + parser.add_argument( + "-s", "--fakesign", action="store_true", + help="fakesign all binaries for use with appsync/trollstore" + ) + parser.add_argument( + "-q", "--thin", action="store_true", + help="thin all binaries to arm64, may largely reduce size" + ) + parser.add_argument( + "-e", "--remove-extensions", action="store_true", + help="remove all app extensions" + ) + parser.add_argument( + "-g", "--remove-encrypted", action="store_true", + help="only remove encrypted app extensions" + ) + + generate_cyan(parser) + + +def generate_cyan(parser: argparse.ArgumentParser) -> None: + args = parser.parse_args() + + # input validation + if args.m is not None and any(c not in "0123456789." for c in args.m): + parser.error(f"invalid minimum OS version: {args.m}") + if args.k is not None and not os.path.isfile(args.k): + parser.error(f"{args.k} does not exist") + if args.f is not None: + fake = [f for f in args.f if not os.path.exists(f)] + + # it would be great if everyone was using python 3.12 !!! + if len(fake) != 0: + parser.error(f"the following file(s) do not exist: {', '.join(fake)}") + + if not args.output.endswith(".cyan"): + print("[*] appended cyan file extension to output") + args.output += ".cyan" + if os.path.isfile(args.output): + try: + overwrite = input( + f"[<] {args.output} already exists. overwrite? [Y/n] " + ).lower().strip() + except KeyboardInterrupt: + sys.exit("\n[?] guess not") + + if overwrite not in ("y", "yes", ""): + sys.exit("[>] quitting.") + + real_args = {k: v for k, v in dict(vars(args)).items() if v} + del real_args["output"] + + for key in "fk": # these need files + if key in real_args: + real_args[key] = True + + print("[*] generating..") + with zipfile.ZipFile( + args.output, "w", zipfile.ZIP_DEFLATED, compresslevel=1 + ) as zf: + with zf.open("config.json", "w") as f: + f.write(json.dumps(real_args).encode()) + + if args.f is not None: + for f in args.f: + if os.path.isfile(f): + zf.write(f, f"inject/{os.path.basename(f)}") + else: # G YHUJMNFTGYHNFTGYHTGYHUT6Y7UJM8RFTYHNR564TY + for dp, _, files in os.walk(f): + for f2 in files: + thing = f"{dp}/{f2}" + zf.write( + thing, + f"inject/{os.path.relpath(thing, os.path.dirname(f))}" + ) # no, i don't know what this is doing. + + if args.k is not None: + zf.write(args.k, "icon.idk") + + +if __name__ == "__main__": + main() + diff --git a/setup.py b/setup.py index 4b72875..81ca5d5 100644 --- a/setup.py +++ b/setup.py @@ -8,12 +8,15 @@ setup( description="finally, pyzule doesn't suck", author="zx", author_email="zx@hrzn.email", - packages=["cyan", "cyan.tbhtypes"], + packages=["cyan", "cyan.tbhtypes", "cgen"], # install_requires=["lief"], python_requires=">=3.9", include_package_data=True, entry_points={ - "console_scripts": ["cyan=cyan.__main__:main"], + "console_scripts": [ + "cyan=cyan.__main__:main", + "cgen=cgen.__main__:main" + ], } )