feat: thinning

This commit is contained in:
zx
2024-09-06 16:57:50 -04:00
parent bacc28e9c7
commit 17da3e897c
6 changed files with 72 additions and 26 deletions

View File

@@ -43,6 +43,10 @@ def main() -> None:
"-s", "--fakesign", action="store_true", "-s", "--fakesign", action="store_true",
help="fakesign all binaries for use with appsync/trollstore" 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( parser.add_argument(
"-c", "--compress", metavar="level", type=int, default=6, "-c", "--compress", metavar="level", type=int, default=6,
@@ -53,6 +57,10 @@ def main() -> None:
"--ignore-encrypted", action="store_true", "--ignore-encrypted", action="store_true",
help="skip main binary encryption check" help="skip main binary encryption check"
) )
parser.add_argument(
"--overwrite", action="store_true",
help="overwrite existing files without confirming"
)
parser.add_argument( parser.add_argument(
"-v", "--version", action="version", version="cyan v1.0b" "-v", "--version", action="version", version="cyan v1.0b"

View File

@@ -49,6 +49,8 @@ def main(parser: ArgumentParser) -> None:
app.plist.enable_documents() app.plist.enable_documents()
if args.fakesign: if args.fakesign:
app.fakesign_all() app.fakesign_all()
if args.thin:
app.thin_all()
# create subdirectories if necessary # create subdirectories if necessary
if "/" in args.o: if "/" in args.o:
@@ -56,7 +58,7 @@ def main(parser: ArgumentParser) -> None:
# done ! # done !
if OUTPUT_IS_IPA: if OUTPUT_IS_IPA:
print("[*] generating ipa..") print(f"[*] generating ipa with compression level {args.compress}..")
tbhutils.make_ipa(tmpdir, args.o, args.compress) tbhutils.make_ipa(tmpdir, args.o, args.compress)
print(f"[*] generated ipa at {args.o}") print(f"[*] generated ipa at {args.o}")
else: else:

View File

@@ -1,6 +1,7 @@
import os import os
import shutil import shutil
from glob import glob from glob import glob
from typing import Optional
from .executable import Executable from .executable import Executable
from .plist import Plist from .plist import Plist
@@ -15,6 +16,8 @@ class AppBundle:
path path
) )
self.cached_executables: Optional[list[str]] = None
def remove(self, name: str) -> bool: def remove(self, name: str) -> bool:
path = f"{self.path}/{name}" path = f"{self.path}/{name}"
if not os.path.exists(path): if not os.path.exists(path):
@@ -38,20 +41,38 @@ class AppBundle:
else: else:
print("[?] watch app not present") print("[?] watch app not present")
def fakesign_all(self) -> None: def get_executables(self) -> list[str]:
self.executable.fakesign() return sum((
count = 1 glob(f"{self.path}/**/*.dylib", recursive=True),
for ts in sum((
glob(f"{self.path}/**/*.appex", recursive=True), glob(f"{self.path}/**/*.appex", recursive=True),
glob(f"{self.path}/**/*.framework", recursive=True) glob(f"{self.path}/**/*.framework", recursive=True)
), []): # type: ignore ), []) # type: ignore
def mass_operate(self, op: str, func: str) -> None:
# this works since we call this after injecting
if self.cached_executables is None:
self.cached_executables = self.get_executables()
if getattr(self.executable, func)():
count = 1
else:
count = 0
for ts in self.cached_executables:
if ts.endswith(".dylib"):
call = getattr(Executable(ts), func)()
else:
pl = Plist(f"{ts}/Info.plist") pl = Plist(f"{ts}/Info.plist")
Executable(f"{ts}/{pl['CFBundleExecutable']}").fakesign() call = getattr(Executable(f"{ts}/{pl['CFBundleExecutable']}"), func)()
if call:
count += 1 count += 1
for ts in glob(f"{self.path}/**/*.dylib", recursive=True): print(f"[*] {op} \033[96m{count}\033[0m item(s)")
Executable(ts).fakesign()
count += 1 def fakesign_all(self) -> None:
self.mass_operate("fakesigned", "fakesign")
def thin_all(self) -> None:
self.mass_operate("thinned", "thin")
print(f"[*] fakesigned \033[96m{count}\033[0m items")

View File

@@ -16,6 +16,7 @@ class Executable:
install_dir, specific = tbhutils.get_tools_dir() install_dir, specific = tbhutils.get_tools_dir()
nt = f"{specific}/install_name_tool" nt = f"{specific}/install_name_tool"
ldid = f"{specific}/ldid" ldid = f"{specific}/ldid"
lipo = f"{specific}/lipo"
otool = f"{specific}/otool" otool = f"{specific}/otool"
starters = ("\t/Library/", "\t@rpath", "\t@executable_path") starters = ("\t/Library/", "\t@rpath", "\t@executable_path")
@@ -183,12 +184,22 @@ class Executable:
subprocess.run(["ldid", f"-S{ENT_PATH}", self.path]) subprocess.run(["ldid", f"-S{ENT_PATH}", self.path])
print("[*] restored entitlements") print("[*] restored entitlements")
def fakesign(self, keep_entitlements: bool = True) -> None: def fakesign(self, keep_entitlements: bool = True) -> bool:
cmd = [self.ldid, "-S"] cmd = [self.ldid, "-S"]
if keep_entitlements: if keep_entitlements:
cmd.append("-M") cmd.append("-M")
subprocess.run(cmd + [self.path]) subprocess.run(cmd + [self.path])
return True
def thin(self) -> bool:
if subprocess.run(
[self.lipo, "-thin", "arm64", self.path, "-output", self.path],
stderr=subprocess.DEVNULL
).returncode == 0:
return True
return False
def change_dependency(self, old: str, new: str) -> None: def change_dependency(self, old: str, new: str) -> None:
subprocess.run([self.nt, "-change", old, new, self.path]) subprocess.run([self.nt, "-change", old, new, self.path])

View File

@@ -54,3 +54,4 @@ class Plist:
print("[*] enabled documents support") print("[*] enabled documents support")
else: else:
print("[?] documents support was already enabled") print("[?] documents support was already enabled")

View File

@@ -22,6 +22,9 @@ def validate_inputs(args: Namespace) -> Optional[str]:
return f"{args.i} does not exist" return f"{args.i} does not exist"
if os.path.exists(args.o): if os.path.exists(args.o):
if args.overwrite:
print(f"[*] {args.o} already exists; overwriting")
else:
try: try:
overwrite = input( overwrite = input(
f"[<] {args.o} already exists, overwrite it? [Y/n] " f"[<] {args.o} already exists, overwrite it? [Y/n] "