diff --git a/LCSharedUtils.m b/LCSharedUtils.m index f670d0f..c098976 100644 --- a/LCSharedUtils.m +++ b/LCSharedUtils.m @@ -39,7 +39,7 @@ + (BOOL)launchToGuestApp { if (!access(tsPath.UTF8String, F_OK)) { urlScheme = @"apple-magnifier://enable-jit?bundle-id=%@"; } else if (self.certificatePassword) { - tries = 8; + tries = 2; urlScheme = [NSString stringWithFormat:@"%@://livecontainer-relaunch", lcAppUrlScheme]; } else { urlScheme = @"sidestore://sidejit-enable?bid=%@"; diff --git a/LiveContainerSwiftUI/LCAppListView.swift b/LiveContainerSwiftUI/LCAppListView.swift index 1d0adce..555b9a7 100644 --- a/LiveContainerSwiftUI/LCAppListView.swift +++ b/LiveContainerSwiftUI/LCAppListView.swift @@ -388,9 +388,29 @@ struct LCAppListView : View, LCAppBannerDelegate, LCAppModelDelegate { var outputFolder = LCPath.bundlePath.appendingPathComponent(appRelativePath) var appToReplace : LCAppModel? = nil // Folder exist! show alert for user to choose which bundle to replace - let sameBundleIdApp = self.apps.filter { app in + var sameBundleIdApp = self.apps.filter { app in return app.appInfo.bundleIdentifier()! == newAppInfo.bundleIdentifier() } + if sameBundleIdApp.count == 0 { + sameBundleIdApp = self.hiddenApps.filter { app in + return app.appInfo.bundleIdentifier()! == newAppInfo.bundleIdentifier() + } + + // we found a hidden app, we need to authenticate before proceeding + if sameBundleIdApp.count > 0 && !sharedModel.isHiddenAppUnlocked { + do { + if !(try await LCUtils.authenticateUser()) { + return + } + } catch { + errorInfo = error.localizedDescription + errorShow = true + return + } + } + + } + if fm.fileExists(atPath: outputFolder.path) || sameBundleIdApp.count > 0 { appRelativePath = "\(newAppInfo.bundleIdentifier()!)_\(Int(CFAbsoluteTimeGetCurrent())).app" @@ -407,13 +427,15 @@ struct LCAppListView : View, LCAppBannerDelegate, LCAppModelDelegate { return } - outputFolder = LCPath.bundlePath.appendingPathComponent(installOptionChosen.nameOfFolderToInstall) + if let appToReplace = installOptionChosen.appToReplace, appToReplace.uiIsShared { + outputFolder = LCPath.lcGroupBundlePath.appendingPathComponent(installOptionChosen.nameOfFolderToInstall) + } else { + outputFolder = LCPath.bundlePath.appendingPathComponent(installOptionChosen.nameOfFolderToInstall) + } + appRelativePath = installOptionChosen.nameOfFolderToInstall appToReplace = installOptionChosen.appToReplace if installOptionChosen.isReplace { try fm.removeItem(at: outputFolder) - self.apps.removeAll { appNow in - return appNow.appInfo.relativeBundlePath == installOptionChosen.nameOfFolderToInstall - } } } // Move it! @@ -440,12 +462,36 @@ struct LCAppListView : View, LCAppBannerDelegate, LCAppModelDelegate { if let signError { throw signError } - // set data folder to the folder of the chosen app - if let appToReplace = appToReplace { + + if let appToReplace { + // copy previous configration to new app + finalNewApp.isLocked = appToReplace.appInfo.isLocked + finalNewApp.isHidden = appToReplace.appInfo.isHidden + finalNewApp.isJITNeeded = appToReplace.appInfo.isJITNeeded + finalNewApp.isShared = appToReplace.appInfo.isShared + finalNewApp.bypassAssertBarrierOnQueue = appToReplace.appInfo.bypassAssertBarrierOnQueue + finalNewApp.doSymlinkInbox = appToReplace.appInfo.doSymlinkInbox finalNewApp.setDataUUID(appToReplace.appInfo.getDataUUIDNoAssign()) + finalNewApp.setTweakFolder(appToReplace.appInfo.tweakFolder()) } DispatchQueue.main.async { - self.apps.append(LCAppModel(appInfo: finalNewApp)) + if let appToReplace { + if appToReplace.uiIsHidden { + self.hiddenApps.removeAll { appNow in + return appNow == appToReplace + } + self.hiddenApps.append(LCAppModel(appInfo: finalNewApp, delegate: self)) + } else { + self.apps.removeAll { appNow in + return appNow == appToReplace + } + self.apps.append(LCAppModel(appInfo: finalNewApp, delegate: self)) + } + + } else { + self.apps.append(LCAppModel(appInfo: finalNewApp, delegate: self)) + } + self.installprogressVisible = false } } diff --git a/LiveContainerSwiftUI/LCSettingsView.swift b/LiveContainerSwiftUI/LCSettingsView.swift index 7bd645a..8a3feeb 100644 --- a/LiveContainerSwiftUI/LCSettingsView.swift +++ b/LiveContainerSwiftUI/LCSettingsView.swift @@ -286,7 +286,7 @@ struct LCSettingsView: View { } } message: { if folderRemoveCount > 0 { - Text("lc.settings.cleanDataFolderConfirm".localizeWithFormat(folderRemoveCount)) + Text("lc.settings.cleanDataFolderConfirm %lld".localizeWithFormat(folderRemoveCount)) } else { Text("lc.settings.noDataFolderToClean".loc) } diff --git a/LiveContainerUI/LCAppInfo.m b/LiveContainerUI/LCAppInfo.m index e5daf29..7a0b0bf 100644 --- a/LiveContainerUI/LCAppInfo.m +++ b/LiveContainerUI/LCAppInfo.m @@ -184,7 +184,7 @@ - (void)preprocessBundleBeforeSiging:(NSURL *)bundleURL completion:(dispatch_blo - (void)patchExecAndSignIfNeedWithCompletionHandler:(void(^)(NSString* errorInfo))completetionHandler progressHandler:(void(^)(NSProgress* errorInfo))progressHandler forceSign:(BOOL)forceSign { NSString *appPath = self.bundlePath; NSString *infoPath = [NSString stringWithFormat:@"%@/Info.plist", appPath]; - NSMutableDictionary *info = [NSMutableDictionary dictionaryWithContentsOfFile:infoPath]; + NSMutableDictionary *info = _info; if (!info) { completetionHandler(@"Info.plist not found"); return; @@ -255,7 +255,7 @@ - (void)patchExecAndSignIfNeedWithCompletionHandler:(void(^)(NSString* errorInfo [NSFileManager.defaultManager removeItemAtPath:tmpExecPath error:nil]; // Save sign ID and restore bundle ID - [info writeToFile:infoPath atomically:YES]; + [self save]; if(error) { diff --git a/README.md b/README.md index 010cea5..40796e2 100644 --- a/README.md +++ b/README.md @@ -106,4 +106,4 @@ To install tweaks, you can use the built-in tweak manager in LiveContainer, whic - @hugeBlack for SwiftUI contribution - @Staubgeborener for automatic AltStore/SideStore source updater - @fkunn1326 for improved app hiding -- @slds1 for dynamic color frature +- @slds1 for dynamic color feature diff --git a/TPRO.h b/TPRO.h new file mode 100644 index 0000000..b2ea4dd --- /dev/null +++ b/TPRO.h @@ -0,0 +1,32 @@ +// by khanhduytran0 +#define _COMM_PAGE_START_ADDRESS (0x0000000FFFFFC000ULL) +//#define _COMM_PAGE_TPRO_SUPPORT (_COMM_PAGE_START_ADDRESS + ????) +#define _COMM_PAGE_TPRO_WRITE_ENABLE (_COMM_PAGE_START_ADDRESS + 0x0D0) +//#define _COMM_PAGE_TPRO_WRITE_DISABLE (_COMM_PAGE_START_ADDRESS + 0x0D8) + +static inline bool os_thread_self_restrict_tpro_to_rw() { + if (!*(uint64_t*)_COMM_PAGE_TPRO_WRITE_ENABLE) { + // Doesn't have TPRO, skip this + return false; + } + __asm__ __volatile__ ( + "mov x0, %0\n" + "ldr x0, [x0]\n" + "msr s3_6_c15_c1_5, x0\n" + "isb sy\n" + :: "r" (_COMM_PAGE_TPRO_WRITE_ENABLE) + : "memory", "x0" + ); + return true; +} + +/* +inline uint64_t sprr_read() { + uint64_t v; + __asm__ __volatile__( + "isb sy\n" + "mrs %0, s3_6_c15_c1_5\n" + : "=r"(v)::"memory"); + return v; +} +*/ diff --git a/TweakLoader/TweakLoader.m b/TweakLoader/TweakLoader.m index 9c95cfb..2697459 100644 --- a/TweakLoader/TweakLoader.m +++ b/TweakLoader/TweakLoader.m @@ -94,3 +94,23 @@ static void TweakLoaderConstructor() { }); } } + +// 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; +} diff --git a/TweakLoader/UIKit+GuestHooks.m b/TweakLoader/UIKit+GuestHooks.m index e0d8be5..48259fc 100644 --- a/TweakLoader/UIKit+GuestHooks.m +++ b/TweakLoader/UIKit+GuestHooks.m @@ -85,7 +85,7 @@ void LCOpenWebPage(NSString* webPageUrlString, NSString* originalUrl) { NSString *message = @"lc.guestTweak.openWebPageTip".loc; UIWindow *window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds]; UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"LiveContainer" message:message preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction* okAction = [UIAlertAction actionWithTitle:@"lc.common.ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { + UIAlertAction* okAction = [UIAlertAction actionWithTitle:@"lc.common.ok".loc style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [NSClassFromString(@"LCSharedUtils") setWebPageUrlForNextLaunch:webPageUrlString]; [NSClassFromString(@"LCSharedUtils") launchToGuestApp]; }]; diff --git a/control b/control index 1ce766b..846835f 100644 --- a/control +++ b/control @@ -1,6 +1,6 @@ Package: com.kdt.livecontainer Name: livecontainer -Version: 2.1.2 +Version: 3.0.0 Architecture: iphoneos-arm Description: Run iOS app without actually installing it! Maintainer: khanhduytran0 diff --git a/main.m b/main.m index 3256c4f..a37d39b 100644 --- a/main.m +++ b/main.m @@ -14,6 +14,7 @@ #include #include #include +#include "TPRO.h" static int (*appMain)(int, char**); static const char *dyldImageName; @@ -124,13 +125,17 @@ static void overwriteExecPath_handler(int signum, siginfo_t* siginfo, void* cont size_t newLen = strlen(newPath); // Check if it's long enough... assert(maxLen >= newLen); - - // Make it RW and overwrite now - kern_return_t ret = builtin_vm_protect(mach_task_self(), (mach_vm_address_t)path, maxLen, false, PROT_READ | PROT_WRITE); - if (ret != KERN_SUCCESS) { - ret = builtin_vm_protect(mach_task_self(), (mach_vm_address_t)path, maxLen, false, PROT_READ | PROT_WRITE | VM_PROT_COPY); + + // if we don't have TPRO, we will use the old way + if(!os_thread_self_restrict_tpro_to_rw()) { + // Make it RW and overwrite now + kern_return_t ret = builtin_vm_protect(mach_task_self(), (mach_vm_address_t)path, maxLen, false, PROT_READ | PROT_WRITE); + if (ret != KERN_SUCCESS) { + ret = builtin_vm_protect(mach_task_self(), (mach_vm_address_t)path, maxLen, false, PROT_READ | PROT_WRITE | VM_PROT_COPY); + } + assert(ret == KERN_SUCCESS); } - assert(ret == KERN_SUCCESS); + bzero(path, maxLen); strncpy(path, newPath, newLen); } @@ -157,6 +162,7 @@ static void overwriteExecPath(NSString *bundlePath) { char currPath[PATH_MAX]; uint32_t len = PATH_MAX; _NSGetExecutablePath(currPath, &len); + if (strncmp(currPath, newPath, newLen)) { struct sigaction sa, saOld; sa.sa_sigaction = overwriteExecPath_handler; @@ -395,9 +401,9 @@ static void overwriteExecPath(NSString *bundlePath) { // Go! NSLog(@"[LCBootstrap] jumping to main %p", appMain); argv[0] = (char *)appExecPath; - appMain(argc, argv); + int ret = appMain(argc, argv); - return nil; + return [NSString stringWithFormat:@"App returned from its main function with code %d.", ret]; } static void exceptionHandler(NSException *exception) {