diff --git a/FoundationPrivate.h b/FoundationPrivate.h new file mode 100644 index 0000000..85a4d91 --- /dev/null +++ b/FoundationPrivate.h @@ -0,0 +1,10 @@ +#include + +@interface NSBundle(private) +- (id)_cfBundle; +@end + +@interface NSUserDefaults(private) ++ (void)setStandardUserDefaults:(id)defaults; +- (NSString*)_identifier; +@end diff --git a/LCSharedUtils.m b/LCSharedUtils.m index c0b458b..4604578 100644 --- a/LCSharedUtils.m +++ b/LCSharedUtils.m @@ -70,10 +70,12 @@ + (BOOL)launchToGuestApp { urlScheme = @"sidestore://sidejit-enable?bid=%@"; } NSURL *launchURL = [NSURL URLWithString:[NSString stringWithFormat:urlScheme, NSBundle.mainBundle.bundleIdentifier]]; - if ([UIApplication.sharedApplication canOpenURL:launchURL]) { + + UIApplication *application = [NSClassFromString(@"UIApplication") sharedApplication]; + if ([application canOpenURL:launchURL]) { //[UIApplication.sharedApplication suspend]; for (int i = 0; i < tries; i++) { - [UIApplication.sharedApplication openURL:launchURL options:@{} completionHandler:^(BOOL b) { + [application openURL:launchURL options:@{} completionHandler:^(BOOL b) { exit(0); }]; } @@ -88,8 +90,9 @@ + (BOOL)askForJIT { if (!access(tsPath.UTF8String, F_OK)) { urlScheme = @"apple-magnifier://enable-jit?bundle-id=%@"; NSURL *launchURL = [NSURL URLWithString:[NSString stringWithFormat:urlScheme, NSBundle.mainBundle.bundleIdentifier]]; - if ([UIApplication.sharedApplication canOpenURL:launchURL]) { - [UIApplication.sharedApplication openURL:launchURL options:@{} completionHandler:nil]; + UIApplication *application = [NSClassFromString(@"UIApplication") sharedApplication]; + if ([application canOpenURL:launchURL]) { + [application openURL:launchURL options:@{} completionHandler:nil]; [LCSharedUtils launchToGuestApp]; return YES; } @@ -346,7 +349,7 @@ + (NSString*)findDefaultContainerWithBundleId:(NSString*)bundleId { // find app's default container NSURL* appGroupFolder = [[LCSharedUtils appGroupPath] URLByAppendingPathComponent:@"LiveContainer"]; - NSString* bundleInfoPath = [NSString stringWithFormat:@"%@/Applications/%@/Info.plist", appGroupFolder.path, bundleId]; + NSString* bundleInfoPath = [NSString stringWithFormat:@"%@/Applications/%@/LCAppInfo.plist", appGroupFolder.path, bundleId]; NSDictionary* infoDict = [NSDictionary dictionaryWithContentsOfFile:bundleInfoPath]; return infoDict[@"LCDataUUID"]; } diff --git a/LiveContainerSwiftUI/LCAppInfo.h b/LiveContainerSwiftUI/LCAppInfo.h index 26336e5..6b5928b 100644 --- a/LiveContainerSwiftUI/LCAppInfo.h +++ b/LiveContainerSwiftUI/LCAppInfo.h @@ -9,8 +9,9 @@ typedef NS_ENUM(NSInteger, LCOrientationLock){ }; @interface LCAppInfo : NSObject { - NSMutableDictionary* _info; - NSString* _bundlePath; + NSMutableDictionary* _info; + NSMutableDictionary* _infoPlist; + NSString* _bundlePath; } @property NSString* relativeBundlePath; @property bool isShared; @@ -18,6 +19,8 @@ typedef NS_ENUM(NSInteger, LCOrientationLock){ @property bool isLocked; @property bool isHidden; @property bool doSymlinkInbox; +@property bool ignoreDlopenError; +@property bool fixBlackScreen; @property bool bypassAssertBarrierOnQueue; @property UIColor* cachedColor; @property Signer signer; diff --git a/LiveContainerSwiftUI/LCAppInfo.m b/LiveContainerSwiftUI/LCAppInfo.m index 8b176c2..3aba9ce 100644 --- a/LiveContainerSwiftUI/LCAppInfo.m +++ b/LiveContainerSwiftUI/LCAppInfo.m @@ -11,7 +11,46 @@ - (instancetype)initWithBundlePath:(NSString*)bundlePath { self.isShared = false; if(self) { _bundlePath = bundlePath; - _info = [NSMutableDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"%@/Info.plist", bundlePath]]; + _infoPlist = [NSMutableDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"%@/Info.plist", bundlePath]]; + _info = [NSMutableDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"%@/LCAppInfo.plist", bundlePath]]; + if(!_info) { + _info = [[NSMutableDictionary alloc] init]; + } + if(!_infoPlist) { + _infoPlist = [[NSMutableDictionary alloc] init]; + } + + // migrate old appInfo + if(_infoPlist[@"LCPatchRevision"]) { + NSArray* lcAppInfoKeys = @[ + @"LCPatchRevision", + @"LCOrignalBundleIdentifier", + @"LCDataUUID", + @"LCTweakFolder", + @"LCJITLessSignID", + @"LCSelectedLanguage", + @"LCExpirationDate", + @"LCTeamId", + @"isJITNeeded", + @"isLocked", + @"isHidden", + @"doUseLCBundleId", + @"doSymlinkInbox", + @"bypassAssertBarrierOnQueue", + @"signer", + @"LCOrientationLock", + @"cachedColor", + @"LCContainers", + @"ignoreDlopenError" + ]; + for(NSString* key in lcAppInfoKeys) { + _info[key] = _infoPlist[key]; + [_infoPlist removeObjectForKey:key]; + } + [_infoPlist writeToFile:[NSString stringWithFormat:@"%@/Info.plist", bundlePath] atomically:YES]; + [self save]; + } + _autoSaveDisabled = false; } return self; @@ -25,8 +64,8 @@ - (NSMutableArray*)urlSchemes { // find all url schemes NSMutableArray* urlSchemes = [[NSMutableArray alloc] init]; int nowSchemeCount = 0; - if (_info[@"CFBundleURLTypes"]) { - NSMutableArray* urlTypes = _info[@"CFBundleURLTypes"]; + if (_infoPlist[@"CFBundleURLTypes"]) { + NSMutableArray* urlTypes = _infoPlist[@"CFBundleURLTypes"]; for(int i = 0; i < [urlTypes count]; ++i) { NSMutableDictionary* nowUrlType = [urlTypes objectAtIndex:i]; @@ -45,21 +84,21 @@ - (NSMutableArray*)urlSchemes { } - (NSString*)displayName { - if (_info[@"CFBundleDisplayName"]) { - return _info[@"CFBundleDisplayName"]; - } else if (_info[@"CFBundleName"]) { - return _info[@"CFBundleName"]; - } else if (_info[@"CFBundleExecutable"]) { - return _info[@"CFBundleExecutable"]; + if (_infoPlist[@"CFBundleDisplayName"]) { + return _infoPlist[@"CFBundleDisplayName"]; + } else if (_infoPlist[@"CFBundleName"]) { + return _infoPlist[@"CFBundleName"]; + } else if (_infoPlist[@"CFBundleExecutable"]) { + return _infoPlist[@"CFBundleExecutable"]; } else { - return nil; + return @"Unknown"; } } - (NSString*)version { - NSString* version = _info[@"CFBundleShortVersionString"]; + NSString* version = _infoPlist[@"CFBundleShortVersionString"]; if (!version) { - version = _info[@"CFBundleVersion"]; + version = _infoPlist[@"CFBundleVersion"]; } return version; } @@ -68,7 +107,7 @@ - (NSString*)bundleIdentifier { if([self doUseLCBundleId]) { return _info[@"LCOrignalBundleIdentifier"]; } else { - return _info[@"CFBundleIdentifier"]; + return _infoPlist[@"CFBundleIdentifier"]; } } @@ -113,13 +152,13 @@ - (NSMutableDictionary*)info { } - (UIImage*)icon { - UIImage* icon = [UIImage imageNamed:[_info valueForKeyPath:@"CFBundleIcons.CFBundlePrimaryIcon.CFBundleIconFiles"][0] inBundle:[[NSBundle alloc] initWithPath: _bundlePath] compatibleWithTraitCollection:nil]; + UIImage* icon = [UIImage imageNamed:[_infoPlist valueForKeyPath:@"CFBundleIcons.CFBundlePrimaryIcon.CFBundleIconFiles"][0] inBundle:[[NSBundle alloc] initWithPath: _bundlePath] compatibleWithTraitCollection:nil]; if(!icon) { - icon = [UIImage imageNamed:[_info valueForKeyPath:@"CFBundleIconFiles"][0] inBundle:[[NSBundle alloc] initWithPath: _bundlePath] compatibleWithTraitCollection:nil]; + icon = [UIImage imageNamed:[_infoPlist valueForKeyPath:@"CFBundleIconFiles"][0] inBundle:[[NSBundle alloc] initWithPath: _bundlePath] compatibleWithTraitCollection:nil]; } if(!icon) { - icon = [UIImage imageNamed:[_info valueForKeyPath:@"CFBundleIcons~ipad"][@"CFBundlePrimaryIcon"][@"CFBundleIconName"] inBundle:[[NSBundle alloc] initWithPath: _bundlePath] compatibleWithTraitCollection:nil]; + icon = [UIImage imageNamed:[_infoPlist valueForKeyPath:@"CFBundleIcons~ipad"][@"CFBundlePrimaryIcon"][@"CFBundleIconName"] inBundle:[[NSBundle alloc] initWithPath: _bundlePath] compatibleWithTraitCollection:nil]; } if(!icon) { @@ -188,7 +227,7 @@ - (NSDictionary *)generateWebClipConfigWithContainerId:(NSString*)containerId { - (void)save { if(!_autoSaveDisabled) { - [_info writeToFile:[NSString stringWithFormat:@"%@/Info.plist", _bundlePath] atomically:YES]; + [_info writeToFile:[NSString stringWithFormat:@"%@/LCAppInfo.plist", _bundlePath] atomically:YES]; } } @@ -212,6 +251,7 @@ - (void)patchExecAndSignIfNeedWithCompletionHandler:(void(^)(bool success, NSStr NSString *appPath = self.bundlePath; NSString *infoPath = [NSString stringWithFormat:@"%@/Info.plist", appPath]; NSMutableDictionary *info = _info; + NSMutableDictionary *infoPlist = _infoPlist; if (!info) { completetionHandler(NO, @"Info.plist not found"); return; @@ -220,7 +260,7 @@ - (void)patchExecAndSignIfNeedWithCompletionHandler:(void(^)(bool success, NSStr // Update patch int currentPatchRev = 5; if ([info[@"LCPatchRevision"] intValue] < currentPatchRev) { - NSString *execPath = [NSString stringWithFormat:@"%@/%@", appPath, info[@"CFBundleExecutable"]]; + NSString *execPath = [NSString stringWithFormat:@"%@/%@", appPath, _infoPlist[@"CFBundleExecutable"]]; NSString *error = LCParseMachO(execPath.UTF8String, ^(const char *path, struct mach_header_64 *header) { LCPatchExecSlice(path, header); }); @@ -270,16 +310,16 @@ - (void)patchExecAndSignIfNeedWithCompletionHandler:(void(^)(bool success, NSStr // Don't let main executable get entitlements [NSFileManager.defaultManager copyItemAtPath:NSBundle.mainBundle.executablePath toPath:tmpExecPath error:nil]; - info[@"LCBundleExecutable"] = info[@"CFBundleExecutable"]; - info[@"LCBundleIdentifier"] = info[@"CFBundleIdentifier"]; - info[@"CFBundleExecutable"] = tmpExecPath.lastPathComponent; - info[@"CFBundleIdentifier"] = NSBundle.mainBundle.bundleIdentifier; - [info writeToFile:infoPath atomically:YES]; + infoPlist[@"LCBundleExecutable"] = infoPlist[@"CFBundleExecutable"]; + infoPlist[@"LCBundleIdentifier"] = infoPlist[@"CFBundleIdentifier"]; + infoPlist[@"CFBundleExecutable"] = tmpExecPath.lastPathComponent; + infoPlist[@"CFBundleIdentifier"] = NSBundle.mainBundle.bundleIdentifier; + [infoPlist writeToFile:infoPath atomically:YES]; } - info[@"CFBundleExecutable"] = info[@"LCBundleExecutable"]; - info[@"CFBundleIdentifier"] = info[@"LCBundleIdentifier"]; - [info removeObjectForKey:@"LCBundleExecutable"]; - [info removeObjectForKey:@"LCBundleIdentifier"]; + infoPlist[@"CFBundleExecutable"] = infoPlist[@"LCBundleExecutable"]; + infoPlist[@"CFBundleIdentifier"] = infoPlist[@"LCBundleIdentifier"]; + [infoPlist removeObjectForKey:@"LCBundleExecutable"]; + [infoPlist removeObjectForKey:@"LCBundleIdentifier"]; void (^signCompletionHandler)(BOOL success, NSDate* expirationDate, NSString* teamId, NSError *error) = ^(BOOL success, NSDate* expirationDate, NSString* teamId, NSError *_Nullable error) { dispatch_async(dispatch_get_main_queue(), ^{ @@ -299,7 +339,7 @@ - (void)patchExecAndSignIfNeedWithCompletionHandler:(void(^)(bool success, NSStr } // Save sign ID and restore bundle ID [self save]; - + [infoPlist writeToFile:infoPath atomically:YES]; completetionHandler(success, error.localizedDescription); }); @@ -384,6 +424,31 @@ - (void)setDoSymlinkInbox:(bool)doSymlinkInbox { } +- (bool)ignoreDlopenError { + if(_info[@"ignoreDlopenError"] != nil) { + return [_info[@"ignoreDlopenError"] boolValue]; + } else { + return NO; + } +} +- (void)setIgnoreDlopenError:(bool)ignoreDlopenError { + _info[@"ignoreDlopenError"] = [NSNumber numberWithBool:ignoreDlopenError]; + [self save]; +} + +- (bool)fixBlackScreen { + if(_info[@"fixBlackScreen"] != nil) { + return [_info[@"fixBlackScreen"] boolValue]; + } else { + return NO; + } +} +- (void)setFixBlackScreen:(bool)fixBlackScreen { + _info[@"fixBlackScreen"] = [NSNumber numberWithBool:fixBlackScreen]; + [self save]; +} + + - (bool)doUseLCBundleId { if(_info[@"doUseLCBundleId"] != nil) { return [_info[@"doUseLCBundleId"] boolValue]; @@ -393,13 +458,15 @@ - (bool)doUseLCBundleId { } - (void)setDoUseLCBundleId:(bool)doUseLCBundleId { _info[@"doUseLCBundleId"] = [NSNumber numberWithBool:doUseLCBundleId]; + NSString *infoPath = [NSString stringWithFormat:@"%@/Info.plist", self.bundlePath]; if(doUseLCBundleId) { - _info[@"LCOrignalBundleIdentifier"] = _info[@"CFBundleIdentifier"]; - _info[@"CFBundleIdentifier"] = NSBundle.mainBundle.bundleIdentifier; + _info[@"LCOrignalBundleIdentifier"] = _infoPlist[@"CFBundleIdentifier"]; + _infoPlist[@"CFBundleIdentifier"] = NSBundle.mainBundle.bundleIdentifier; } else if (_info[@"LCOrignalBundleIdentifier"]) { - _info[@"CFBundleIdentifier"] = _info[@"LCOrignalBundleIdentifier"]; + _infoPlist[@"CFBundleIdentifier"] = _info[@"LCOrignalBundleIdentifier"]; [_info removeObjectForKey:@"LCOrignalBundleIdentifier"]; } + [_infoPlist writeToFile:infoPath atomically:YES]; [self save]; } diff --git a/LiveContainerSwiftUI/LCAppListView.swift b/LiveContainerSwiftUI/LCAppListView.swift index 855df98..cd91da3 100644 --- a/LiveContainerSwiftUI/LCAppListView.swift +++ b/LiveContainerSwiftUI/LCAppListView.swift @@ -479,6 +479,7 @@ struct LCAppListView : View, LCAppBannerDelegate, LCAppModelDelegate { finalNewApp.selectedLanguage = appToReplace.appInfo.selectedLanguage finalNewApp.dataUUID = appToReplace.appInfo.dataUUID finalNewApp.orientationLock = appToReplace.appInfo.orientationLock + finalNewApp.ignoreDlopenError = appToReplace.appInfo.ignoreDlopenError finalNewApp.autoSaveDisabled = false finalNewApp.save() } diff --git a/LiveContainerSwiftUI/LCAppModel.swift b/LiveContainerSwiftUI/LCAppModel.swift index b4a2719..6826df6 100644 --- a/LiveContainerSwiftUI/LCAppModel.swift +++ b/LiveContainerSwiftUI/LCAppModel.swift @@ -26,6 +26,8 @@ class LCAppModel: ObservableObject, Hashable { @Published var uiUseLCBundleId : Bool @Published var uiBypassAssertBarrierOnQueue : Bool @Published var uiSigner : Signer + @Published var uiIgnoreDlopenError : Bool + @Published var uiFixBlackScreen : Bool @Published var uiOrientationLock : LCOrientationLock @Published var uiSelectedLanguage : String @Published var supportedLanaguages : [String]? @@ -55,6 +57,8 @@ class LCAppModel: ObservableObject, Hashable { self.uiSigner = appInfo.signer self.uiOrientationLock = appInfo.orientationLock self.uiUseLCBundleId = appInfo.doUseLCBundleId + self.uiIgnoreDlopenError = appInfo.ignoreDlopenError + self.uiFixBlackScreen = appInfo.fixBlackScreen for container in uiContainers { if container.folderName == uiDefaultDataFolder { diff --git a/LiveContainerSwiftUI/LCAppSettingsView.swift b/LiveContainerSwiftUI/LCAppSettingsView.swift index eb28718..82e712a 100644 --- a/LiveContainerSwiftUI/LCAppSettingsView.swift +++ b/LiveContainerSwiftUI/LCAppSettingsView.swift @@ -250,6 +250,28 @@ struct LCAppSettingsView : View{ }) } } + + Section { + Toggle(isOn: $model.uiIgnoreDlopenError) { + Text("lc.appSettings.ignoreDlopenError".loc) + } + .onChange(of: model.uiIgnoreDlopenError, perform: { newValue in + Task { await setIgnoreDlopenError(newValue) } + }) + } footer: { + Text("lc.appSettings.ignoreDlopenErrorDesc".loc) + } + + Section { + Toggle(isOn: $model.uiFixBlackScreen) { + Text("lc.appSettings.fixBlackScreen".loc) + } + .onChange(of: model.uiFixBlackScreen, perform: { newValue in + Task { await setFixBlackScreen(newValue) } + }) + } footer: { + Text("lc.appSettings.fixBlackScreenDesc".loc) + } Section { @@ -492,6 +514,16 @@ struct LCAppSettingsView : View{ model.uiUseLCBundleId = doUseLCBundleId } + func setIgnoreDlopenError(_ ignoreDlopenError : Bool) async { + appInfo.ignoreDlopenError = ignoreDlopenError + model.uiIgnoreDlopenError = ignoreDlopenError + } + + func setFixBlackScreen(_ fixBlackScreen : Bool) async { + appInfo.fixBlackScreen = fixBlackScreen + model.uiFixBlackScreen = fixBlackScreen + } + func setOrientationLock(_ lock : LCOrientationLock) async { appInfo.orientationLock = lock model.uiOrientationLock = lock diff --git a/LiveContainerSwiftUI/Localizable.xcstrings b/LiveContainerSwiftUI/Localizable.xcstrings index d68b7a6..00179ef 100644 --- a/LiveContainerSwiftUI/Localizable.xcstrings +++ b/LiveContainerSwiftUI/Localizable.xcstrings @@ -773,6 +773,40 @@ } } }, + "lc.appSettings.fixBlackScreen" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fix Black Screen" + } + }, + "zh_CN" : { + "stringUnit" : { + "state" : "translated", + "value" : "修复黑屏" + } + } + } + }, + "lc.appSettings.fixBlackScreenDesc" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "If app shows a black screen when launching and crash after about 10s, enable this option." + } + }, + "zh_CN" : { + "stringUnit" : { + "state" : "translated", + "value" : "如果App启动时黑屏,并在10秒后崩溃,启用这个选项。" + } + } + } + }, "lc.appSettings.fixes" : { "extractionState" : "manual", "localizations" : { @@ -892,6 +926,40 @@ } } }, + "lc.appSettings.ignoreDlopenError" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignore Error During dlopen" + } + }, + "zh_CN" : { + "stringUnit" : { + "state" : "translated", + "value" : "忽略dlopen时的错误" + } + } + } + }, + "lc.appSettings.ignoreDlopenErrorDesc" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Might solve error like dlsym(xxxx, xxxx): symbol not found" + } + }, + "zh_CN" : { + "stringUnit" : { + "state" : "translated", + "value" : "可能解决类似dlsym(xxxx,xxxx): symbol not found的问题" + } + } + } + }, "lc.appSettings.launchWithJit" : { "extractionState" : "manual", "localizations" : { diff --git a/NSUserDefaults.m b/NSUserDefaults.m index 201ffaa..d5cd530 100644 --- a/NSUserDefaults.m +++ b/NSUserDefaults.m @@ -5,9 +5,8 @@ // Created by s s on 2024/11/29. // -#import +#import "FoundationPrivate.h" #import "LCSharedUtils.h" -#import "UIKitPrivate.h" #import "utils.h" #import "LCSharedUtils.h" @@ -42,7 +41,7 @@ void NUDGuestHooksInit() { [fm createDirectoryAtPath:preferenceFolderPath.path withIntermediateDirectories:YES attributes:@{} error:&error]; } - [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification + [[NSNotificationCenter defaultCenter] addObserverForName:@"UIApplicationWillTerminateNotification" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull notification) { diff --git a/Resources/Info.plist b/Resources/Info.plist index 7d63d18..f022599 100644 --- a/Resources/Info.plist +++ b/Resources/Info.plist @@ -42,7 +42,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 3.1.54 + 3.1.55 CFBundleSignature ???? CFBundleSupportedPlatforms @@ -61,7 +61,7 @@ CFBundleVersion - 3.1.54 + 3.1.55 LSApplicationCategoryType public.app-category.games LSApplicationQueriesSchemes diff --git a/Resources/Settings.bundle/Root.plist b/Resources/Settings.bundle/Root.plist new file mode 100644 index 0000000..533fa10 --- /dev/null +++ b/Resources/Settings.bundle/Root.plist @@ -0,0 +1,17 @@ + + + + + PreferenceSpecifiers + + + Type + PSTextFieldSpecifier + Title + selected + Key + selected + + + + diff --git a/TweakLoader/DocumentPicker.m b/TweakLoader/DocumentPicker.m index df4c7bc..63237df 100644 --- a/TweakLoader/DocumentPicker.m +++ b/TweakLoader/DocumentPicker.m @@ -7,7 +7,7 @@ BOOL fixFilePicker; __attribute__((constructor)) static void NSFMGuestHooksInit() { - fixFilePicker = [NSBundle.mainBundle.infoDictionary[@"doSymlinkInbox"] boolValue]; + fixFilePicker = [NSUserDefaults.guestAppInfo[@"doSymlinkInbox"] boolValue]; swizzle(UIDocumentPickerViewController.class, @selector(initForOpeningContentTypes:asCopy:), @selector(hook_initForOpeningContentTypes:asCopy:)); swizzle(UIDocumentPickerViewController.class, @selector(initWithDocumentTypes:inMode:), @selector(hook_initWithDocumentTypes:inMode:)); diff --git a/TweakLoader/FBSSerialQueue.m b/TweakLoader/FBSSerialQueue.m index 218227b..ec00bec 100644 --- a/TweakLoader/FBSSerialQueue.m +++ b/TweakLoader/FBSSerialQueue.m @@ -26,7 +26,7 @@ - (void)setCurrentSubscription:(id)sub { __attribute__((constructor)) static void NSFMGuestHooksInit() { - if(![[NSBundle.mainBundle infoDictionary][@"bypassAssertBarrierOnQueue"] boolValue]) { + if(![NSUserDefaults.guestAppInfo[@"bypassAssertBarrierOnQueue"] boolValue]) { return; } diff --git a/TweakLoader/SecItem.m b/TweakLoader/SecItem.m index 5600b75..1ea346c 100644 --- a/TweakLoader/SecItem.m +++ b/TweakLoader/SecItem.m @@ -84,8 +84,11 @@ static void SecItemGuestHooksInit() { accessGroup = [NSString stringWithFormat:@"%@.com.kdt.livecontainer.shared.%d", groupId, keychainGroupId]; } - rebind_symbols((struct rebinding[1]){{"SecItemAdd", (void *)new_SecItemAdd, (void **)&orig_SecItemAdd}},1); - rebind_symbols((struct rebinding[1]){{"SecItemCopyMatching", (void *)new_SecItemCopyMatching, (void **)&orig_SecItemCopyMatching}},1); - rebind_symbols((struct rebinding[1]){{"SecItemUpdate", (void *)new_SecItemUpdate, (void **)&orig_SecItemUpdate}},1); - rebind_symbols((struct rebinding[1]){{"SecItemDelete", (void *)new_SecItemDelete, (void **)&orig_SecItemDelete}},1); + struct rebinding rebindings[] = (struct rebinding[]){ + {"SecItemAdd", (void *)new_SecItemAdd, (void **)&orig_SecItemAdd}, + {"SecItemCopyMatching", (void *)new_SecItemCopyMatching, (void **)&orig_SecItemCopyMatching}, + {"SecItemUpdate", (void *)new_SecItemUpdate, (void **)&orig_SecItemUpdate}, + {"SecItemDelete", (void *)new_SecItemDelete, (void **)&orig_SecItemDelete} + }; + rebind_symbols(rebindings, sizeof(rebindings)/sizeof(struct rebinding)); } diff --git a/TweakLoader/TweakLoader.m b/TweakLoader/TweakLoader.m index 2697459..3eb9695 100644 --- a/TweakLoader/TweakLoader.m +++ b/TweakLoader/TweakLoader.m @@ -2,6 +2,7 @@ #import #include #include +#include "utils.h" static NSString *loadTweakAtURL(NSURL *url) { NSString *tweakPath = url.path; @@ -70,7 +71,7 @@ static void TweakLoaderConstructor() { } // Load selected tweak folder, recursively - NSString *tweakFolderName = NSBundle.mainBundle.infoDictionary[@"LCTweakFolder"]; + NSString *tweakFolderName = NSUserDefaults.guestAppInfo[@"LCTweakFolder"]; if (tweakFolderName.length > 0) { NSLog(@"Loading tweaks from the selected folder"); NSString *tweakFolder = [globalTweakFolder stringByAppendingPathComponent:tweakFolderName]; @@ -93,24 +94,4 @@ static void TweakLoaderConstructor() { showDlerrAlert(error); }); } -} - -// fix dlsym(RTLD_DEFAULT, bd_requestURLParameters): symbol not found -// by declearing a dummy funtion that generates trash data since it's just a user tracking function -// see https://github.com/volcengine/datarangers-sdk-ios/blob/7ca475f90be36016d35281a02b4e44b6f99f4c72/BDAutoTracker/Classes/Core/Network/BDAutoTrackNetworkRequest.m#L22 -NSMutableDictionary * bd_requestURLParameters(NSString *appID) { - NSMutableDictionary *result = [NSMutableDictionary new]; - [result setValue:@"ios" forKey:@"platform"]; - [result setValue:@"ios" forKey:@"sdk_lib"]; - [result setValue:@"iPhone" forKey:@"device_platform"]; - [result setValue:@(61002) forKey:@"sdk_version"]; - [result setValue:@"iOS" forKey:@"os"]; - [result setValue:@"18.0" forKey:@"os_version"]; - [result setValue:@"6.9.69" forKey:@"app_version"]; - [result setValue:@"iPhone14,2" forKey:@"device_model"]; - [result setValue:@(NO) forKey:@"is_upgrade_user"]; - [result setValue:@"00000000-0000-0000-0000-000000000000" forKey:@"idfa"]; - [result setValue:@"00000000-0000-0000-0000-000000000000" forKey:@"idfv"]; - [result setValue:@"6.9.69" forKey:@"version_code"]; - return result; -} +} \ No newline at end of file diff --git a/TweakLoader/UIKit+GuestHooks.m b/TweakLoader/UIKit+GuestHooks.m index d548e67..dfcb60b 100644 --- a/TweakLoader/UIKit+GuestHooks.m +++ b/TweakLoader/UIKit+GuestHooks.m @@ -13,10 +13,10 @@ static void UIKitGuestHooksInit() { swizzle(UIApplication.class, @selector(_applicationOpenURLAction:payload:origin:), @selector(hook__applicationOpenURLAction:payload:origin:)); swizzle(UIApplication.class, @selector(_connectUISceneFromFBSScene:transitionContext:), @selector(hook__connectUISceneFromFBSScene:transitionContext:)); swizzle(UIApplication.class, @selector(openURL:options:completionHandler:), @selector(hook_openURL:options:completionHandler:)); + swizzle(UIApplication.class, @selector(canOpenURL:), @selector(hook_canOpenURL:)); swizzle(UIScene.class, @selector(scene:didReceiveActions:fromTransitionContext:), @selector(hook_scene:didReceiveActions:fromTransitionContext:)); swizzle(UIScene.class, @selector(openURL:options:completionHandler:), @selector(hook_openURL:options:completionHandler:)); - - NSInteger LCOrientationLockDirection = [NSBundle.mainBundle.infoDictionary[@"LCOrientationLock"] integerValue]; + NSInteger LCOrientationLockDirection = [NSUserDefaults.guestAppInfo[@"LCOrientationLock"] integerValue]; if([UIDevice.currentDevice userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { switch (LCOrientationLockDirection) { case 1: @@ -45,12 +45,12 @@ static void UIKitGuestHooksInit() { NSString *appGroupPath = [NSUserDefaults lcAppGroupPath]; NSString* appGroupFolder = [appGroupPath stringByAppendingPathComponent:@"LiveContainer"]; - NSString* bundleInfoPath = [NSString stringWithFormat:@"%@/Applications/%@/Info.plist", appGroupFolder, bundleId]; + NSString* bundleInfoPath = [NSString stringWithFormat:@"%@/Applications/%@/LCAppInfo.plist", appGroupFolder, bundleId]; NSDictionary* infoDict = [NSDictionary dictionaryWithContentsOfFile:bundleInfoPath]; if(!infoDict) { NSString* lcDocFolder = [[NSString stringWithUTF8String:getenv("LC_HOME_PATH")] stringByAppendingPathComponent:@"Documents"]; - bundleInfoPath = [NSString stringWithFormat:@"%@/Applications/%@/Info.plist", lcDocFolder, bundleId]; + bundleInfoPath = [NSString stringWithFormat:@"%@/Applications/%@/LCAppInfo.plist", lcDocFolder, bundleId]; infoDict = [NSDictionary dictionaryWithContentsOfFile:bundleInfoPath]; } @@ -238,9 +238,14 @@ void handleLiveContainerLaunch(NSURL* url) { } NSBundle* bundle = [NSClassFromString(@"LCSharedUtils") findBundleWithBundleId: bundleName]; - if(!bundle || ([bundle.infoDictionary[@"isHidden"] boolValue] && [NSUserDefaults.lcSharedDefaults boolForKey:@"LCStrictHiding"])) { + NSDictionary* lcAppInfo; + if(bundle) { + lcAppInfo = [NSDictionary dictionaryWithContentsOfURL:[bundle URLForResource:@"LCAppInfo" withExtension:@"plist"]]; + } + + if(!bundle || ([lcAppInfo[@"isHidden"] boolValue] && [NSUserDefaults.lcSharedDefaults boolForKey:@"LCStrictHiding"])) { LCShowAppNotFoundAlert(bundleName); - } else if ([bundle.infoDictionary[@"isLocked"] boolValue]) { + } else if ([lcAppInfo[@"isLocked"] boolValue]) { // need authentication authenticateUser(^(BOOL success, NSError *error) { if (success) { @@ -354,6 +359,14 @@ - (void)hook_openURL:(NSURL *)url options:(NSDictionary *)options [self hook_openURL:url options:options completionHandler:completion]; } } +- (BOOL)hook_canOpenURL:(NSURL *) url { + if(canAppOpenItself(url)) { + return YES; + } else { + return [self hook_canOpenURL:url]; + } +} + @end // Handler for SceneDelegate diff --git a/TweakLoader/utils.h b/TweakLoader/utils.h index 2556414..9f78500 100644 --- a/TweakLoader/utils.h +++ b/TweakLoader/utils.h @@ -10,4 +10,5 @@ void swizzle(Class class, SEL originalAction, SEL swizzledAction); + (NSString *)lcAppUrlScheme; + (NSString *)lcAppGroupPath; + (NSBundle *)lcMainBundle; ++ (NSDictionary*)guestAppInfo; @end diff --git a/UIKitPrivate.h b/UIKitPrivate.h index 67063f3..f23c4df 100644 --- a/UIKitPrivate.h +++ b/UIKitPrivate.h @@ -1,14 +1,5 @@ #import -@interface NSBundle(private) -- (id)_cfBundle; -@end - -@interface NSUserDefaults(private) -+ (void)setStandardUserDefaults:(id)defaults; -- (NSString*)_identifier; -@end - @interface UIImage(private) - (UIImage *)_imageWithSize:(CGSize)size; @end diff --git a/control b/control index c18e386..8a5b9eb 100644 --- a/control +++ b/control @@ -1,6 +1,6 @@ Package: com.kdt.livecontainer Name: livecontainer -Version: 3.1.54 +Version: 3.1.55 Architecture: iphoneos-arm Description: Run iOS app without actually installing it! Maintainer: khanhduytran0 diff --git a/main.m b/main.m index d79457a..28c67ec 100644 --- a/main.m +++ b/main.m @@ -1,4 +1,4 @@ -#import +#import "FoundationPrivate.h" #import "LCSharedUtils.h" #import "UIKitPrivate.h" #import "utils.h" @@ -24,6 +24,7 @@ NSString *lcAppGroupPath; NSString* lcAppUrlScheme; NSBundle* lcMainBundle; +NSDictionary* guestAppInfo; void NUDGuestHooksInit(); @@ -43,6 +44,9 @@ + (NSString *)lcAppUrlScheme { + (NSBundle *)lcMainBundle { return lcMainBundle; } ++ (NSDictionary *)guestAppInfo { + return guestAppInfo; +} @end static BOOL checkJITEnabled() { @@ -196,14 +200,16 @@ static void overwriteExecPath(NSString *bundlePath) { } uint32_t appMainImageIndex = 0; +void* appExecutableHandle = 0; void* (*orig_dlsym)(void * __handle, const char * __symbol); void* new_dlsym(void * __handle, const char * __symbol) { if(__handle == (void*)RTLD_MAIN_ONLY) { if(strcmp(__symbol, MH_EXECUTE_SYM) == 0) { return (void*)_dyld_get_image_header(appMainImageIndex); } - return orig_dlsym(RTLD_DEFAULT, __symbol); + return orig_dlsym(appExecutableHandle, __symbol); } + return orig_dlsym(__handle, __symbol); } @@ -238,6 +244,7 @@ static void overwriteExecPath(NSString *bundlePath) { appBundle = [[NSBundle alloc] initWithPath:bundlePath]; isSharedBundle = true; } + guestAppInfo = [NSDictionary dictionaryWithContentsOfURL:[appBundle URLForResource:@"LCAppInfo" withExtension:@"plist"]]; if(!appBundle) { return @"App not found"; @@ -246,7 +253,7 @@ static void overwriteExecPath(NSString *bundlePath) { // find container in Info.plist NSString* dataUUID = selectedContainer; if(!dataUUID) { - dataUUID = appBundle.infoDictionary[@"LCDataUUID"]; + dataUUID = guestAppInfo[@"LCDataUUID"]; } if(dataUUID == nil) { @@ -299,8 +306,8 @@ static void overwriteExecPath(NSString *bundlePath) { overwriteExecPath(appBundle.bundlePath); // Overwrite NSUserDefaults - if([appBundle.infoDictionary[@"doUseLCBundleId"] boolValue]) { - NSUserDefaults.standardUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:appBundle.infoDictionary[@"LCOrignalBundleIdentifier"]]; + if([guestAppInfo[@"doUseLCBundleId"] boolValue]) { + NSUserDefaults.standardUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:guestAppInfo[@"LCOrignalBundleIdentifier"]]; } else { NSUserDefaults.standardUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:appBundle.bundleIdentifier]; } @@ -324,7 +331,7 @@ static void overwriteExecPath(NSString *bundlePath) { remove(newTmpPath.UTF8String); symlink(getenv("TMPDIR"), newTmpPath.UTF8String); - if([appBundle.infoDictionary[@"doSymlinkInbox"] boolValue]) { + if([guestAppInfo[@"doSymlinkInbox"] boolValue]) { NSString* inboxSymlinkPath = [NSString stringWithFormat:@"%s/%@-Inbox", getenv("TMPDIR"), [appBundle bundleIdentifier]]; NSString* inboxPath = [newHomePath stringByAppendingPathComponent:@"Inbox"]; @@ -354,6 +361,11 @@ static void overwriteExecPath(NSString *bundlePath) { } } + + if([guestAppInfo[@"fixBlackScreen"] boolValue]) { + dlopen("/System/Library/Frameworks/UIKit.framework/UIKit", RTLD_GLOBAL); + NSLog(@"[LC] Fix BlackScreen2 %@", [NSClassFromString(@"UIScreen") mainScreen]); + } setenv("CFFIXED_USER_HOME", newHomePath.UTF8String, 1); setenv("HOME", newHomePath.UTF8String, 1); @@ -394,8 +406,10 @@ static void overwriteExecPath(NSString *bundlePath) { uint32_t appIndex = _dyld_image_count(); appMainImageIndex = appIndex; void *appHandle = dlopen(*path, RTLD_LAZY|RTLD_GLOBAL|RTLD_FIRST); + appExecutableHandle = appHandle; const char *dlerr = dlerror(); - if (!appHandle || (uint64_t)appHandle > 0xf00000000000 || dlerr) { + + if (!appHandle || (uint64_t)appHandle > 0xf00000000000 || (dlerr && ![guestAppInfo[@"ignoreDlopenError"] boolValue]) ) { if (dlerr) { appError = @(dlerr); } else { @@ -443,7 +457,7 @@ static void exceptionHandler(NSException *exception) { int LiveContainerMain(int argc, char *argv[]) { // This strangely fixes some apps getting stuck on black screen - NSLog(@"Ignore this: %@", UIScreen.mainScreen); + NSLog(@"Ignore this: %@", dispatch_get_main_queue()); lcMainBundle = [NSBundle mainBundle]; lcUserDefaults = NSUserDefaults.standardUserDefaults; @@ -493,8 +507,8 @@ int LiveContainerMain(int argc, char *argv[]) { } NSURL* url = [NSURL URLWithString:urlStr]; - if([[UIApplication sharedApplication] canOpenURL:url]){ - [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil]; + if([[NSClassFromString(@"UIApplication") sharedApplication] canOpenURL:url]){ + [[NSClassFromString(@"UIApplication") sharedApplication] openURL:url options:@{} completionHandler:nil]; NSString *launchUrl = [lcUserDefaults stringForKey:@"launchAppUrlScheme"]; // also pass url scheme to another lc @@ -508,7 +522,7 @@ int LiveContainerMain(int argc, char *argv[]) { NSString* finalUrl = [NSString stringWithFormat:@"%@://open-url?url=%@", runningLC, encodedUrl]; NSURL* url = [NSURL URLWithString: finalUrl]; - [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil]; + [[NSClassFromString(@"UIApplication") sharedApplication] openURL:url options:@{} completionHandler:nil]; } } else { @@ -536,7 +550,7 @@ int LiveContainerMain(int argc, char *argv[]) { NSString* finalUrl = [NSString stringWithFormat:@"%@://open-url?url=%@", lcAppUrlScheme, encodedUrl]; NSURL* url = [NSURL URLWithString: finalUrl]; - [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil]; + [[NSClassFromString(@"UIApplication") sharedApplication] openURL:url options:@{} completionHandler:nil]; }); } NSSetUncaughtExceptionHandler(&exceptionHandler); @@ -565,6 +579,9 @@ int LiveContainerMain(int argc, char *argv[]) { if ([lcUserDefaults boolForKey:@"LCLoadTweaksToSelf"]) { dlopen("@executable_path/Frameworks/TweakLoader.dylib", RTLD_LAZY); } + + void *uikitHandle = dlopen("/System/Library/Frameworks/UIKit.framework/UIKit", RTLD_GLOBAL); + int (*UIApplicationMain)(int, char**, NSString *, NSString *) = dlsym(uikitHandle, "UIApplicationMain"); return UIApplicationMain(argc, argv, nil, @"LiveContainerSwiftUI.AppDelegate"); } }