Skip to content
This repository has been archived by the owner on Sep 9, 2024. It is now read-only.

Commit

Permalink
feat: .pyzule files
Browse files Browse the repository at this point in the history
  • Loading branch information
asdfzxcvbn committed Feb 10, 2024
1 parent 673dc71 commit 9d4f0ad
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 8 deletions.
6 changes: 5 additions & 1 deletion install-pyzule.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ mkdir -p ${PZ_DIR}
if [ ! -d ${PZ_DIR}/venv ]; then
echo "[*] installing required pip libraries.."
$PYTHON -m venv ${PZ_DIR}/venv > /dev/null
${PZ_DIR}/venv/bin/pip install -U Pillow lief &> /dev/null
${PZ_DIR}/venv/bin/pip install -U Pillow lief orjson &> /dev/null
elif [ ! -f ${PZ_DIR}/orjson_upd ]; then
touch ${PZ_DIR}/orjson_upd
echo "[*] installing new dependencies.."
${PZ_DIR}/venv/bin/pip install -U orjson &> /dev/null
fi

if [ ! -x "$(command -v ldid)" ]; then
Expand Down
50 changes: 43 additions & 7 deletions pyzule.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from subprocess import run, DEVNULL, CalledProcessError

import lief
import orjson
from PIL import Image

WORKING_DIR = os.getcwd()
Expand All @@ -30,6 +31,8 @@
help="the .ipa/.app to patch")
parser.add_argument("-o", metavar="output", type=str, required=True,
help="the name of the patched .ipa/.app that will be created")
parser.add_argument("-z", metavar=".pyzule", type=str, required=False,
help="the .pyzule file to get info from")
parser.add_argument("-n", metavar="name", type=str, required=False,
help="modify the app's name")
parser.add_argument("-v", metavar="version", type=str, required=False,
Expand Down Expand Up @@ -77,14 +80,16 @@
parser.error("the input file must be an ipa/app")
elif not os.path.exists(args.i):
parser.error(f"{args.i} does not exist")
elif not any((args.f, args.u, args.w, args.m, args.d, args.n, args.v, args.b, args.s, args.e, args.r, args.k, args.x, args.l)):
elif not any((args.z, args.f, args.u, args.w, args.m, args.d, args.n, args.v, args.b, args.s, args.e, args.r, args.k, args.x, args.l)):
parser.error("at least one option to modify the ipa must be present")
elif args.p and args.t:
# well, you know, you CAN, but i just dont wanna implement that.
# i would remove -p altogether but i already spent a considerable amount of time on it.
parser.error("sorry, you can't use substitute while injecting into @executable_path")
elif args.m and any(char not in "0123456789." for char in args.m):
parser.error(f"invalid OS version: {args.m}")
elif args.z and not os.path.isfile(args.z):
parser.error("the .pyzule file does not exist")
elif args.k and not os.path.isfile(args.k):
parser.error("the image file does not exist")
elif args.x and not os.path.isfile(args.x):
Expand All @@ -104,15 +109,47 @@
print("[>] quitting.")
sys.exit()
EXTRACT_DIR = f".pyzule-{time()}"
REAL_EXTRACT_DIR = os.path.join(os.getcwd(), EXTRACT_DIR)
os.makedirs((REAL_EXTRACT_DIR := os.path.join(os.getcwd(), EXTRACT_DIR)))

# i never thought i would write code this bad. im tired.
if args.z:
changing = vars(args)
with ZipFile(args.z) as zf:
with zf.open("config.json") as conf:
config = orjson.loads(conf.read())
DOT_PATH = os.path.join(REAL_EXTRACT_DIR, "dot/")
DOT_OTHER_PATH = os.path.join(REAL_EXTRACT_DIR, "dotother/")

if "f" in config:
DYL_NAMES = [name for name in zf.namelist() if name.startswith("inject/")]
zf.extractall(DOT_PATH, DYL_NAMES)
changing["f"] = changing["f"] if changing["f"] else []
for name in DYL_NAMES:
changing["f"].append(f"{DOT_PATH}{name}")
del config["f"]
if "k" in config:
zf.extract("icon.png", DOT_OTHER_PATH)
changing["k"] = f"{DOT_OTHER_PATH}icon.png"
del config["k"]
if "x" in config:
zf.extract("new.entitlements", DOT_OTHER_PATH)
changing["x"] = f"{DOT_OTHER_PATH}new.entitlements"
del config["x"]
if "l" in config:
zf.extract("merge.plist", DOT_OTHER_PATH)
changing["l"] = f"{DOT_OTHER_PATH}merge.plist"
del config["l"]

for k, v in config.items():
changing[k] = v

if args.f:
if (nonexistant := ", ".join(ne for ne in args.f if not os.path.exists(ne))):
if (nonexistent := ", ".join(ne for ne in args.f if not os.path.exists(ne))):
# yes, TOTALLY required.
if len(nonexistant.split(", ")) == 1:
print(f"[!] {nonexistant} does not exist")
if len(nonexistent.split(", ")) == 1:
print(f"[!] {nonexistent} does not exist")
else:
print(f"[!] {nonexistant} do not exist")
print(f"[!] {nonexistent} do not exist")
sys.exit(1)

if args.p:
Expand Down Expand Up @@ -186,7 +223,6 @@ def cleanup():
if INPUT_IS_IPA:
print("[*] extracting ipa..")
try:
os.makedirs(EXTRACT_DIR)
with ZipFile(args.i, "r") as ipa:
if not any(name.startswith("Payload/") for name in ipa.namelist()):
raise KeyError
Expand Down

0 comments on commit 9d4f0ad

Please sign in to comment.