From 5c0363b5a95a1748590e2274e5ea7f3a7719ae08 Mon Sep 17 00:00:00 2001 From: zx Date: Sun, 8 Sep 2024 18:48:05 -0400 Subject: [PATCH] feat: plist operations --- README.md | 2 +- cyan/__main__.py | 18 +++++++- cyan/logic.py | 8 ++++ cyan/tbhtypes/app_bundle.py | 2 +- cyan/tbhtypes/plist.py | 83 ++++++++++++++++++++++++++++++++----- 5 files changed, 100 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index a06050d..d8a4de9 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ then finally, to install or update cyan, just `pip install --force-reinstall git [x] refactor: dont prepare, just copy and fix as you go -[2] feat: plist operations (-n, -v, -b, -m, -l, -r) +[x] feat: plist operations (-l and -r wont be implemented) [x] feat: remove watch app diff --git a/cyan/__main__.py b/cyan/__main__.py index 3b7a052..fbd3f19 100755 --- a/cyan/__main__.py +++ b/cyan/__main__.py @@ -26,6 +26,22 @@ def main() -> None: "-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( "-u", "--remove-supported-devices", action="store_true", @@ -71,7 +87,7 @@ def main() -> None: ) parser.add_argument( - "-v", "--version", action="version", version="cyan v1.0" + "--version", action="version", version="cyan v1.0" ) from cyan import logic diff --git a/cyan/logic.py b/cyan/logic.py index 9772243..8d9b870 100644 --- a/cyan/logic.py +++ b/cyan/logic.py @@ -47,6 +47,14 @@ def main(parser: ArgumentParser) -> None: if args.f is not None: app.executable.inject(args.f, tmpdir) + if args.n is not None: + app.plist.change_name(args.n) + if args.v is not None: + app.plist.change_version(args.v) + if args.b is not None: + app.plist.change_bundle_id(args.b) + if args.m is not None: + app.plist.change_minimum_version(args.m) if args.remove_supported_devices: app.plist.remove_uisd() diff --git a/cyan/tbhtypes/app_bundle.py b/cyan/tbhtypes/app_bundle.py index 9b33ee9..cfa00f6 100644 --- a/cyan/tbhtypes/app_bundle.py +++ b/cyan/tbhtypes/app_bundle.py @@ -9,7 +9,7 @@ from .plist import Plist class AppBundle: def __init__(self, path: str): self.path = path - self.plist = Plist(f"{path}/Info.plist") + self.plist = Plist(f"{path}/Info.plist", path) self.executable = Executable( f"{path}/{self.plist['CFBundleExecutable']}", diff --git a/cyan/tbhtypes/plist.py b/cyan/tbhtypes/plist.py index f5ed185..1d5c023 100644 --- a/cyan/tbhtypes/plist.py +++ b/cyan/tbhtypes/plist.py @@ -1,16 +1,23 @@ import sys import plistlib -from typing import Any +from glob import glob +from typing import Optional, Any class Plist: - def __init__(self, path: str): + def __init__( + self, path: str, app_path: Optional[str] = None, throw: bool = True + ): try: with open(path, "rb") as f: self.data: dict[str, Any] = plistlib.load(f) except Exception: - sys.exit(f"[!] couldn't read {path}") + if throw: + sys.exit(f"[!] couldn't read {path}") + else: + return None self.path = path + self.app_path = app_path def __getitem__(self, key: str) -> Any: return self.data.get(key, None) @@ -25,33 +32,89 @@ class Plist: def remove(self, key: str) -> bool: try: del self.data[key] + self.save() return True except KeyError: return False - def change(self, key: str, val: Any) -> bool: + def change(self, val: Any, *keys: str) -> bool: try: - if self[key] == val: + if all(self[key] == val for key in keys): return False except KeyError: - self[key] = val + for key in keys: + self[key] = val + self.save() return True def remove_uisd(self) -> None: if self.remove("UISupportedDevices"): - self.save() print("[*] removed UISupportedDevices") else: print("[?] no UISupportedDevices") def enable_documents(self) -> None: - c1 = self.change("UISupportsDocumentBrowser", True) - c2 = self.change("UIFileSharingEnabled", True) + c1 = self.change(True, "UISupportsDocumentBrowser") + c2 = self.change(True, "UIFileSharingEnabled") if c1 or c2: - self.save() print("[*] enabled documents support") else: print("[?] documents support was already enabled") + def change_name(self, name: str) -> None: + if self.change("CFBundleName", "CFBundleDisplayName", name): + print(f"[*] changed name to \"{name}\"") + changed = 0 + + for lproj in glob(f"{self.app_path}/*.lproj"): + try: + pl = Plist(f"{lproj}/InfoPlist.strings", None, False) + pl["CFBundleDisplayName"] = name + pl["CFBundleName"] = name + pl.save() + changed += 1 + except Exception: + pass # file might not exist + + if changed != 0: + print(f"[*] changed \033[96m{changed}\033[0m localized names") + else: + print(f"[?] name was already \"{name}\"") + + def change_version(self, version: str) -> None: + if self.change("CFBundleVersion", "CFBundleShortVersionString", version): + print(f"[*] changed version to \"{version}\"") + else: + print(f"[?] version was already \"{version}\"") + + def change_bundle_id(self, bundle_id: str) -> None: + orig = self["CFBundleIdentifier"] + + if self.change("CFBundleIdentifier", bundle_id): + print(f"[*] changed bundle id to \"{bundle_id}\"") + changed = 0 + + # change all other bundle ids + for ext in glob(f"{self.app_path}/*/*.appex"): + try: + pl = Plist(f"{ext}/Info.plist", None, False) + current = pl["CFBundleIdentifier"] + pl["CFBundleIdentifier"] = current.replace(orig, bundle_id) + pl.save() + changed += 1 + except Exception: + pass # how tf would it not exist? idk + + if changed != 0: + print(f"[*] changed \033[96m{changed}\033[0m other bundle ids") + else: + print(f"[?] bundle id was already \"{bundle_id}\"") + + def change_minimum_version(self, minimum: str) -> None: + if self.change("MinimumOSVersion", minimum): + print(f"[*] changed minimum version to \"{minimum}\"") + else: + print(f"[?] minimum version was already \"{minimum}\"") +