├── .gitignore ├── .gitmodules ├── README.md ├── Makefile └── src ├── Header.h ├── zxPluginsInject.mm ├── SecRebinds.xm ├── SideloadFix.xm └── Paths.mm /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .theos 3 | *.dylib 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "fishhook"] 2 | path = fishhook 3 | url = https://github.com/facebook/fishhook.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zxPluginsInject 2 | 3 | original source code by choco, just rewritten better 4 | 5 | compile with `make FINALPACKAGE=1 && mv .theos/obj/*.dylib .` -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET := iphone:clang:16.5:14.0 2 | ARCHS := arm64 3 | 4 | include $(THEOS)/makefiles/common.mk 5 | 6 | TWEAK_NAME := zxPluginsInject 7 | 8 | $(TWEAK_NAME)_FILES := $(shell find src -type f -name "*.*m") fishhook/fishhook.c 9 | $(TWEAK_NAME)_CFLAGS := -fobjc-arc -Os 10 | $(TWEAK_NAME)_LOGOS_DEFAULT_GENERATOR := internal 11 | 12 | include $(THEOS_MAKE_PATH)/tweak.mk 13 | 14 | -------------------------------------------------------------------------------- /src/Header.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | extern NSString *accessGroupId; 4 | extern NSString *bundleId; 5 | 6 | extern void rebindSecFuncs(); 7 | 8 | extern BOOL createDirectoryIfNotExists(NSString *path); 9 | extern NSURL *getAppGroupPathIfExists(); 10 | 11 | @interface LSBundleProxy: NSObject 12 | @property(nonatomic, assign, readonly) NSDictionary *entitlements; 13 | @property(nonatomic, assign, readonly) NSDictionary *groupContainerURLs; 14 | + (instancetype)bundleProxyForCurrentProcess; 15 | @end 16 | -------------------------------------------------------------------------------- /src/zxPluginsInject.mm: -------------------------------------------------------------------------------- 1 | #import "Header.h" 2 | 3 | NSString *accessGroupId; 4 | NSString *bundleId; 5 | 6 | static void setRequiredIDs() { 7 | NSDictionary *query = @{ 8 | (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, 9 | (__bridge NSString *)kSecAttrAccount: @"zxPluginsInjectGenericEntry", 10 | (__bridge NSString *)kSecAttrService: @"", 11 | (__bridge id)kSecReturnAttributes: (id)kCFBooleanTrue 12 | }; 13 | 14 | CFDictionaryRef result = nil; 15 | OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result); 16 | if (status == errSecItemNotFound) { 17 | status = SecItemAdd((__bridge CFDictionaryRef)query, (CFTypeRef *)&result); 18 | } 19 | if (status != errSecSuccess) { 20 | return; 21 | } 22 | 23 | bundleId = [[NSBundle mainBundle] bundleIdentifier]; 24 | accessGroupId = [(__bridge NSDictionary *)result objectForKey:(__bridge NSString *)kSecAttrAccessGroup]; 25 | if (result) { // uhh, okay 26 | CFRelease(result); 27 | } 28 | } 29 | 30 | __attribute__((constructor)) static void init() { 31 | setRequiredIDs(); 32 | rebindSecFuncs(); 33 | } -------------------------------------------------------------------------------- /src/SecRebinds.xm: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "Header.h" 4 | #import "../fishhook/fishhook.h" 5 | 6 | static OSStatus (*origSecItemAdd)(CFDictionaryRef attributes, CFTypeRef *result); 7 | static OSStatus (*origSecItemCopyMatching)(CFDictionaryRef query, CFTypeRef *result); 8 | static OSStatus (*origSecItemUpdate)(CFDictionaryRef query, CFDictionaryRef attributesToUpdate); 9 | static OSStatus (*origSecItemDelete)(CFDictionaryRef query); 10 | 11 | static OSStatus zxSecItemAdd(CFDictionaryRef attributes, CFTypeRef *result) { 12 | NSMutableDictionary *mutableAttributes = [(__bridge NSDictionary *)attributes mutableCopy]; 13 | mutableAttributes[(__bridge NSString *)kSecAttrAccessGroup] = accessGroupId; 14 | return origSecItemAdd((__bridge CFDictionaryRef)mutableAttributes, result); 15 | } 16 | 17 | static OSStatus zxSecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result) { 18 | NSMutableDictionary *mutableQuery = [(__bridge NSDictionary *)query mutableCopy]; 19 | mutableQuery[(__bridge NSString *)kSecAttrAccessGroup] = accessGroupId; 20 | return origSecItemCopyMatching((__bridge CFDictionaryRef)mutableQuery, result); 21 | } 22 | 23 | static OSStatus zxSecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate) { 24 | NSMutableDictionary *mutableQuery = [(__bridge NSDictionary *)query mutableCopy]; 25 | mutableQuery[(__bridge NSString *)kSecAttrAccessGroup] = accessGroupId; 26 | return origSecItemUpdate((__bridge CFDictionaryRef)mutableQuery, attributesToUpdate); 27 | } 28 | 29 | static OSStatus zxSecItemDelete(CFDictionaryRef query) { 30 | NSMutableDictionary *mutableQuery = [(__bridge NSDictionary *)query mutableCopy]; 31 | mutableQuery[(__bridge NSString *)kSecAttrAccessGroup] = accessGroupId; 32 | return origSecItemDelete((__bridge CFDictionaryRef)mutableQuery); 33 | } 34 | 35 | void rebindSecFuncs() { 36 | struct rebinding rebinds[4] = { 37 | {"SecItemAdd", (void *)zxSecItemAdd, (void **)&origSecItemAdd}, 38 | {"SecItemCopyMatching", (void *)zxSecItemCopyMatching, (void **)&origSecItemCopyMatching}, 39 | {"SecItemUpdate", (void *)zxSecItemUpdate, (void **)&origSecItemUpdate}, 40 | {"SecItemDelete", (void *)zxSecItemDelete, (void **)&origSecItemDelete} 41 | }; 42 | NSLog(@"[zx] rebind_symbols result: %d", rebind_symbols(rebinds, 4)); 43 | } -------------------------------------------------------------------------------- /src/SideloadFix.xm: -------------------------------------------------------------------------------- 1 | #import "Header.h" 2 | 3 | %hook CKContainer 4 | - (id)_setupWithContainerID:(id)a options:(id)b { return nil; } 5 | - (id)_initWithContainerIdentifier:(id)a { return nil; } 6 | %end 7 | 8 | %hook CKEntitlements 9 | - (id)initWithEntitlementsDict:(NSDictionary *)entitlements { 10 | NSMutableDictionary *mutEntitlements = [entitlements mutableCopy]; 11 | [mutEntitlements removeObjectForKey:@"com.apple.developer.icloud-container-environment"]; 12 | [mutEntitlements removeObjectForKey:@"com.apple.developer.icloud-services"]; 13 | return %orig([mutEntitlements copy]); // why? whatever 14 | } 15 | %end 16 | 17 | %hook NSFileManager 18 | - (NSURL *)containerURLForSecurityApplicationGroupIdentifier:(NSString *)groupIdentifier { 19 | if (NSURL *ourAppGroupURL = getAppGroupPathIfExists()) { 20 | NSURL *fakeAppGroupURL = [ourAppGroupURL URLByAppendingPathComponent:groupIdentifier]; 21 | createDirectoryIfNotExists(fakeAppGroupURL.path); 22 | return fakeAppGroupURL; 23 | } 24 | 25 | // fallback to a fake App Group path in Documents/App Group 26 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 27 | NSString *fakePath = [[paths lastObject] stringByAppendingPathComponent:groupIdentifier]; 28 | createDirectoryIfNotExists(fakePath); 29 | return [NSURL fileURLWithPath:fakePath]; 30 | } 31 | %end 32 | 33 | %hook NSUserDefaults 34 | - (id)_initWithSuiteName:(NSString *)suiteName container:(NSURL *)container { 35 | NSLog(@"[zx] hooking NSUserDefaults init..."); 36 | 37 | NSURL *appGroupURL = getAppGroupPathIfExists(); 38 | if (!appGroupURL) { 39 | NSLog(@"[zx] no valid app group available, defaulting to original container"); 40 | return %orig(suiteName, container); 41 | } 42 | NSLog(@"[zx] app group URL: %@", appGroupURL); 43 | 44 | if (![suiteName hasPrefix:@"group"]) { 45 | NSLog(@"[zx] suite name '%@' does not start with 'group' ,, defaulting to original container", suiteName); 46 | return %orig(suiteName, container); 47 | } 48 | 49 | if (NSURL *customContainerURL = [appGroupURL URLByAppendingPathComponent:suiteName]) { 50 | NSLog(@"[zx] using custom container URL: %@", customContainerURL); 51 | return %orig(suiteName, customContainerURL); 52 | } 53 | 54 | NSLog(@"[zx] failed to construct valid URL for suite '%@' in app group container", suiteName); 55 | return %orig(suiteName, container); 56 | } 57 | %end 58 | -------------------------------------------------------------------------------- /src/Paths.mm: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "Header.h" 4 | 5 | BOOL createDirectoryIfNotExists(NSString *path) { 6 | NSFileManager *fileManager = [NSFileManager defaultManager]; 7 | if ([fileManager fileExistsAtPath:path]) { 8 | NSLog(@"[zx] directory already exists: %@", path); 9 | return YES; 10 | } 11 | 12 | NSError *error = nil; 13 | [fileManager createDirectoryAtPath:path 14 | withIntermediateDirectories:YES 15 | attributes:nil 16 | error:&error]; 17 | 18 | if (error) { 19 | NSLog(@"[zx] failed to create directory at path (%@): %@", path, error); 20 | return NO; 21 | } 22 | 23 | NSLog(@"[zx] created directory at path: %@", path); 24 | return YES; 25 | } 26 | 27 | NSURL *getAppGroupPathIfExists() { 28 | static NSURL *cachedAppGroupPath = nil; 29 | static dispatch_once_t onceToken; 30 | 31 | dispatch_once(&onceToken, ^{ 32 | NSLog(@"[zx] fetching app group path..."); 33 | 34 | LSBundleProxy *bundleProxy = [objc_getClass("LSBundleProxy") bundleProxyForCurrentProcess]; 35 | if (!bundleProxy) { 36 | NSLog(@"[zx] failed to retrieve LSBundleProxy for the current process"); 37 | return; 38 | } 39 | 40 | NSDictionary *entitlements = bundleProxy.entitlements; 41 | if (!entitlements || ![entitlements isKindOfClass:[NSDictionary class]]) { 42 | NSLog(@"[zx] failed to retrieve entitlements"); 43 | return; 44 | } 45 | 46 | NSArray *appGroups = entitlements[@"com.apple.security.application-groups"]; 47 | if (!appGroups) { 48 | NSLog(@"[zx] no app groups found in entitlements"); 49 | return; 50 | } 51 | 52 | if (appGroups.count == 0) { 53 | NSLog(@"[zx] app group entitlement exists, but no app groups are configured"); 54 | return; 55 | } 56 | 57 | NSString *appGroupName = [appGroups firstObject]; 58 | NSLog(@"[zx] app group name: %@", appGroupName); 59 | 60 | NSDictionary *appGroupsPaths = bundleProxy.groupContainerURLs; 61 | if (!appGroupsPaths || ![appGroupsPaths isKindOfClass:[NSDictionary class]]) { 62 | NSLog(@"[zx] failed to retrieve group container URLs"); 63 | return; 64 | } 65 | 66 | NSURL *ourAppGroupURL = appGroupsPaths[appGroupName]; 67 | if (ourAppGroupURL) { 68 | cachedAppGroupPath = ourAppGroupURL; 69 | NSLog(@"[zx] app group path: %@", cachedAppGroupPath.path); 70 | } else { 71 | NSLog(@"[zx] no path found for app group name: %@", appGroupName); 72 | } 73 | }); 74 | 75 | return cachedAppGroupPath; 76 | } --------------------------------------------------------------------------------