- 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
+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]];
}
});