mirror of
https://github.com/faroukbmiled/RyukGram.git
synced 2026-06-01 05:01:35 +02:00
- Renamed project from SCInsta to RyukGram across all user-facing text,
settings UI, build scripts, workflows, control file, and Makefile
- Added built-in sideload compatibility patch:
keychain access group discovery, SecItem rebinding via fishhook,
NSFileManager app group fallback, Cloud Kit entitlement patches
- Added fishhook library (modules/fishhook/) for C function rebinding
- Updated README with new features, repo links, and credits
- Updated GitHub Actions workflows for RyukGram naming
- Plist renamed from SCInsta.plist to RyukGram.plist
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
# https://github.com/ISnackable/YTCubePlus/blob/main/.github/workflows/Build.yml
|
||||
# https://github.com/BandarHL/BHTwitter/actions/workflows/build.yml
|
||||
|
||||
name: Build and Package SCInsta
|
||||
name: Build and Package RyukGram
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -18,13 +18,13 @@ on:
|
||||
default: true
|
||||
required: false
|
||||
type: boolean
|
||||
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
build:
|
||||
name: Build SCInsta
|
||||
name: Build RyukGram
|
||||
runs-on: macos-latest
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
run: brew install ldid dpkg make
|
||||
|
||||
- name: Set PATH environment variable
|
||||
run: echo "$(brew --prefix make)/libexec/gnubin" >> $GITHUB_PATH
|
||||
run: echo "$(brew --prefix make)/libexec/gnubin" >> $GITHUB_PATH
|
||||
|
||||
- name: Setup Theos
|
||||
uses: actions/checkout@v4
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
ref: master
|
||||
path: ${{ github.workspace }}/theos
|
||||
submodules: recursive
|
||||
|
||||
|
||||
- name: SDK Caching
|
||||
id: SDK
|
||||
uses: actions/cache@v4
|
||||
@@ -74,28 +74,24 @@ jobs:
|
||||
- name: Prepare Instagram IPA
|
||||
run: |
|
||||
cd main
|
||||
|
||||
mkdir -p packages
|
||||
wget "$Instagram_URL" --no-verbose -O packages/com.burbn.instagram.ipa
|
||||
|
||||
ls -la packages
|
||||
|
||||
env:
|
||||
THEOS: ${{ github.workspace }}/theos
|
||||
Instagram_URL: ${{ inputs.decrypted_instagram_url }}
|
||||
|
||||
- name: Get SCInsta Version
|
||||
id: scinsta_version
|
||||
- name: Get Version
|
||||
id: version
|
||||
run: |
|
||||
SCINSTA_VERSION=$(awk '/Version:/ {print $2}' main/control)
|
||||
VERSION=$(awk '/Version:/ {print $2}' main/control)
|
||||
echo "VERSION=${VERSION}" >> "$GITHUB_ENV"
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
echo "SCINSTA_VERSION=${SCINSTA_VERSION}" >> "$GITHUB_ENV"
|
||||
echo "version=${SCINSTA_VERSION}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Build SCInsta tweak for sideloading (as IPA)
|
||||
- name: Build RyukGram tweak for sideloading (as IPA)
|
||||
run: |
|
||||
pip install --force-reinstall https://github.com/asdfzxcvbn/pyzule-rw/archive/main.zip
|
||||
|
||||
|
||||
cd main
|
||||
curl -Lo ipapatch https://github.com/asdfzxcvbn/ipapatch/releases/download/v2.1.3/ipapatch.macos-arm64
|
||||
chmod +x ipapatch
|
||||
@@ -104,33 +100,32 @@ jobs:
|
||||
|
||||
./build.sh sideload
|
||||
ls -la packages
|
||||
|
||||
env:
|
||||
THEOS: ${{ github.workspace }}/theos
|
||||
|
||||
- name: Rename IPA to include version info
|
||||
run: |
|
||||
cd main/packages
|
||||
mv "$(ls -t | head -n1)" "SCInsta_sideloaded_v${SCINSTA_VERSION}.ipa"
|
||||
mv "$(ls -t | head -n1)" "RyukGram_sideloaded_v${VERSION}.ipa"
|
||||
|
||||
- name: Pass package name to upload action
|
||||
id: package_name
|
||||
run: |
|
||||
echo "package=$(ls -t main/packages | head -n1)" >> "$GITHUB_OUTPUT"
|
||||
echo "package=$(ls -t main/packages | head -n1)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload Artifact
|
||||
if: ${{ inputs.upload_artifact }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: SCInsta_sideloaded_v${{ steps.scinsta_version.outputs.version }}
|
||||
name: RyukGram_sideloaded_v${{ steps.version.outputs.version }}
|
||||
path: ${{ github.workspace }}/main/packages/${{ steps.package_name.outputs.package }}
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v2.0.6
|
||||
with:
|
||||
name: SCInsta_sideloaded_v${{ steps.scinsta_version.outputs.version }}
|
||||
files: ${{ github.workspace }}/main/packages/SCInsta_sideloaded_v*.ipa
|
||||
name: RyukGram_sideloaded_v${{ steps.version.outputs.version }}
|
||||
files: ${{ github.workspace }}/main/packages/RyukGram_sideloaded_v*.ipa
|
||||
draft: true
|
||||
|
||||
- name: Output Release URL
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Build SCInsta tweak for Rootless
|
||||
name: Build RyukGram tweak for Rootless
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -6,13 +6,13 @@ on:
|
||||
- 'main'
|
||||
- 'dev'
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
build:
|
||||
name: Build SCInsta Rootless
|
||||
name: Build RyukGram Rootless
|
||||
runs-on: macos-latest
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
run: brew install ldid dpkg make
|
||||
|
||||
- name: Set PATH environment variable
|
||||
run: echo "$(brew --prefix make)/libexec/gnubin" >> $GITHUB_PATH
|
||||
run: echo "$(brew --prefix make)/libexec/gnubin" >> $GITHUB_PATH
|
||||
|
||||
- name: Setup Theos
|
||||
uses: actions/checkout@v4
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
ref: master
|
||||
path: ${{ github.workspace }}/theos
|
||||
submodules: recursive
|
||||
|
||||
|
||||
- name: SDK Caching
|
||||
id: SDK
|
||||
uses: actions/cache@v4
|
||||
@@ -58,29 +58,26 @@ jobs:
|
||||
env:
|
||||
THEOS: ${{ github.workspace }}/theos
|
||||
|
||||
- name: Get SCInsta Version
|
||||
id: scinsta_version
|
||||
- name: Get Version
|
||||
id: version
|
||||
run: |
|
||||
SCINSTA_VERSION=$(awk '/Version:/ {print $2}' main/control)
|
||||
VERSION=$(awk '/Version:/ {print $2}' main/control)
|
||||
echo "VERSION=${VERSION}" >> "$GITHUB_ENV"
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
echo "SCINSTA_VERSION=${SCINSTA_VERSION}" >> "$GITHUB_ENV"
|
||||
echo "version=${SCINSTA_VERSION}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Build SCInsta tweak for Rootless
|
||||
run: |
|
||||
- name: Build RyukGram tweak for Rootless
|
||||
run: |
|
||||
cd main
|
||||
ls -la
|
||||
|
||||
./build.sh rootless
|
||||
ls -la packages
|
||||
|
||||
env:
|
||||
THEOS: ${{ github.workspace }}/theos
|
||||
|
||||
- name: Rename deb to include version info
|
||||
run: |
|
||||
cd main/packages
|
||||
mv "$(ls -t | head -n1)" "com.socuul.scinsta_${SCINSTA_VERSION}+debug-rootless.deb"
|
||||
mv "$(ls -t | head -n1)" "com.faroukbmiled.ryukgram_${VERSION}+debug-rootless.deb"
|
||||
|
||||
- name: Pass package name to upload action
|
||||
id: package_name
|
||||
|
||||
@@ -4,9 +4,9 @@ ARCHS = arm64
|
||||
|
||||
include $(THEOS)/makefiles/common.mk
|
||||
|
||||
TWEAK_NAME = SCInsta
|
||||
TWEAK_NAME = RyukGram
|
||||
|
||||
$(TWEAK_NAME)_FILES = $(shell find src -type f \( -iname \*.x -o -iname \*.xm -o -iname \*.m \)) $(wildcard modules/JGProgressHUD/*.m)
|
||||
$(TWEAK_NAME)_FILES = $(shell find src -type f \( -iname \*.x -o -iname \*.xm -o -iname \*.m \)) $(wildcard modules/JGProgressHUD/*.m) modules/fishhook/fishhook.c
|
||||
$(TWEAK_NAME)_FRAMEWORKS = UIKit Foundation CoreGraphics Photos CoreServices SystemConfiguration SafariServices Security QuartzCore
|
||||
$(TWEAK_NAME)_PRIVATE_FRAMEWORKS = Preferences
|
||||
$(TWEAK_NAME)_CFLAGS = -fobjc-arc -Wno-unsupported-availability-guard -Wno-unused-value -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-unused-function -Wno-incompatible-pointer-types
|
||||
|
||||
@@ -1,31 +1,23 @@
|
||||
# SCInsta
|
||||
|
||||
A feature-rich iOS tweak for Instagram.
|
||||
|
||||
**Version:** v1.1.4
|
||||
**Tested on:** Instagram 423.1.0
|
||||
|
||||
A more actively maintained fork of Scinsta, featuring enhancements, fixes, and improved stability.
|
||||
# RyukGram
|
||||
A feature-rich iOS tweak for Instagram, forked from [SCInsta](https://github.com/SoCuul/SCInsta) with additional features and fixes.\
|
||||
`Version v1.1.4` | `Tested on Instagram 423.1.0`
|
||||
|
||||
---
|
||||
|
||||
> [!NOTE]
|
||||
> ⚙️ To modify SCInsta's settings, check out [this section below](https://github.com/SoCuul/SCInsta#Opening-Tweak-Settings) for help\
|
||||
> ❓ If you have any questions or need help with the tweak, visit the [Discussions](https://github.com/SoCuul/SCInsta/discussions) tab
|
||||
>
|
||||
> ✨ If you have a feature request, [click here](https://github.com/SoCuul/SCInsta/issues/new/choose)\
|
||||
> 🐛 If you have a bug report, [click here](https://github.com/SoCuul/SCInsta/issues/new/choose)
|
||||
>
|
||||
> To modify RyukGram's settings, check out [this section below](#opening-tweak-settings) for help
|
||||
|
||||
---
|
||||
|
||||
# Installation
|
||||
>[!IMPORTANT]
|
||||
> Which type of device are you planning on installing this tweak on?
|
||||
> - Jailbroken/TrollStore device -> [Download pre-built tweak](https://github.com/SoCuul/SCInsta/releases/latest)
|
||||
> - Standard iOS device -> [Visit the wiki to create an IPA file](https://github.com/SoCuul/SCInsta/wiki/Building-IPA)
|
||||
> - Jailbroken/TrollStore device -> [Download pre-built tweak](https://github.com/faroukbmiled/RyukGram/releases/latest)
|
||||
> - Standard iOS device -> Sideload the dylib using Feather or similar
|
||||
|
||||
# Features
|
||||
> Features marked with **\*** are new or improved in RyukGram
|
||||
|
||||
### General
|
||||
- Hide ads
|
||||
- Hide Meta AI
|
||||
@@ -57,18 +49,30 @@ A more actively maintained fork of Scinsta, featuring enhancements, fixes, and i
|
||||
### Reels
|
||||
- Modify tap controls
|
||||
- Always show progress scrubber
|
||||
- Disable auto-unmuting reels
|
||||
- Disable auto-unmuting reels (properly blocks mute switch, volume buttons, and announcer broadcasts) **\***
|
||||
- Confirm reel refresh
|
||||
- Hide reels header
|
||||
- Hide reels blend button
|
||||
- Disable scrolling reels
|
||||
- Prevent doom scrolling (limit maximum viewable reels)
|
||||
- Enhanced Pause/Play mode (when Pause/Play tap control is set): **\***
|
||||
- Mute toggle auto-hidden, only play/pause icon visible
|
||||
- Audio forced on in reels tab
|
||||
- Play indicator properly hidden when video plays (fixes IG bug after hold/zoom)
|
||||
- Playback toggle synced with overlay during hold/zoom
|
||||
- Works across IG A/B test variants
|
||||
|
||||
### Saving
|
||||
- Download feed posts
|
||||
- Download feed posts (photo + video)
|
||||
- Download reels
|
||||
- Download stories
|
||||
- Save profile picture
|
||||
- Download buttons on media — tap a button directly on feed posts, reels sidebar, and story overlay **\***
|
||||
- Download method — choose between download button or long-press gesture **\***
|
||||
- Save action — choose between share sheet or save directly to Photos **\***
|
||||
- Download confirmation — optional confirmation dialog before downloading **\***
|
||||
- Non-blocking download HUD — pill-style progress at the top, tap to cancel **\***
|
||||
- Debug fallback — if IG updates break downloads, shows diagnostic info instead of crashing **\***
|
||||
- *Customize finger count for long-press*
|
||||
- *Customize hold time for long-press*
|
||||
|
||||
@@ -79,7 +83,11 @@ A more actively maintained fork of Scinsta, featuring enhancements, fixes, and i
|
||||
- Unlimited replay of direct stories
|
||||
- Disable view-once limitations
|
||||
- Disable screenshot detection
|
||||
- Disable story seen receipt
|
||||
- Disable story seen receipt (blocks network upload, toggleable at runtime without restart) **\***
|
||||
- Keep stories visually unseen — keeps the colorful ring in the tray after viewing **\***
|
||||
- Manual mark story as seen — button on story overlay to selectively mark stories as seen **\***
|
||||
- Stop story auto-advance — stories won't auto-skip when the timer ends **\***
|
||||
- Story download button — download directly from the story overlay **\***
|
||||
- Disable instants creation
|
||||
|
||||
### Navigation
|
||||
@@ -126,9 +134,9 @@ A more actively maintained fork of Scinsta, featuring enhancements, fixes, and i
|
||||
1. Install iOS 16.2 frameworks for theos
|
||||
1. [Click to download iOS SDKs](https://github.com/xybp888/iOS-SDKs/archive/refs/heads/master.zip)
|
||||
2. Unzip, then copy the `iPhoneOS16.2.sdk` folder into `~/theos/sdks`
|
||||
2. Clone SCInsta repo from GitHub: `git clone --recurse-submodules https://github.com/SoCuul/SCInsta`
|
||||
2. Clone repo: `git clone --recurse-submodules https://github.com/faroukbmiled/RyukGram`
|
||||
3. **For sideloading**: Download a decrypted Instagram IPA from a trusted source, making sure to rename it to `com.burbn.instagram.ipa`.
|
||||
Then create a folder called `packages` inside of the `SCInsta` folder, and move the Instagram IPA file into it.
|
||||
Then create a folder called `packages` inside of the project folder, and move the Instagram IPA file into it.
|
||||
|
||||
### Run build script
|
||||
```sh
|
||||
@@ -136,18 +144,7 @@ $ chmod +x build.sh
|
||||
$ ./build.sh <sideload/rootless/rootful>
|
||||
```
|
||||
|
||||
# Contributing
|
||||
Contributions to this tweak are greatly appreciated. Feel free to create a pull request if you would like to contribute.
|
||||
|
||||
If you do not have the technical knowledge to contribute to the codebase, improvements to the documentation are always welcome!
|
||||
|
||||
# Support the project
|
||||
SCInsta takes a lot of time to develop, as the Instagram app is ever-changing and difficult to keep up with. Additionally, I'm still a student which doesn't leave me much time to work on this tweak.
|
||||
|
||||
If you'd like to support my work, you can donate to my [ko-fi page](https://ko-fi.com/socuul)!\
|
||||
There's many other ways to support this project however, by simply sharing a link to this tweak with others who would like it!
|
||||
|
||||
Seeing people use this tweak is what keeps me motivated to keep working on it ❤️
|
||||
|
||||
# Credits
|
||||
- Huge thanks to [@BandarHL](https://github.com/BandarHL) for creating the original BHInstagram project, which SCInsta is based upon.
|
||||
- [SCInsta](https://github.com/SoCuul/SCInsta) by [@SoCuul](https://github.com/SoCuul) — original tweak this fork is based on
|
||||
- [@BandarHL](https://github.com/BandarHL) — creator of the original BHInstagram project
|
||||
- [@faroukbmiled](https://github.com/faroukbmiled) — RyukGram modifications and additional features
|
||||
|
||||
@@ -55,7 +55,7 @@ then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e '\033[1m\033[32mBuilding SCInsta tweak for sideloading (as IPA)\033[0m'
|
||||
echo -e '\033[1m\033[32mBuilding RyukGram tweak for sideloading (as IPA)\033[0m'
|
||||
|
||||
make $MAKEARGS
|
||||
|
||||
@@ -65,36 +65,35 @@ then
|
||||
exit
|
||||
fi
|
||||
|
||||
SCINSTAPATH=".theos/obj/debug/SCInsta.dylib"
|
||||
TWEAKPATH=".theos/obj/debug/RyukGram.dylib"
|
||||
if [ "$2" == "--devquick" ];
|
||||
then
|
||||
# Exclude SCInsta.dylib from ipa for livecontainer quick builds
|
||||
SCINSTAPATH=""
|
||||
TWEAKPATH=""
|
||||
fi
|
||||
|
||||
# Create IPA File
|
||||
echo -e '\033[1m\033[32mCreating the IPA file...\033[0m'
|
||||
rm -f packages/SCInsta-sideloaded.ipa
|
||||
cyan -i "packages/${ipaFile}" -o packages/SCInsta-sideloaded.ipa -f $SCINSTAPATH $FLEXPATH -c $COMPRESSION -m 15.0 -du
|
||||
|
||||
# Patch IPA for sideloading
|
||||
ipapatch --input "packages/SCInsta-sideloaded.ipa" --inplace --noconfirm
|
||||
rm -f packages/RyukGram-sideloaded.ipa
|
||||
cyan -i "packages/${ipaFile}" -o packages/RyukGram-sideloaded.ipa -f $TWEAKPATH $FLEXPATH -c $COMPRESSION -m 15.0 -du
|
||||
|
||||
echo -e "\033[1m\033[32mDone, we hope you enjoy SCInsta!\033[0m\n\nYou can find the ipa file at: $(pwd)/packages"
|
||||
# Patch IPA for sideloading
|
||||
ipapatch --input "packages/RyukGram-sideloaded.ipa" --inplace --noconfirm
|
||||
|
||||
echo -e "\033[1m\033[32mDone, enjoy RyukGram!\033[0m\n\nYou can find the ipa file at: $(pwd)/packages"
|
||||
|
||||
elif [ "$1" == "rootless" ];
|
||||
then
|
||||
|
||||
|
||||
# Clean build artifacts
|
||||
make clean
|
||||
rm -rf .theos
|
||||
|
||||
echo -e '\033[1m\033[32mBuilding SCInsta tweak for rootless\033[0m'
|
||||
echo -e '\033[1m\033[32mBuilding RyukGram tweak for rootless\033[0m'
|
||||
|
||||
export THEOS_PACKAGE_SCHEME=rootless
|
||||
make package
|
||||
|
||||
echo -e "\033[1m\033[32mDone, we hope you enjoy SCInsta!\033[0m\n\nYou can find the deb file at: $(pwd)/packages"
|
||||
echo -e "\033[1m\033[32mDone, enjoy RyukGram!\033[0m\n\nYou can find the deb file at: $(pwd)/packages"
|
||||
|
||||
elif [ "$1" == "rootful" ];
|
||||
then
|
||||
@@ -103,18 +102,18 @@ then
|
||||
make clean
|
||||
rm -rf .theos
|
||||
|
||||
echo -e '\033[1m\033[32mBuilding SCInsta tweak for rootful\033[0m'
|
||||
echo -e '\033[1m\033[32mBuilding RyukGram tweak for rootful\033[0m'
|
||||
|
||||
unset THEOS_PACKAGE_SCHEME
|
||||
make package
|
||||
|
||||
echo -e "\033[1m\033[32mDone, we hope you enjoy SCInsta!\033[0m\n\nYou can find the deb file at: $(pwd)/packages"
|
||||
echo -e "\033[1m\033[32mDone, enjoy RyukGram!\033[0m\n\nYou can find the deb file at: $(pwd)/packages"
|
||||
|
||||
else
|
||||
echo '+--------------------+'
|
||||
echo '|SCInsta Build Script|'
|
||||
echo '+--------------------+'
|
||||
echo '+----------------------+'
|
||||
echo '|RyukGram Build Script |'
|
||||
echo '+----------------------+'
|
||||
echo
|
||||
echo 'Usage: ./build.sh <sideload/rootless/rootful>'
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
Package: com.socuul.scinsta
|
||||
Name: SCInsta
|
||||
Package: com.faroukbmiled.ryukgram
|
||||
Name: RyukGram
|
||||
Version: 1.1.4
|
||||
Architecture: iphoneos-arm
|
||||
Description: A feature-rich tweak for Instagram on iOS!
|
||||
Homepage: https://github.com/SoCuul/SCInsta
|
||||
Maintainer: SoCuul
|
||||
Author: SoCuul
|
||||
Description: A feature-rich tweak for Instagram on iOS, based on SCInsta
|
||||
Homepage: https://github.com/faroukbmiled/RyukGram
|
||||
Maintainer: Ryuk
|
||||
Author: Ryuk
|
||||
Section: Tweaks
|
||||
Depends: mobilesubstrate
|
||||
Depends: mobilesubstrate
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
// Copyright (c) 2013, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name Facebook nor the names of its contributors may be used to
|
||||
// endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "fishhook.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/vm_map.h>
|
||||
#include <mach/vm_region.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach-o/nlist.h>
|
||||
|
||||
#ifdef __LP64__
|
||||
typedef struct mach_header_64 mach_header_t;
|
||||
typedef struct segment_command_64 segment_command_t;
|
||||
typedef struct section_64 section_t;
|
||||
typedef struct nlist_64 nlist_t;
|
||||
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64
|
||||
#else
|
||||
typedef struct mach_header mach_header_t;
|
||||
typedef struct segment_command segment_command_t;
|
||||
typedef struct section section_t;
|
||||
typedef struct nlist nlist_t;
|
||||
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT
|
||||
#endif
|
||||
|
||||
#ifndef SEG_DATA_CONST
|
||||
#define SEG_DATA_CONST "__DATA_CONST"
|
||||
#endif
|
||||
|
||||
struct rebindings_entry {
|
||||
struct rebinding *rebindings;
|
||||
size_t rebindings_nel;
|
||||
struct rebindings_entry *next;
|
||||
};
|
||||
|
||||
static struct rebindings_entry *_rebindings_head;
|
||||
|
||||
static int prepend_rebindings(struct rebindings_entry **rebindings_head,
|
||||
struct rebinding rebindings[],
|
||||
size_t nel) {
|
||||
struct rebindings_entry *new_entry = (struct rebindings_entry *) malloc(sizeof(struct rebindings_entry));
|
||||
if (!new_entry) {
|
||||
return -1;
|
||||
}
|
||||
new_entry->rebindings = (struct rebinding *) malloc(sizeof(struct rebinding) * nel);
|
||||
if (!new_entry->rebindings) {
|
||||
free(new_entry);
|
||||
return -1;
|
||||
}
|
||||
memcpy(new_entry->rebindings, rebindings, sizeof(struct rebinding) * nel);
|
||||
new_entry->rebindings_nel = nel;
|
||||
new_entry->next = *rebindings_head;
|
||||
*rebindings_head = new_entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int get_protection(void *addr, vm_prot_t *prot, vm_prot_t *max_prot) {
|
||||
mach_port_t task = mach_task_self();
|
||||
vm_size_t size = 0;
|
||||
vm_address_t address = (vm_address_t)addr;
|
||||
memory_object_name_t object;
|
||||
#ifdef __LP64__
|
||||
mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64;
|
||||
vm_region_basic_info_data_64_t info;
|
||||
kern_return_t info_ret = vm_region_64(
|
||||
task, &address, &size, VM_REGION_BASIC_INFO_64, (vm_region_info_64_t)&info, &count, &object);
|
||||
#else
|
||||
mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT;
|
||||
vm_region_basic_info_data_t info;
|
||||
kern_return_t info_ret = vm_region(task, &address, &size, VM_REGION_BASIC_INFO, (vm_region_info_t)&info, &count, &object);
|
||||
#endif
|
||||
if (info_ret == KERN_SUCCESS) {
|
||||
if (prot != NULL)
|
||||
*prot = info.protection;
|
||||
|
||||
if (max_prot != NULL)
|
||||
*max_prot = info.max_protection;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void perform_rebinding_with_section(struct rebindings_entry *rebindings,
|
||||
section_t *section,
|
||||
intptr_t slide,
|
||||
nlist_t *symtab,
|
||||
char *strtab,
|
||||
uint32_t *indirect_symtab) {
|
||||
uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1;
|
||||
void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr);
|
||||
|
||||
for (uint i = 0; i < section->size / sizeof(void *); i++) {
|
||||
uint32_t symtab_index = indirect_symbol_indices[i];
|
||||
if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL ||
|
||||
symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) {
|
||||
continue;
|
||||
}
|
||||
uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx;
|
||||
char *symbol_name = strtab + strtab_offset;
|
||||
bool symbol_name_longer_than_1 = symbol_name[0] && symbol_name[1];
|
||||
struct rebindings_entry *cur = rebindings;
|
||||
while (cur) {
|
||||
for (uint j = 0; j < cur->rebindings_nel; j++) {
|
||||
if (symbol_name_longer_than_1 && strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) {
|
||||
kern_return_t err;
|
||||
|
||||
if (cur->rebindings[j].replaced != NULL && indirect_symbol_bindings[i] != cur->rebindings[j].replacement)
|
||||
*(cur->rebindings[j].replaced) = indirect_symbol_bindings[i];
|
||||
|
||||
/**
|
||||
* 1. Moved the vm protection modifying codes to here to reduce the
|
||||
* changing scope.
|
||||
* 2. Adding VM_PROT_WRITE mode unconditionally because vm_region
|
||||
* API on some iOS/Mac reports mismatch vm protection attributes.
|
||||
* -- Lianfu Hao Jun 16th, 2021
|
||||
**/
|
||||
err = vm_protect (mach_task_self (), (uintptr_t)indirect_symbol_bindings, section->size, 0, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY);
|
||||
if (err == KERN_SUCCESS) {
|
||||
/**
|
||||
* Once we failed to change the vm protection, we
|
||||
* MUST NOT continue the following write actions!
|
||||
* iOS 15 has corrected the const segments prot.
|
||||
* -- Lionfore Hao Jun 11th, 2021
|
||||
**/
|
||||
indirect_symbol_bindings[i] = cur->rebindings[j].replacement;
|
||||
}
|
||||
goto symbol_loop;
|
||||
}
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
symbol_loop:;
|
||||
}
|
||||
}
|
||||
|
||||
static void rebind_symbols_for_image(struct rebindings_entry *rebindings,
|
||||
const struct mach_header *header,
|
||||
intptr_t slide) {
|
||||
Dl_info info;
|
||||
if (dladdr(header, &info) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
segment_command_t *cur_seg_cmd;
|
||||
segment_command_t *linkedit_segment = NULL;
|
||||
struct symtab_command* symtab_cmd = NULL;
|
||||
struct dysymtab_command* dysymtab_cmd = NULL;
|
||||
|
||||
uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t);
|
||||
for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {
|
||||
cur_seg_cmd = (segment_command_t *)cur;
|
||||
if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
|
||||
if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) {
|
||||
linkedit_segment = cur_seg_cmd;
|
||||
}
|
||||
} else if (cur_seg_cmd->cmd == LC_SYMTAB) {
|
||||
symtab_cmd = (struct symtab_command*)cur_seg_cmd;
|
||||
} else if (cur_seg_cmd->cmd == LC_DYSYMTAB) {
|
||||
dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd;
|
||||
}
|
||||
}
|
||||
|
||||
if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment ||
|
||||
!dysymtab_cmd->nindirectsyms) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find base symbol/string table addresses
|
||||
uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
|
||||
nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff);
|
||||
char *strtab = (char *)(linkedit_base + symtab_cmd->stroff);
|
||||
|
||||
// Get indirect symbol table (array of uint32_t indices into symbol table)
|
||||
uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff);
|
||||
|
||||
cur = (uintptr_t)header + sizeof(mach_header_t);
|
||||
for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {
|
||||
cur_seg_cmd = (segment_command_t *)cur;
|
||||
if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
|
||||
if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 &&
|
||||
strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0) {
|
||||
continue;
|
||||
}
|
||||
for (uint j = 0; j < cur_seg_cmd->nsects; j++) {
|
||||
section_t *sect =
|
||||
(section_t *)(cur + sizeof(segment_command_t)) + j;
|
||||
if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) {
|
||||
perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);
|
||||
}
|
||||
if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) {
|
||||
perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _rebind_symbols_for_image(const struct mach_header *header,
|
||||
intptr_t slide) {
|
||||
rebind_symbols_for_image(_rebindings_head, header, slide);
|
||||
}
|
||||
|
||||
int rebind_symbols_image(void *header,
|
||||
intptr_t slide,
|
||||
struct rebinding rebindings[],
|
||||
size_t rebindings_nel) {
|
||||
struct rebindings_entry *rebindings_head = NULL;
|
||||
int retval = prepend_rebindings(&rebindings_head, rebindings, rebindings_nel);
|
||||
rebind_symbols_for_image(rebindings_head, (const struct mach_header *) header, slide);
|
||||
if (rebindings_head) {
|
||||
free(rebindings_head->rebindings);
|
||||
}
|
||||
free(rebindings_head);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) {
|
||||
int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel);
|
||||
if (retval < 0) {
|
||||
return retval;
|
||||
}
|
||||
// If this was the first call, register callback for image additions (which is also invoked for
|
||||
// existing images, otherwise, just run on existing images
|
||||
if (!_rebindings_head->next) {
|
||||
_dyld_register_func_for_add_image(_rebind_symbols_for_image);
|
||||
} else {
|
||||
uint32_t c = _dyld_image_count();
|
||||
for (uint32_t i = 0; i < c; i++) {
|
||||
_rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i));
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2013, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name Facebook nor the names of its contributors may be used to
|
||||
// endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef fishhook_h
|
||||
#define fishhook_h
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if !defined(FISHHOOK_EXPORT)
|
||||
#define FISHHOOK_VISIBILITY __attribute__((visibility("hidden")))
|
||||
#else
|
||||
#define FISHHOOK_VISIBILITY __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif //__cplusplus
|
||||
|
||||
/*
|
||||
* A structure representing a particular intended rebinding from a symbol
|
||||
* name to its replacement
|
||||
*/
|
||||
struct rebinding {
|
||||
const char *name;
|
||||
void *replacement;
|
||||
void **replaced;
|
||||
};
|
||||
|
||||
/*
|
||||
* For each rebinding in rebindings, rebinds references to external, indirect
|
||||
* symbols with the specified name to instead point at replacement for each
|
||||
* image in the calling process as well as for all future images that are loaded
|
||||
* by the process. If rebind_functions is called more than once, the symbols to
|
||||
* rebind are added to the existing list of rebindings, and if a given symbol
|
||||
* is rebound more than once, the later rebinding will take precedence.
|
||||
*/
|
||||
FISHHOOK_VISIBILITY
|
||||
int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel);
|
||||
|
||||
/*
|
||||
* Rebinds as above, but only in the specified image. The header should point
|
||||
* to the mach-o header, the slide should be the slide offset. Others as above.
|
||||
*/
|
||||
FISHHOOK_VISIBILITY
|
||||
int rebind_symbols_image(void *header,
|
||||
intptr_t slide,
|
||||
struct rebinding rebindings[],
|
||||
size_t rebindings_nel);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
|
||||
#endif //fishhook_h
|
||||
|
||||
@@ -162,7 +162,7 @@ static void sciShowDebugIvarDump(UIView *cell) {
|
||||
NSLog(@"[SCInsta] Debug: %@", debug);
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"SCInsta Debug"
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"RyukGram Debug"
|
||||
message:debug
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Copy & Close" style:UIAlertActionStyleDefault handler:^(UIAlertAction *a) {
|
||||
|
||||
@@ -70,8 +70,8 @@ static char rowStaticRef[] = "row";
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
if (![[[NSUserDefaults standardUserDefaults] objectForKey:@"SCInstaFirstRun"] isEqualToString:SCIVersionString]) {
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"SCInsta Settings Info"
|
||||
message:@"In the future: Hold down on the three lines at the top right of your profile page, to re-open SCInsta settings."
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"RyukGram Settings Info"
|
||||
message:@"In the future: Hold down on the three lines at the top right of your profile page, to re-open RyukGram settings."
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"I understand!"
|
||||
|
||||
@@ -233,10 +233,10 @@
|
||||
]
|
||||
},
|
||||
@{
|
||||
@"header": @"SCInsta",
|
||||
@"header": @"RyukGram",
|
||||
@"rows": @[
|
||||
[SCISetting switchCellWithTitle:@"Enable tweak settings quick-access" subtitle:@"Allows you to hold on the home tab to open the SCInsta settings" defaultsKey:@"settings_shortcut" requiresRestart:YES],
|
||||
[SCISetting switchCellWithTitle:@"Show tweak settings on app launch" subtitle:@"Automatically opens the SCInsta settings when the app launches" defaultsKey:@"tweak_settings_app_launch"],
|
||||
[SCISetting switchCellWithTitle:@"Enable tweak settings quick-access" subtitle:@"Allows you to hold on the home tab to open the RyukGram settings" defaultsKey:@"settings_shortcut" requiresRestart:YES],
|
||||
[SCISetting switchCellWithTitle:@"Show tweak settings on app launch" subtitle:@"Automatically opens the RyukGram settings when the app launches" defaultsKey:@"tweak_settings_app_launch"],
|
||||
[SCISetting buttonCellWithTitle:@"Reset onboarding completion state"
|
||||
subtitle:@""
|
||||
icon:nil
|
||||
@@ -283,11 +283,11 @@
|
||||
@{
|
||||
@"header": @"Credits",
|
||||
@"rows": @[
|
||||
[SCISetting linkCellWithTitle:@"Developer" subtitle:@"SoCuul" imageUrl:@"https://i.imgur.com/c9CbytZ.png" url:@"https://socuul.dev"],
|
||||
[SCISetting linkCellWithTitle:@"Original Developer" subtitle:@"SoCuul (SCInsta)" imageUrl:@"https://i.imgur.com/c9CbytZ.png" url:@"https://github.com/SoCuul/SCInsta"],
|
||||
[SCISetting linkCellWithTitle:@"Modded by" subtitle:@"Ryuk" imageUrl:@"https://github.com/faroukbmiled.png" url:@"https://github.com/faroukbmiled"],
|
||||
[SCISetting linkCellWithTitle:@"View Repo" subtitle:@"View the tweak's source code on GitHub" imageUrl:@"https://i.imgur.com/BBUNzeP.png" url:@"https://github.com/SoCuul/SCInsta"]
|
||||
[SCISetting linkCellWithTitle:@"View Repo" subtitle:@"View the source code on GitHub" imageUrl:@"https://i.imgur.com/BBUNzeP.png" url:@"https://github.com/faroukbmiled/RyukGram"]
|
||||
],
|
||||
@"footer": [NSString stringWithFormat:@"SCInsta %@\n\nInstagram v%@\n\nModded by Ryuk", SCIVersionString, [SCIUtils IGVersionString]]
|
||||
@"footer": [NSString stringWithFormat:@"RyukGram %@\n\nInstagram v%@\n\nBased on SCInsta by SoCuul", SCIVersionString, [SCIUtils IGVersionString]]
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -300,7 +300,7 @@
|
||||
///
|
||||
|
||||
+ (NSString *)title {
|
||||
return @"SCInsta Settings";
|
||||
return @"RyukGram Settings";
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
// Sideload compatibility patch for Instagram.
|
||||
// Fixes keychain, app groups, CloudKit, and container access when sideloaded.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Security/Security.h>
|
||||
#import <objc/runtime.h>
|
||||
#import <objc/message.h>
|
||||
#import "../../modules/fishhook/fishhook.h"
|
||||
|
||||
static NSString *bundleId = nil;
|
||||
static NSString *accessGroupId = nil;
|
||||
|
||||
static OSStatus (*orig_SecItemAdd)(CFDictionaryRef, CFTypeRef *) = NULL;
|
||||
static OSStatus (*orig_SecItemCopyMatching)(CFDictionaryRef, CFTypeRef *) = NULL;
|
||||
static OSStatus (*orig_SecItemUpdate)(CFDictionaryRef, CFDictionaryRef) = NULL;
|
||||
static OSStatus (*orig_SecItemDelete)(CFDictionaryRef) = NULL;
|
||||
|
||||
static IMP orig_CKEntitlements_initWithEntitlementsDict __attribute__((unused)) = NULL;
|
||||
static IMP orig_CKContainer_setupWithContainerID __attribute__((unused)) = NULL;
|
||||
static IMP orig_CKContainer_initWithContainerIdentifier __attribute__((unused)) = NULL;
|
||||
static IMP orig_NSFileManager_containerURL __attribute__((unused)) = NULL;
|
||||
|
||||
// -- app group path --
|
||||
|
||||
static NSString *_appGroupPath = nil;
|
||||
static dispatch_once_t _appGroupOnce = 0;
|
||||
|
||||
static NSString *getAppGroupPathIfExists(void) {
|
||||
dispatch_once(&_appGroupOnce, ^{
|
||||
Class LSBundleProxy = objc_getClass("LSBundleProxy");
|
||||
if (!LSBundleProxy) return;
|
||||
|
||||
id proxy = ((id(*)(id, SEL))objc_msgSend)(
|
||||
(id)LSBundleProxy, sel_registerName("bundleProxyForCurrentProcess"));
|
||||
if (!proxy) return;
|
||||
|
||||
NSDictionary *ents = ((NSDictionary *(*)(id, SEL))objc_msgSend)(
|
||||
proxy, sel_registerName("entitlements"));
|
||||
if (!ents || ![ents isKindOfClass:[NSDictionary class]]) return;
|
||||
|
||||
NSArray *groups = ents[@"com.apple.security.application-groups"];
|
||||
if (!groups || groups.count == 0) return;
|
||||
|
||||
NSDictionary *urls = ((NSDictionary *(*)(id, SEL))objc_msgSend)(
|
||||
proxy, sel_registerName("groupContainerURLs"));
|
||||
if (!urls || ![urls isKindOfClass:[NSDictionary class]]) return;
|
||||
|
||||
NSURL *url = urls[groups.firstObject];
|
||||
if (url) _appGroupPath = [url path];
|
||||
});
|
||||
return _appGroupPath;
|
||||
}
|
||||
|
||||
static BOOL createDirectoryIfNotExists(NSString *path) {
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
if ([fm fileExistsAtPath:path]) return YES;
|
||||
return [fm createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
|
||||
// -- SecItem replacements: set the correct access group on every call --
|
||||
|
||||
static OSStatus replaced_SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result) {
|
||||
if (attributes && accessGroupId) {
|
||||
NSMutableDictionary *q = [(__bridge NSDictionary *)attributes mutableCopy];
|
||||
q[(__bridge id)kSecAttrAccessGroup] = accessGroupId;
|
||||
return orig_SecItemAdd((__bridge CFDictionaryRef)q, result);
|
||||
}
|
||||
return orig_SecItemAdd(attributes, result);
|
||||
}
|
||||
|
||||
static OSStatus replaced_SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result) {
|
||||
if (query && accessGroupId) {
|
||||
NSMutableDictionary *q = [(__bridge NSDictionary *)query mutableCopy];
|
||||
q[(__bridge id)kSecAttrAccessGroup] = accessGroupId;
|
||||
return orig_SecItemCopyMatching((__bridge CFDictionaryRef)q, result);
|
||||
}
|
||||
return orig_SecItemCopyMatching(query, result);
|
||||
}
|
||||
|
||||
static OSStatus replaced_SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attrs) {
|
||||
if (query && accessGroupId) {
|
||||
NSMutableDictionary *q = [(__bridge NSDictionary *)query mutableCopy];
|
||||
q[(__bridge id)kSecAttrAccessGroup] = accessGroupId;
|
||||
return orig_SecItemUpdate((__bridge CFDictionaryRef)q, attrs);
|
||||
}
|
||||
return orig_SecItemUpdate(query, attrs);
|
||||
}
|
||||
|
||||
static OSStatus replaced_SecItemDelete(CFDictionaryRef query) {
|
||||
if (query && accessGroupId) {
|
||||
NSMutableDictionary *q = [(__bridge NSDictionary *)query mutableCopy];
|
||||
q[(__bridge id)kSecAttrAccessGroup] = accessGroupId;
|
||||
return orig_SecItemDelete((__bridge CFDictionaryRef)q);
|
||||
}
|
||||
return orig_SecItemDelete(query);
|
||||
}
|
||||
|
||||
// -- CloudKit patches: strip iCloud entitlements, disable container init --
|
||||
|
||||
static id replaced_CKEntitlements_init(id self, SEL _cmd, NSDictionary *dict) {
|
||||
NSMutableDictionary *d = [dict mutableCopy];
|
||||
[d removeObjectForKey:@"com.apple.developer.icloud-container-environment"];
|
||||
[d removeObjectForKey:@"com.apple.developer.icloud-services"];
|
||||
return ((id(*)(id, SEL, NSDictionary *))orig_CKEntitlements_initWithEntitlementsDict)(self, _cmd, [d copy]);
|
||||
}
|
||||
|
||||
static id replaced_CKContainer_setup(id self, SEL _cmd, id containerID, id options) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
static id replaced_CKContainer_init(id self, SEL _cmd, id identifier) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// -- NSFileManager: redirect app group container to a local fallback --
|
||||
|
||||
static NSURL *replaced_containerURL(id self, SEL _cmd, NSString *groupId) {
|
||||
NSString *groupPath = getAppGroupPathIfExists();
|
||||
if (!groupPath) {
|
||||
NSString *docs = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
|
||||
NSString *fallback = [docs stringByAppendingPathComponent:groupId];
|
||||
createDirectoryIfNotExists(fallback);
|
||||
return [NSURL fileURLWithPath:fallback];
|
||||
}
|
||||
NSURL *url = [[NSURL fileURLWithPath:groupPath] URLByAppendingPathComponent:groupId];
|
||||
createDirectoryIfNotExists([url path]);
|
||||
return url;
|
||||
}
|
||||
|
||||
// -- swizzle helper: walks class hierarchy, handles inherited methods --
|
||||
|
||||
static void swizzleMethod(Class cls, SEL sel, IMP newIMP, IMP *outOrig) {
|
||||
if (!cls) return;
|
||||
Class cur = cls;
|
||||
while (cur) {
|
||||
unsigned int count = 0;
|
||||
Method *list = class_copyMethodList(cur, &count);
|
||||
for (unsigned int i = 0; i < count; i++) {
|
||||
if (method_getName(list[i]) == sel) {
|
||||
if (cur == cls) {
|
||||
*outOrig = method_setImplementation(list[i], newIMP);
|
||||
} else {
|
||||
*outOrig = method_getImplementation(list[i]);
|
||||
class_addMethod(cls, sel, newIMP, method_getTypeEncoding(list[i]));
|
||||
}
|
||||
free(list);
|
||||
return;
|
||||
}
|
||||
}
|
||||
free(list);
|
||||
cur = class_getSuperclass(cur);
|
||||
}
|
||||
}
|
||||
|
||||
// -- keychain bootstrap: discover the access group assigned to this app --
|
||||
|
||||
static void bootstrapKeychainAccessGroup(void) {
|
||||
NSDictionary *query = @{
|
||||
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
||||
(__bridge id)kSecAttrAccount: @"RyukGramSideloadPatch",
|
||||
(__bridge id)kSecAttrService: @"",
|
||||
(__bridge id)kSecReturnAttributes: @YES,
|
||||
};
|
||||
|
||||
CFTypeRef result = NULL;
|
||||
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
|
||||
if (status == errSecItemNotFound)
|
||||
status = SecItemAdd((__bridge CFDictionaryRef)query, &result);
|
||||
|
||||
if (status == errSecSuccess && result) {
|
||||
bundleId = [[NSBundle mainBundle] bundleIdentifier];
|
||||
NSDictionary *attrs = (__bridge NSDictionary *)result;
|
||||
NSString *group = attrs[(__bridge id)kSecAttrAccessGroup];
|
||||
if (group) accessGroupId = [group copy];
|
||||
CFRelease(result);
|
||||
}
|
||||
}
|
||||
|
||||
// -- init --
|
||||
|
||||
%ctor {
|
||||
@autoreleasepool {
|
||||
bootstrapKeychainAccessGroup();
|
||||
|
||||
// rebind SecItem functions so keychain calls use the right access group
|
||||
struct rebinding rebindings[] = {
|
||||
{"SecItemAdd", (void *)replaced_SecItemAdd, (void **)&orig_SecItemAdd},
|
||||
{"SecItemCopyMatching", (void *)replaced_SecItemCopyMatching, (void **)&orig_SecItemCopyMatching},
|
||||
{"SecItemUpdate", (void *)replaced_SecItemUpdate, (void **)&orig_SecItemUpdate},
|
||||
{"SecItemDelete", (void *)replaced_SecItemDelete, (void **)&orig_SecItemDelete},
|
||||
};
|
||||
rebind_symbols(rebindings, 4);
|
||||
|
||||
// patch NSFileManager for app group container fallback
|
||||
Class fm = objc_getClass("NSFileManager");
|
||||
if (fm) swizzleMethod(fm, sel_registerName("containerURLForSecurityApplicationGroupIdentifier:"),
|
||||
(IMP)replaced_containerURL, &orig_NSFileManager_containerURL);
|
||||
|
||||
// patch CloudKit to prevent crashes from missing entitlements
|
||||
Class ckEnt = objc_getClass("CKEntitlements");
|
||||
if (ckEnt) swizzleMethod(ckEnt, sel_registerName("initWithEntitlementsDict:"),
|
||||
(IMP)replaced_CKEntitlements_init, &orig_CKEntitlements_initWithEntitlementsDict);
|
||||
|
||||
Class ckCon = objc_getClass("CKContainer");
|
||||
if (ckCon) {
|
||||
swizzleMethod(ckCon, sel_registerName("_setupWithContainerID:options:"),
|
||||
(IMP)replaced_CKContainer_setup, &orig_CKContainer_setupWithContainerID);
|
||||
swizzleMethod(ckCon, sel_registerName("_initWithContainerIdentifier:"),
|
||||
(IMP)replaced_CKContainer_init, &orig_CKContainer_initWithContainerIdentifier);
|
||||
}
|
||||
|
||||
// NSUserDefaults _initWithSuiteName:container: intentionally not patched —
|
||||
// crashes on current IG versions. the NSFileManager patch covers the
|
||||
// group container redirect which is what actually matters.
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -73,7 +73,7 @@ BOOL dmVisualMsgsViewedButtonEnabled = false;
|
||||
NSLog(@"[SCInsta] First run, initializing");
|
||||
|
||||
// Display settings modal on screen
|
||||
NSLog(@"[SCInsta] Displaying SCInsta first-time settings modal");
|
||||
NSLog(@"[SCInsta] Displaying RyukGram first-time settings modal");
|
||||
[SCIUtils showSettingsVC:[self window]];
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user