8 Commits

Author SHA1 Message Date
zx
d1c3bc6908 chore: bump version to v1.1.5 2024-09-15 15:17:40 -04:00
zx
8fe0fc2984 fix: plist changing operations (#3)
lol, this is so fucking stupid, i was trying to fix #3 but also found that `Plist.change()` was being used incorrectly EVERYWHERE in cyan, LMAO

slight oversight since i forgot i had even changed its usage, but at least it's fixed now
2024-09-15 15:16:43 -04:00
zx
e315454544 docs: acknowledgements 2024-09-14 21:01:04 -04:00
zx
9072c91965 docs(install): remove ensurepath step 2024-09-14 20:51:06 -04:00
zx
06a2ea8dad chore: bump version to v1.1.4 2024-09-13 18:28:35 -04:00
zx
55b2ec9d07 fix: folders ending in / 2024-09-13 18:27:45 -04:00
zx
52b3b11a1a chore: use insert_dylib on more platforms 2024-09-13 18:07:00 -04:00
zx
4375b7f880 docs: update install instructions 2024-09-11 17:33:40 -04:00
10 changed files with 66 additions and 40 deletions

View File

@@ -2,8 +2,6 @@
a rewrite of [pyzule](https://github.com/asdfzxcvbn/pyzule) that doesn't (completely) suck !!
cyan supports **linux, macOS, WSL, and jailbroken iOS!** all either x86_64 or arm64/aarch64 !!
## features
you can open an issue to request a feature :D !!
@@ -22,17 +20,32 @@ you can open an issue to request a feature :D !!
## install instructions
cyan supports **linux, macOS, WSL, and jailbroken iOS!** all either x86_64 or arm64/aarch64 !!
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)
also obviously install python, version 3.9 or greater is required
1. install [pipx](https://github.com/pypa/pipx?tab=readme-ov-file#install-pipx)
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`
<details>
<summary><b>linux/WSL/macOS instructions</b></summary>
<br/>
<ol>
<li>install <a href="https://github.com/pypa/pipx?tab=readme-ov-file#install-pipx">pipx</a></li>
<li>install OR update cyan: <code>pipx install --force https://github.com/asdfzxcvbn/pyzule-rw/archive/main.zip</code></li>
<li><b>if you want to inject dylibs ON AARCH64 LINUX</b>: <code>pipx inject cyan lief</code></li>
<li><b>if you want to change app icons (iOS NOT supported)</b>: <code>pipx inject cyan Pillow</code></li>
</ol>
</details>
to *update*, just run step 2 again !
<details>
<summary><b>jailbroken iOS instructions / automated environment (github workflow, etc)</b></summary>
<br/>
<ol>
<li>install OR update cyan: <code>pip install --force-reinstall https://github.com/asdfzxcvbn/pyzule-rw/archive/main.zip</code></li>
</ol>
</details>
note: if you installed cyan before v1.1.3 using `pip`, make sure you `pip uninstall cyan`, then verify you have the latest version with `cyan --version`
## making cyan files
@@ -42,4 +55,6 @@ cyan comes bundled with the `cgen` command, which lets you generate `.cyan` file
- [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)
- [tyilo](https://github.com/tyilo) for [insert_dylib](https://github.com/tyilo/insert_dylib/) (macOS/iOS)
- [LeanVel](https://github.com/LeanVel) for [insert_dylib](https://github.com/LeanVel/insert_dylib) (linux)

View File

@@ -95,7 +95,7 @@ def main() -> None:
)
parser.add_argument(
"--version", action="version", version="cyan v1.1.3"
"--version", action="version", version="cyan v1.1.5"
)
from cyan import logic

View File

@@ -86,6 +86,6 @@ def main(parser: ArgumentParser) -> None:
if os.path.isdir(args.o):
shutil.rmtree(args.o)
shutil.move(app_path, args.o)
shutil.move(app.path, args.o)
print(f"[*] generated app at {args.o}")

View File

@@ -18,6 +18,7 @@ class Executable:
ldid = f"{specific}/ldid"
lipo = f"{specific}/lipo"
otool = f"{specific}/otool"
idylib = f"{specific}/insert_dylib"
starters = ("\t/Library/", "\t@rpath", "\t@executable_path")
common = {
@@ -42,10 +43,10 @@ class Executable:
self.bn = os.path.basename(path)
self.inj: Optional = None # type: ignore
if self.specific.endswith("iOS"):
self.inj_func = self.ios_inject
if os.path.isfile(self.idylib):
self.inj_func = self.idyl_inject
else:
self.inj_func = self.insert_cmd
self.inj_func = self.lief_inj
def is_encrypted(self) -> bool:
proc = subprocess.run(
@@ -177,7 +178,7 @@ class Executable:
stderr=subprocess.DEVNULL
)
def insert_cmd(self, cmd: str) -> None:
def lief_inj(self, cmd: str) -> None:
if self.inj is None: # type: ignore
try:
lief.logging.disable() # type: ignore
@@ -186,17 +187,22 @@ class Executable:
self.inj = lief.parse(self.path) # type: ignore
self.inj.add(lief.MachO.DylibCommand.weak_lib(cmd)) # type: ignore
try:
self.inj.add(lief.MachO.DylibCommand.weak_lib(cmd)) # type: ignore
except AttributeError:
sys.exit("[!] couldn't add LC (lief), did you use a valid app?")
def ios_inject(self, cmd: str) -> None:
subprocess.run(
def idyl_inject(self, cmd: str) -> None:
proc = subprocess.run(
[
f"{self.specific}/insert_dylib",
"--weak", "--inplace", "--no-strip-codesig", "--all-yes",
self.idylib, "--weak", "--inplace", "--strip-codesig", "--all-yes",
cmd, self.path
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
], capture_output=True, text=True
)
if proc.returncode != 0:
sys.exit(f"[!] couldn't add LC (insert_dylib), error:\n{proc.stderr}")
def fix_dependencies(self, tweaks: dict[str, str], need: set[str]) -> None:
self.remove_signature()

View File

@@ -44,6 +44,7 @@ class Plist:
try:
if all(self[key] == val for key in keys):
return False
raise KeyError # lets pretend this was always here..
except KeyError:
for key in keys:
self[key] = val
@@ -67,15 +68,14 @@ class Plist:
print("[?] documents support was already enabled")
def change_name(self, name: str) -> None:
if self.change("CFBundleName", "CFBundleDisplayName", name):
if self.change(name, "CFBundleName", "CFBundleDisplayName"):
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.change(name, "CFBundleName", "CFBundleDisplayName")
pl.save()
changed += 1
except Exception:
@@ -87,7 +87,7 @@ class Plist:
print(f"[?] name was already \"{name}\"")
def change_version(self, version: str) -> None:
if self.change("CFBundleVersion", "CFBundleShortVersionString", version):
if self.change(version, "CFBundleVersion", "CFBundleShortVersionString"):
print(f"[*] changed version to \"{version}\"")
else:
print(f"[?] version was already \"{version}\"")
@@ -95,7 +95,7 @@ class Plist:
def change_bundle_id(self, bundle_id: str) -> None:
orig = self["CFBundleIdentifier"]
if self.change("CFBundleIdentifier", bundle_id):
if self.change(bundle_id, "CFBundleIdentifier"):
print(f"[*] changed bundle id to \"{bundle_id}\"")
changed = 0
@@ -116,7 +116,7 @@ class Plist:
print(f"[?] bundle id was already \"{bundle_id}\"")
def change_minimum_version(self, minimum: str) -> None:
if self.change("MinimumOSVersion", minimum):
if self.change(minimum, "MinimumOSVersion"):
print(f"[*] changed minimum version to \"{minimum}\"")
else:
print(f"[?] minimum version was already \"{minimum}\"")

View File

@@ -39,15 +39,19 @@ def validate_inputs(args: Namespace) -> Optional[str]:
sys.exit(0)
if args.f is not None:
# dictionary ensures unique names
args.f = {os.path.basename(f): os.path.realpath(f) for f in args.f}
nonexistent = [f for f in args.f.values() if not os.path.exists(f)]
new: dict[str, str] = {} # dictionary ensures unique names
if len(nonexistent) != 0:
print("[!] please ensure the following file(s) exist:")
for ne in nonexistent:
print(f"[?] - {ne}")
sys.exit(1)
for f in list(args.f):
if f[-1] == "/": # yeah this is stupid
f = f[:-1]
if not os.path.exists(f):
sys.exit(f"[!] \"{f}\" does not exist")
new[os.path.basename(f)] = os.path.realpath(f)
# i would've modified args.f directly, but it causes type-hinting error :(
args.f = new
if (
args.m is not None
@@ -106,11 +110,12 @@ def get_tools_dir() -> tuple[str, str]:
# thank god i dont have to use importlib,
# it's the WORST standard library package prior to 3.12
install_dir = os.path.dirname(__file__)
specific_dir = f"{install_dir}/tools/{system}/{mach}"
return (
install_dir,
f"{install_dir}/tools/{system}/{mach}"
)
if not os.path.isdir(specific_dir):
sys.exit(f"[!] cyan is not supported on: {system} {mach}")
return (install_dir, specific_dir)
def delete_if_exists(path: str, bn: str) -> bool:

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -4,7 +4,7 @@ from setuptools import setup
setup(
name="cyan",
version="1.1.3",
version="1.1.5",
description="finally, pyzule doesn't suck",
author="zx",
author_email="zx@hrzn.email",