Created
June 13, 2023 00:22
-
-
Save Geczy/cc1f3f1b5d7d7a44fbaa0c204b5b7567 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
set -e | |
# check os compatibility | |
system=$(uname) | |
if [ "$system" = "Windows" ]; then | |
echo "Windows is not currently supported." | |
exit 1 | |
fi | |
# set/get all args | |
while getopts "i:o:c:f:uw" opt; do | |
case $opt in | |
i) | |
ipa=$OPTARG | |
;; | |
o) | |
output=$OPTARG | |
;; | |
c) | |
compression_level=$OPTARG | |
;; | |
f) | |
files+=("$OPTARG") | |
;; | |
u) | |
remove_UISupportedDevices=true | |
;; | |
w) | |
remove_watch_app=true | |
;; | |
\?) | |
exit 1 | |
;; | |
esac | |
done | |
# checking received args | |
if [ "${ipa: -4}" != ".ipa" ] || [ "${output: -4}" != ".ipa" ]; then | |
echo "The input and output file must be an ipa." | |
exit 1 | |
fi | |
if [ ! -f "$ipa" ]; then | |
echo "$ipa does not exist." | |
exit 1 | |
fi | |
if [ ${#files[@]} -eq 0 ] && [ -z "$remove_UISupportedDevices" ] && [ -z "$remove_watch_app" ]; then | |
echo "At least one option to modify the ipa must be present." | |
exit 1 | |
fi | |
cleanup() { | |
rm -rf "$extract_dir" | |
if [ "$1" = true ]; then | |
exit 0 | |
else | |
exit 1 | |
fi | |
} | |
# extracting ipa | |
echo "[*] extracting ipa.." | |
extract_dir=".pyzule-$(date +%s)" | |
mkdir "$extract_dir" | |
unzip "$ipa" -d "$extract_dir" > /dev/null | |
echo "[*] extracted ipa successfully" | |
# checking if everything exists (to see if it's a valid ipa) | |
app_path=$(find "$extract_dir"/Payload -type d -name "*.app" | head -n 1) | |
plist_path=$(find "$app_path" -type f -name "Info.plist" | head -n 1) | |
if [ -z "$app_path" ] || [ -z "$plist_path" ]; then | |
echo "[!] Couldn't find Payload folder and/or Info.plist file, invalid ipa specified" | |
cleanup false | |
fi | |
# injecting stuff | |
if [ ${#files[@]} -gt 0 ]; then | |
binary=$(plutil -convert json -o - "$plist_path" | grep -o 'CFBundleExecutable[^,]*' | cut -d '"' -f 4) | |
if [[ " ${files[@]} " =~ \.appex ]]; then | |
mkdir -p "$app_path/PlugIns" | |
fi | |
if [[ " ${files[@]} " =~ \.(deb|dylib|framework) ]]; then | |
mkdir -p "$app_path/Frameworks" | |
deb_counter=0 | |
fi | |
dylibs=() | |
id=("${dylibs[@]}" $(for file in "${files[@]}"; do if [[ "$file" == *.dylib ]]; then echo "$file"; fi; done)) | |
remove=() | |
substrate_injected=0 | |
# extracting all debs | |
for deb in "${files[@]}"; do | |
if [ "${deb: -4}" != ".deb" ]; then | |
continue | |
fi | |
bn=$(basename "$deb") | |
echo "[*] extracting $bn.." | |
output="$extract_dir/$deb_counter" | |
mkdir -p "$output" "$output/e" | |
if [ "$system" = "Linux" ]; then | |
ar -x "$deb" --output="$output" > /dev/null | |
else | |
tar -xf "$deb" -C "$output" > /dev/null | |
fi | |
data_tar=$(find "$output" -name "data.*") | |
tar -xf "$data_tar" -C "$output/e" > /dev/null | |
find "$output/e" -type f -name "*.dylib" -exec cp {} "$WORKING_DIR" \; | |
find "$output/e" \( -type d -name "*.bundle" -o -name "*.framework" \) -exec cp -r {} "$WORKING_DIR" \; | |
echo "[*] extracted $bn successfully" | |
deb_counter=$((deb_counter + 1)) | |
done | |
# remove codesign + fix all dependencies | |
for dylib in "${dylibs[@]}"; do | |
ldid -S "$dylib" > /dev/null | |
deps_temp=$(otool -L "$dylib" | tail -n +3 | awk '{print $1}' | grep -E '^/Library/|^/usr/lib') | |
deps=() | |
while IFS= read -r dep; do | |
deps+=("$dep") | |
done <<< "$deps_temp" | |
if [[ " ${deps_temp[@]} " =~ substrate ]]; then | |
install_name_tool -change "/Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate" "@rpath/CydiaSubstrate.framework/CydiaSubstrate" "$dylib" > /dev/null | |
install_name_tool -change "@executable_path/libsubstrate.dylib" "@rpath/CydiaSubstrate.framework/CydiaSubstrate" "$dylib" > /dev/null | |
if [ "$substrate_injected" -eq 0 ]; then | |
if [ ! -d "$app_path/Frameworks/CydiaSubstrate.framework" ]; then | |
cp -R "$USER_DIR/CydiaSubstrate.framework" "$app_path/Frameworks/CydiaSubstrate.framework" | |
fi | |
echo "[*] injected CydiaSubstrate.framework and fixed dependencies" | |
substrate_injected=1 | |
fi | |
fi | |
for dep in "${deps[@]}"; do | |
for known in "${id[@]}"; do | |
if [[ "$dep" == *"$known"* ]]; then | |
bn=$(basename "$dep") | |
if [[ "$dep" == *.dylib ]]; then | |
fni=$(echo "$dep" | awk -v bn="$bn" '{print index($0, bn)}') | |
install_name_tool -change "${dep:0:fni}$bn" "@rpath/$bn" "$dylib" > /dev/null | |
echo "[*] fixed dependency in $dylib: ${dep:0:fni}$bn -> @rpath/$bn" | |
elif [[ "$dep" == *".framework" ]]; then | |
fni=$(echo "$dep" | awk -v bn="$bn" '{print index($0, bn ".framework/" bn)}') | |
install_name_tool -change "${dep:0:fni}$bn.framework/$bn" "@rpath/$bn.framework/$bn" "$dylib" > /dev/null | |
echo "[*] fixed dependency in $dylib: ${dep:0:fni}$bn.framework/$bn -> @rpath/$bn.framework/$bn" | |
fi | |
fi | |
done | |
done | |
done | |
echo "[*] injecting.." | |
for d in "${dylibs[@]}"; do | |
bn=$(basename "$d") | |
insert_dylib --inplace --no-strip-codesig "@rpath/$bn" "$app_path/$BINARY" > /dev/null | |
cp "$d" "$app_path/Frameworks/$bn" | |
echo "[*] successfully injected $bn" | |
done | |
for tweak in "${args_f[@]}"; do | |
bn=$(basename "$tweak") | |
if [[ "$tweak" == *".framework" ]]; then | |
cp -R "$tweak" "$app_path/Frameworks/$bn" | |
echo "[*] successfully injected $bn" | |
elif [[ "$tweak" == *".appex" ]]; then | |
cp -R "$tweak" "$app_path/PlugIns/$bn" | |
echo "[*] successfully copied $bn to PlugIns" | |
elif [[ ! " ${dylibs[*]} " =~ " $tweak " && ! "$tweak" == *".deb" ]]; then | |
if [ -d "$tweak" ]; then | |
cp -R "$tweak" "$app_path/$bn" | |
else | |
cp "$tweak" "$app_path/$bn" | |
fi | |
echo "[*] successfully copied $bn to app root" | |
fi | |
done | |
changed=1 | |
for r in "${remove[@]}"; do | |
if [ -f "$r" ]; then | |
rm "$r" | |
else | |
rm -r "$r" | |
fi | |
done | |
fi | |
# removing UISupportedDevices (if specified) | |
if [ "$args_u" -eq 1 ]; then | |
echo "[*] removing UISupportedDevices.." | |
plist_tmp=$(mktemp /tmp/plist.XXXXXX) | |
plutil -convert xml1 -o "$plist_tmp" "$plist_path" | |
if [ $(/usr/libexec/PlistBuddy -c 'Print :UISupportedDevices' "$plist_tmp" 2> /dev/null | wc -l) -ne 0 ]; then | |
/usr/libexec/PlistBuddy -c 'Delete :UISupportedDevices' "$plist_tmp" > /dev/null | |
echo "[*] removed UISupportedDevices" | |
changed=1 | |
plutil -convert binary1 -o "$plist_path" "$plist_tmp" | |
else | |
echo "[?] UISupportedDevices not present" | |
fi | |
rm "$plist_tmp" | |
fi | |
# removing watch app (if specified) | |
if [ "$args_w" -eq 1 ]; then | |
echo "[*] removing watch app.." | |
if [ -d "$app_path/Watch" ]; then | |
rm -r "$app_path/Watch" | |
echo "[*] removed watch app" | |
changed=1 | |
else | |
echo "[?] watch app not present" | |
fi | |
fi | |
# checking if anything was actually changed | |
if [ "$changed" -eq 0 ]; then | |
echo "[!] nothing was changed, output file will not be created" | |
cleanup "$EXTRACT_DIR" true | |
fi | |
# zipping everything back into an ipa | |
cd "$EXTRACT_DIR" || exit 1 | |
echo "[*] generating ipa using compression level $args_c.." | |
zip "-$args_c" -r "$(basename "$args_o")" Payload > /dev/null | |
# cleanup when everything is done | |
cd "$WORKING_DIR" || exit 1 | |
if [[ "$args_o" == */* ]]; then | |
o2=$(dirname "$args_o") | |
mkdir -p "$o2" | |
fi | |
mv "$EXTRACT_DIR/$(basename "$args_o")" "$args_o" | |
echo "[*] generated ipa at $args_o" | |
echo "[*] deleting temporary files.." | |
cleanup "$EXTRACT_DIR" true |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment