- Fix BunnyLog compile error in Sideloading.x (leftover from Bunny->Revenge rename; only RevengeLog is defined) - Remove duplicated -loadConfig/+defaultConfig methods in LoaderConfig.m - Repoint CI's ipa-patcher download at a live mirror with a pinned checksum (Cristiandis/ipa-patcher was deleted upstream) - Add a second injection hook on RCTInstance/-_loadScriptFromSource: for React Native's Bridgeless mode, alongside the existing RCTCxxBridge hook. Discord's Android build already runs Bridgeless (confirmed via revenge-xposed's hook targets) and revenge-bundle now pins RN 0.81, which defaults to it - so the legacy bridge hook this tweak relied on was very likely dead. Unverified against a real device; see AUDIT.md. See AUDIT.md for the full iOS-vs-Android comparison and remaining gaps.
88 lines
No EOL
2.7 KiB
Text
88 lines
No EOL
2.7 KiB
Text
#import <Foundation/Foundation.h>
|
|
#import <UIKit/UIKit.h>
|
|
#import <dlfcn.h>
|
|
#import "Logger.h"
|
|
|
|
#define DISCORD_BUNDLE_ID @"com.hammerandchisel.discord"
|
|
#define DISCORD_NAME @"Discord"
|
|
|
|
static NSString *getAccessGroupID(void) {
|
|
NSDictionary *query = @{
|
|
(__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword,
|
|
(__bridge NSString *)kSecAttrAccount: @"bundleSeedID",
|
|
(__bridge NSString *)kSecAttrService: @"",
|
|
(__bridge NSString *)kSecReturnAttributes: @YES
|
|
};
|
|
|
|
CFDictionaryRef result = NULL;
|
|
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
|
|
|
|
if (status == errSecItemNotFound) {
|
|
status = SecItemAdd((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
|
|
}
|
|
|
|
if (status != errSecSuccess) return nil;
|
|
|
|
NSString *accessGroup = [(__bridge NSDictionary *)result objectForKey:(__bridge NSString *)kSecAttrAccessGroup];
|
|
if (result) CFRelease(result);
|
|
|
|
return accessGroup;
|
|
}
|
|
|
|
static BOOL isSelfCall(void) {
|
|
NSArray *address = [NSThread callStackReturnAddresses];
|
|
Dl_info info = {0};
|
|
if (dladdr((void *)[address[2] longLongValue], &info) == 0) return NO;
|
|
NSString *path = [NSString stringWithUTF8String:info.dli_fname];
|
|
return [path hasPrefix:NSBundle.mainBundle.bundlePath];
|
|
}
|
|
|
|
%group Sideloading
|
|
|
|
%hook NSBundle
|
|
- (NSString *)bundleIdentifier {
|
|
return isSelfCall() ? DISCORD_BUNDLE_ID : %orig;
|
|
}
|
|
|
|
- (NSDictionary *)infoDictionary {
|
|
if (!isSelfCall()) return %orig;
|
|
|
|
NSMutableDictionary *info = [%orig mutableCopy];
|
|
info[@"CFBundleIdentifier"] = DISCORD_BUNDLE_ID;
|
|
info[@"CFBundleDisplayName"] = DISCORD_NAME;
|
|
info[@"CFBundleName"] = DISCORD_NAME;
|
|
return info;
|
|
}
|
|
|
|
- (id)objectForInfoDictionaryKey:(NSString *)key {
|
|
if (!isSelfCall()) return %orig;
|
|
|
|
if ([key isEqualToString:@"CFBundleIdentifier"]) return DISCORD_BUNDLE_ID;
|
|
if ([key isEqualToString:@"CFBundleDisplayName"] ||
|
|
[key isEqualToString:@"CFBundleName"]) return DISCORD_NAME;
|
|
return %orig;
|
|
}
|
|
%end
|
|
|
|
%hook NSFileManager
|
|
- (NSURL *)containerURLForSecurityApplicationGroupIdentifier:(NSString *)groupIdentifier {
|
|
RevengeLog(@"containerURLForSecurityApplicationGroupIdentifier called! %@", groupIdentifier ?: @"nil");
|
|
|
|
NSArray *paths = [self URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
|
|
NSURL *lastPath = [paths lastObject];
|
|
return [lastPath URLByAppendingPathComponent:@"AppGroup"];
|
|
}
|
|
%end
|
|
|
|
%hook UIPasteboard
|
|
- (NSString *)_accessGroup {
|
|
return getAccessGroupID();
|
|
}
|
|
%end
|
|
|
|
%end
|
|
|
|
%ctor {
|
|
BOOL isAppStoreApp = [[NSFileManager defaultManager] fileExistsAtPath:[[NSBundle mainBundle] appStoreReceiptURL].path];
|
|
if (!isAppStoreApp) %init(Sideloading);
|
|
} |