- 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:
faroukbmiled
2026-04-03 08:59:13 +01:00
parent 38d8c54e21
commit 4ebd4d2c0d
14 changed files with 656 additions and 112 deletions
+17 -22
View File
@@ -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
+13 -16
View File
@@ -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
+2 -2
View File
@@ -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
+32 -35
View File
@@ -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
View File
+18 -19
View File
@@ -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
+7 -7
View File
@@ -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
+264
View File
@@ -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;
}
+76
View File
@@ -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
+1 -1
View File
@@ -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) {
+2 -2
View File
@@ -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!"
+7 -7
View File
@@ -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";
}
+216
View File
@@ -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
View File
@@ -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]];
}
});