diff --git a/Helper/control b/Helper/control index ad89a46..d89e573 100644 --- a/Helper/control +++ b/Helper/control @@ -1,6 +1,6 @@ Package: com.opa334.trollstoreroothelper Name: trollstoreroothelper -Version: 1.0.1 +Version: 1.0.3 Architecture: iphoneos-arm Description: An awesome tool of some sort!! Maintainer: opa334 diff --git a/Helper/main.m b/Helper/main.m index 2daa446..75b9ef0 100644 --- a/Helper/main.m +++ b/Helper/main.m @@ -8,9 +8,54 @@ #import #import "CoreServices.h" #import "Shared.h" +#import +#import +#import +#import +#import +#import +#import +#import +#import #import +#ifdef __LP64__ +#define segment_command_universal segment_command_64 +#define mach_header_universal mach_header_64 +#define MH_MAGIC_UNIVERSAL MH_MAGIC_64 +#define MH_CIGAM_UNIVERSAL MH_CIGAM_64 +#else +#define segment_command_universal segment_command +#define mach_header_universal mach_header +#define MH_MAGIC_UNIVERSAL MH_MAGIC +#define MH_CIGAM_UNIVERSAL MH_CIGAM +#endif + +#define SWAP32(x) ((((x) & 0xff000000) >> 24) | (((x) & 0xff0000) >> 8) | (((x) & 0xff00) << 8) | (((x) & 0xff) << 24)) +uint32_t s32(uint32_t toSwap, BOOL shouldSwap) +{ + return shouldSwap ? SWAP32(toSwap) : toSwap; +} + +#define CPU_SUBTYPE_ARM64E_NEW_ABI 0x80000002 + +struct CSSuperBlob { + uint32_t magic; + uint32_t length; + uint32_t count; +}; + +struct CSBlob { + uint32_t type; + uint32_t offset; +}; + +#define CS_MAGIC_EMBEDDED_SIGNATURE 0xfade0cc0 +#define CS_MAGIC_EMBEDDED_SIGNATURE_REVERSED 0xc00cdefa +#define CS_MAGIC_EMBEDDED_ENTITLEMENTS 0xfade7171 + + extern mach_msg_return_t SBReloadIconForIdentifier(mach_port_t machport, const char* identifier); @interface SBSHomeScreenService : NSObject - (void)reloadIcons; @@ -176,28 +221,111 @@ int runLdid(NSArray* args, NSString** output, NSString** errorOutput) return WEXITSTATUS(status); } -NSString* dumpEntitlements(NSString* binaryPath) +NSDictionary* dumpEntitlements(NSString* binaryPath) { - NSString* output; - NSString* errorOutput; + char* entitlementsData = NULL; + uint32_t entitlementsLength = 0; - int ldidRet = runLdid(@[@"-e", binaryPath], &output, &errorOutput); + FILE* machoFile = fopen(binaryPath.UTF8String, "rb"); + struct mach_header_universal header; + fread(&header,sizeof(header),1,machoFile); - NSLog(@"entitlements dump exited with status %d", ldidRet); + if(header.magic == FAT_MAGIC || header.magic == FAT_CIGAM) + { + fseek(machoFile,0,SEEK_SET); + + struct fat_header fatHeader; + fread(&fatHeader,sizeof(fatHeader),1,machoFile); + + BOOL swpFat = fatHeader.magic == FAT_CIGAM; + + for(int i = 0; i < s32(fatHeader.nfat_arch, swpFat); i++) + { + struct fat_arch fatArch; + fseek(machoFile,sizeof(fatHeader) + sizeof(fatArch) * i,SEEK_SET); + fread(&fatArch,sizeof(fatArch),1,machoFile); + + if(s32(fatArch.cputype, swpFat) != CPU_TYPE_ARM64) + { + continue; + } + + fseek(machoFile,s32(fatArch.offset, swpFat),SEEK_SET); + struct mach_header_universal header; + fread(&header,sizeof(header),1,machoFile); + + BOOL swp = header.magic == MH_CIGAM_UNIVERSAL; + + // This code is cursed, don't stare at it too long or it will stare back at you + uint32_t offset = s32(fatArch.offset, swpFat) + sizeof(header); + for(int c = 0; c < s32(header.ncmds, swp); c++) + { + fseek(machoFile,offset,SEEK_SET); + struct load_command cmd; + fread(&cmd,sizeof(cmd),1,machoFile); + uint32_t normalizedCmd = s32(cmd.cmd,swp); + if(normalizedCmd == LC_CODE_SIGNATURE) + { + struct linkedit_data_command codeSignCommand; + fseek(machoFile,offset,SEEK_SET); + fread(&codeSignCommand,sizeof(codeSignCommand),1,machoFile); + uint32_t codeSignCmdOffset = s32(fatArch.offset, swpFat) + s32(codeSignCommand.dataoff, swp); + fseek(machoFile, codeSignCmdOffset, SEEK_SET); + struct CSSuperBlob superBlob; + fread(&superBlob, sizeof(superBlob), 1, machoFile); + if(SWAP32(superBlob.magic) == CS_MAGIC_EMBEDDED_SIGNATURE) + { + uint32_t itemCount = SWAP32(superBlob.count); + for(int i = 0; i < itemCount; i++) + { + fseek(machoFile, codeSignCmdOffset + sizeof(superBlob) + i * sizeof(struct CSBlob),SEEK_SET); + struct CSBlob blob; + fread(&blob, sizeof(struct CSBlob), 1, machoFile); + fseek(machoFile, codeSignCmdOffset + SWAP32(blob.offset),SEEK_SET); + uint32_t blobMagic; + fread(&blobMagic, sizeof(uint32_t), 1, machoFile); + if(SWAP32(blobMagic) == CS_MAGIC_EMBEDDED_ENTITLEMENTS) + { + uint32_t entitlementsLengthTmp; + fread(&entitlementsLengthTmp, sizeof(uint32_t), 1, machoFile); + entitlementsLength = SWAP32(entitlementsLengthTmp); + entitlementsData = malloc(entitlementsLength - 8); + fread(&entitlementsData[0], entitlementsLength - 8, 1, machoFile); + break; + } + } + } + + break; + } + + offset += cmd.cmdsize; + } + } + } + + fclose(machoFile); + + NSData* entitlementsNSData = nil; + + if(entitlementsData) + { + entitlementsNSData = [NSData dataWithBytes:entitlementsData length:entitlementsLength]; + free(entitlementsData); + } + + if(entitlementsNSData) + { + NSDictionary* plist = [NSPropertyListSerialization propertyListWithData:entitlementsNSData options:NSPropertyListImmutable format:nil error:nil]; + NSLog(@"%@ dumped entitlements %@", binaryPath, plist); + return plist; + } + else + { + NSLog(@"Failed to dump entitlements of %@... This is bad", binaryPath); + } - NSLog(@"- dump error output start -"); - - printMultilineNSString(errorOutput); - - NSLog(@"- dump error output end -"); - - NSLog(@"- dumped entitlements output start -"); - - printMultilineNSString(output); - - NSLog(@"- dumped entitlements output end -"); - - return output; + return nil; } BOOL signApp(NSString* appPath, NSError** error) @@ -217,8 +345,8 @@ BOOL signApp(NSString* appPath, NSError** error) NSString* errorOutput; int ldidRet; - NSString* entitlements = dumpEntitlements(executablePath); - if(entitlements.length == 0) + NSDictionary* entitlements = dumpEntitlements(executablePath); + if(!entitlements) { NSLog(@"app main binary has no entitlements, signing app with fallback entitlements..."); // app has no entitlements, sign with fallback entitlements @@ -229,7 +357,7 @@ BOOL signApp(NSString* appPath, NSError** error) else { // app has entitlements, keep them - ldidRet = runLdid(@[@"-S", @"-M", certArg, appPath], nil, &errorOutput); + ldidRet = runLdid(@[@"-s", certArg, appPath], nil, &errorOutput); } NSLog(@"ldid exited with status %d", ldidRet); @@ -246,8 +374,10 @@ BOOL signApp(NSString* appPath, NSError** error) // 170: failed to create container for app bundle // 171: a non trollstore app with the same identifier is already installled // 172: no info.plist found in app -int installApp(NSString* appPath, BOOL sign, NSError** error) +int installApp(NSString* appPath, BOOL sign, BOOL force, NSError** error) { + NSLog(@"[installApp force = %d]", force); + NSString* appId = appIdForAppPath(appPath); if(!appId) return 172; @@ -281,12 +411,15 @@ int installApp(NSString* appPath, BOOL sign, NSError** error) // Make sure there isn't already an app store app installed with the same identifier NSURL* trollStoreMarkURL = [appContainer.url URLByAppendingPathComponent:@"_TrollStore"]; - if(existed && !isEmpty && ![trollStoreMarkURL checkResourceIsReachableAndReturnError:nil]) + if(existed && !isEmpty && ![trollStoreMarkURL checkResourceIsReachableAndReturnError:nil] && !force) { NSLog(@"[installApp] already installed and not a TrollStore app... bailing out"); return 171; } + // Mark app as TrollStore app + [[NSFileManager defaultManager] createFileAtPath:trollStoreMarkURL.path contents:[NSData data] attributes:nil]; + // Apply correct permissions NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:[NSURL fileURLWithPath:appPath] includingPropertiesForKeys:nil options:0 errorHandler:nil]; NSURL* fileURL; @@ -342,7 +475,7 @@ int installApp(NSString* appPath, BOOL sign, NSError** error) while(fileURL = [enumerator nextObject]) { // do not under any circumstance delete this file as it makes iOS loose the app registration - if([fileURL.lastPathComponent isEqualToString:@".com.apple.mobile_container_manager.metadata.plist"]) + if([fileURL.lastPathComponent isEqualToString:@".com.apple.mobile_container_manager.metadata.plist"] || [fileURL.lastPathComponent isEqualToString:@"_TrollStore"]) { NSLog(@"[installApp] skip removal of %@", fileURL); continue; @@ -359,9 +492,6 @@ int installApp(NSString* appPath, BOOL sign, NSError** error) BOOL suc = [[NSFileManager defaultManager] copyItemAtPath:appPath toPath:newAppPath error:error]; if(suc) { - // Mark app as TrollStore app - [[NSFileManager defaultManager] createFileAtPath:trollStoreMarkURL.path contents:[NSData data] attributes:nil]; - NSLog(@"[installApp] app installed, adding to icon cache now..."); registerPath((char*)newAppPath.UTF8String, 0); return 0; @@ -382,6 +512,7 @@ int uninstallApp(NSString* appId, NSError** error) MCMContainer *appContainer = [objc_getClass("MCMAppDataContainer") containerWithIdentifier:appId createIfNecessary:NO existed:nil error:nil]; + NSLog(@"1"); NSString *containerPath = [appContainer url].path; if(containerPath) { @@ -391,11 +522,11 @@ int uninstallApp(NSString* appId, NSError** error) } // delete group container paths - for(NSURL* groupURL in [appProxy groupContainerURLs]) + [[appProxy groupContainerURLs] enumerateKeysAndObjectsUsingBlock:^(NSString* groupID, NSURL* groupURL, BOOL* stop) { - [[NSFileManager defaultManager] removeItemAtPath:groupURL.path error:error]; - NSLog(@"deleting %@", groupURL.path); - } + [[NSFileManager defaultManager] removeItemAtURL:groupURL error:nil]; + NSLog(@"deleting %@", groupURL); + }]; // delete app plugin paths for(LSPlugInKitProxy* pluginProxy in appProxy.plugInKitPlugins) @@ -427,7 +558,7 @@ int uninstallApp(NSString* appId, NSError** error) // 166: IPA does not exist or is not accessible // 167: IPA does not appear to contain an app -int installIpa(NSString* ipaPath, NSError** error) +int installIpa(NSString* ipaPath, BOOL force, NSError** error) { if(![[NSFileManager defaultManager] fileExistsAtPath:ipaPath]) return 166; @@ -455,7 +586,7 @@ int installIpa(NSString* ipaPath, NSError** error) } if(!tmpAppPath) return 167; - int ret = installApp(tmpAppPath, YES, error); + int ret = installApp(tmpAppPath, YES, force, error); [[NSFileManager defaultManager] removeItemAtPath:tmpAppPath error:nil]; @@ -530,7 +661,7 @@ BOOL installTrollStore(NSString* pathToTar) _installPersistenceHelper(persistenceHelperApp, trollStorePersistenceHelper, trollStoreRootHelper); } - return installApp(tmpTrollStore, NO, nil);; + return installApp(tmpTrollStore, NO, YES, nil);; } void refreshAppRegistrations() @@ -663,9 +794,19 @@ int main(int argc, char *argv[], char *envp[]) { NSString* cmd = [NSString stringWithUTF8String:argv[1]]; if([cmd isEqualToString:@"install"]) { + NSLog(@"argc = %d", argc); + BOOL force = NO; if(argc <= 2) return -3; + if(argc > 3) + { + NSLog(@"argv3 = %s", argv[3]); + if(!strcmp(argv[3], "force")) + { + force = YES; + } + } NSString* ipaPath = [NSString stringWithUTF8String:argv[2]]; - ret = installIpa(ipaPath, &error); + ret = installIpa(ipaPath, force, &error); } else if([cmd isEqualToString:@"uninstall"]) { if(argc <= 2) return -3; diff --git a/Helper/uicache.m b/Helper/uicache.m index f5fbb27..1a2f8d9 100644 --- a/Helper/uicache.m +++ b/Helper/uicache.m @@ -4,89 +4,247 @@ #import #import "dlfcn.h" -void registerPath(char *path, int unregister) +// uicache on steroids + +extern NSDictionary* dumpEntitlements(NSString* binaryPath); + +NSDictionary* constructGroupsContainersForEntitlements(NSDictionary* entitlements, BOOL systemGroups) { - if(!path) return; + if(!entitlements) return nil; - LSApplicationWorkspace *workspace = - [LSApplicationWorkspace defaultWorkspace]; - if (unregister && ![[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithUTF8String:path]]) { - LSApplicationProxy *app = [LSApplicationProxy - applicationProxyForIdentifier:[NSString stringWithUTF8String:path]]; - if (app.bundleURL) - path = (char *)[[app bundleURL] fileSystemRepresentation]; - } + NSString* entitlementForGroups; + NSString* mcmClass; + if(systemGroups) + { + entitlementForGroups = @"com.apple.security.system-groups"; + mcmClass = @"MCMSystemDataContainer"; + } + else + { + entitlementForGroups = @"com.apple.security.application-groups"; + mcmClass = @"MCMSharedDataContainer"; + } - NSString *rawPath = [NSString stringWithUTF8String:path]; - rawPath = [rawPath stringByResolvingSymlinksInPath]; + NSArray* groupIDs = entitlements[entitlementForGroups]; + if(groupIDs && [groupIDs isKindOfClass:[NSArray class]]) + { + NSMutableDictionary* groupContainers = [NSMutableDictionary new]; - NSDictionary *infoPlist = [NSDictionary - dictionaryWithContentsOfFile: - [rawPath stringByAppendingPathComponent:@"Info.plist"]]; - NSString *bundleID = [infoPlist objectForKey:@"CFBundleIdentifier"]; + for(NSString* groupID in groupIDs) + { + MCMContainer* container = [NSClassFromString(mcmClass) containerWithIdentifier:groupID createIfNecessary:YES existed:nil error:nil]; + if(container.url) + { + groupContainers[groupID] = container.url.path; + } + } - NSURL *url = [NSURL fileURLWithPath:rawPath]; + return groupContainers.copy; + } - if (bundleID && !unregister) { - MCMContainer *appContainer = [objc_getClass("MCMAppDataContainer") - containerWithIdentifier:bundleID - createIfNecessary:YES - existed:nil - error:nil]; - NSString *containerPath = [appContainer url].path; + return nil; +} - NSMutableDictionary *plist = [NSMutableDictionary dictionary]; - [plist setObject:@"System" forKey:@"ApplicationType"]; - [plist setObject:@1 forKey:@"BundleNameIsLocalized"]; - [plist setObject:bundleID forKey:@"CFBundleIdentifier"]; - [plist setObject:@0 forKey:@"CompatibilityState"]; - if (containerPath) [plist setObject:containerPath forKey:@"Container"]; - [plist setObject:@0 forKey:@"IsDeletable"]; - [plist setObject:rawPath forKey:@"Path"]; +BOOL constructContainerizationForEntitlements(NSDictionary* entitlements) +{ + NSNumber* noContainer = entitlements[@"com.apple.private.security.no-container"]; + if(noContainer && [noContainer isKindOfClass:[NSNumber class]]) + { + if(noContainer.boolValue) + { + return NO; + } + } - NSString *pluginsPath = - [rawPath stringByAppendingPathComponent:@"PlugIns"]; - NSArray *plugins = [[NSFileManager defaultManager] - contentsOfDirectoryAtPath:pluginsPath - error:nil]; + NSNumber* containerRequired = entitlements[@"com.apple.private.security.container-required"]; + if(containerRequired && [containerRequired isKindOfClass:[NSNumber class]]) + { + if(!containerRequired.boolValue) + { + return NO; + } + } - NSMutableDictionary *bundlePlugins = [NSMutableDictionary dictionary]; - for (NSString *pluginName in plugins) { - NSString *fullPath = - [pluginsPath stringByAppendingPathComponent:pluginName]; + return YES; +} - NSDictionary *infoPlist = [NSDictionary - dictionaryWithContentsOfFile: - [fullPath stringByAppendingPathComponent:@"Info.plist"]]; - NSString *pluginBundleID = - [infoPlist objectForKey:@"CFBundleIdentifier"]; - if (!pluginBundleID) continue; - MCMContainer *pluginContainer = - [objc_getClass("MCMPluginKitPluginDataContainer") - containerWithIdentifier:pluginBundleID - createIfNecessary:YES - existed:nil - error:nil]; - NSString *pluginContainerPath = [pluginContainer url].path; +NSString* constructTeamIdentifierForEntitlements(NSDictionary* entitlements) +{ + NSString* teamIdentifier = entitlements[@"com.apple.developer.team-identifier"]; + if(teamIdentifier && [teamIdentifier isKindOfClass:[NSString class]]) + { + return teamIdentifier; + } + return nil; +} - NSMutableDictionary *pluginPlist = [NSMutableDictionary dictionary]; - [pluginPlist setObject:@"PluginKitPlugin" - forKey:@"ApplicationType"]; - [pluginPlist setObject:@1 forKey:@"BundleNameIsLocalized"]; - [pluginPlist setObject:pluginBundleID forKey:@"CFBundleIdentifier"]; - [pluginPlist setObject:@0 forKey:@"CompatibilityState"]; - [pluginPlist setObject:pluginContainerPath forKey:@"Container"]; - [pluginPlist setObject:fullPath forKey:@"Path"]; - [pluginPlist setObject:bundleID forKey:@"PluginOwnerBundleID"]; - [bundlePlugins setObject:pluginPlist forKey:pluginBundleID]; - } - [plist setObject:bundlePlugins forKey:@"_LSBundlePlugins"]; - if (![workspace registerApplicationDictionary:plist]) { - fprintf(stderr, "Error: Unable to register %s\n", path); - } - } else { - if (![workspace unregisterApplication:url]) { - fprintf(stderr, "Error: Unable to unregister %s\n", path); - } - } +NSDictionary* constructEnvironmentVariablesForContainerPath(NSString* containerPath) +{ + NSString* tmpDir = [containerPath stringByAppendingPathComponent:@"tmp"]; + return @{ + @"CFFIXED_USER_HOME" : containerPath, + @"HOME" : containerPath, + @"TMPDIR" : tmpDir + }; +} + +void registerPath(char* cPath, int unregister) +{ + if(!cPath) return; + NSString* path = [NSString stringWithUTF8String:cPath]; + + LSApplicationWorkspace* workspace = [LSApplicationWorkspace defaultWorkspace]; + if(unregister && ![[NSFileManager defaultManager] fileExistsAtPath:path]) + { + LSApplicationProxy* app = [LSApplicationProxy applicationProxyForIdentifier:path]; + if(app.bundleURL) + { + path = [app bundleURL].path; + } + } + + path = [path stringByResolvingSymlinksInPath]; + + NSDictionary* appInfoPlist = [NSDictionary dictionaryWithContentsOfFile:[path stringByAppendingPathComponent:@"Info.plist"]]; + NSString* appBundleID = [appInfoPlist objectForKey:@"CFBundleIdentifier"]; + + if(appBundleID && !unregister) + { + MCMContainer* appContainer = [NSClassFromString(@"MCMAppDataContainer") containerWithIdentifier:appBundleID createIfNecessary:YES existed:nil error:nil]; + NSString* containerPath = [appContainer url].path; + + NSMutableDictionary* dictToRegister = [NSMutableDictionary dictionary]; + + // Add entitlements + + NSString* appExecutablePath = [path stringByAppendingPathComponent:appInfoPlist[@"CFBundleExecutable"]]; + NSDictionary* entitlements = dumpEntitlements(appExecutablePath); + if(entitlements) + { + dictToRegister[@"Entitlements"] = entitlements; + } + + // Misc + + dictToRegister[@"ApplicationType"] = @"System"; + dictToRegister[@"BundleNameIsLocalized"] = @1; + dictToRegister[@"CFBundleIdentifier"] = appBundleID; + dictToRegister[@"CodeInfoIdentifier"] = appBundleID; + dictToRegister[@"CompatibilityState"] = @0; + if(containerPath) + { + dictToRegister[@"Container"] = containerPath; + dictToRegister[@"EnvironmentVariables"] = constructEnvironmentVariablesForContainerPath(containerPath); + } + dictToRegister[@"IsDeletable"] = @0; + dictToRegister[@"Path"] = path; + dictToRegister[@"IsContainerized"] = @(constructContainerizationForEntitlements(entitlements)); + + NSString* teamIdentifier = constructTeamIdentifierForEntitlements(entitlements); + if(teamIdentifier) dictToRegister[@"TeamIdentifier"] = teamIdentifier; + + // Add group containers + + NSDictionary* appGroupContainers = constructGroupsContainersForEntitlements(entitlements, NO); + NSDictionary* systemGroupContainers = constructGroupsContainersForEntitlements(entitlements, NO); + NSMutableDictionary* groupContainers = [NSMutableDictionary new]; + [groupContainers addEntriesFromDictionary:appGroupContainers]; + [groupContainers addEntriesFromDictionary:systemGroupContainers]; + if(groupContainers.count) + { + if(appGroupContainers.count) + { + dictToRegister[@"HasAppGroupContainers"] = @YES; + } + if(systemGroupContainers.count) + { + dictToRegister[@"HasSystemGroupContainers"] = @YES; + } + dictToRegister[@"GroupContainers"] = groupContainers.copy; + } + + // Add plugins + + NSString* pluginsPath = [path stringByAppendingPathComponent:@"PlugIns"]; + NSArray* plugins = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:pluginsPath error:nil]; + + NSMutableDictionary* bundlePlugins = [NSMutableDictionary dictionary]; + for (NSString* pluginName in plugins) + { + NSString* pluginPath = [pluginsPath stringByAppendingPathComponent:pluginName]; + + NSDictionary* pluginInfoPlist = [NSDictionary dictionaryWithContentsOfFile:[pluginPath stringByAppendingPathComponent:@"Info.plist"]]; + NSString* pluginBundleID = [pluginInfoPlist objectForKey:@"CFBundleIdentifier"]; + + if(!pluginBundleID) continue; + MCMContainer* pluginContainer = [NSClassFromString(@"MCMPluginKitPluginDataContainer") containerWithIdentifier:pluginBundleID createIfNecessary:YES existed:nil error:nil]; + NSString* pluginContainerPath = [pluginContainer url].path; + + NSMutableDictionary* pluginDict = [NSMutableDictionary dictionary]; + + // Add entitlements + + NSString* pluginExecutablePath = [pluginPath stringByAppendingPathComponent:pluginInfoPlist[@"CFBundleExecutable"]]; + NSDictionary* pluginEntitlements = dumpEntitlements(pluginExecutablePath); + if(pluginEntitlements) + { + pluginDict[@"Entitlements"] = pluginEntitlements; + } + + // Misc + + pluginDict[@"ApplicationType"] = @"PluginKitPlugin"; + pluginDict[@"BundleNameIsLocalized"] = @1; + pluginDict[@"CFBundleIdentifier"] = pluginBundleID; + pluginDict[@"CodeInfoIdentifier"] = pluginBundleID; + pluginDict[@"CompatibilityState"] = @0; + if(pluginContainerPath) + { + pluginDict[@"Container"] = pluginContainerPath; + pluginDict[@"EnvironmentVariables"] = constructEnvironmentVariablesForContainerPath(pluginContainerPath); + } + pluginDict[@"Path"] = pluginPath; + pluginDict[@"PluginOwnerBundleID"] = appBundleID; + pluginDict[@"IsContainerized"] = @(constructContainerizationForEntitlements(pluginEntitlements)); + + NSString* pluginTeamIdentifier = constructTeamIdentifierForEntitlements(pluginEntitlements); + if(pluginTeamIdentifier) pluginDict[@"TeamIdentifier"] = pluginTeamIdentifier; + + // Add plugin group containers + + NSDictionary* pluginAppGroupContainers = constructGroupsContainersForEntitlements(pluginEntitlements, NO); + NSDictionary* pluginSystemGroupContainers = constructGroupsContainersForEntitlements(pluginEntitlements, NO); + NSMutableDictionary* pluginGroupContainers = [NSMutableDictionary new]; + [pluginGroupContainers addEntriesFromDictionary:pluginAppGroupContainers]; + [pluginGroupContainers addEntriesFromDictionary:pluginSystemGroupContainers]; + if(pluginGroupContainers.count) + { + if(pluginAppGroupContainers.count) + { + pluginDict[@"HasAppGroupContainers"] = @YES; + } + if(pluginSystemGroupContainers.count) + { + pluginDict[@"HasSystemGroupContainers"] = @YES; + } + pluginDict[@"GroupContainers"] = pluginGroupContainers.copy; + } + + [bundlePlugins setObject:pluginDict forKey:pluginBundleID]; + } + [dictToRegister setObject:bundlePlugins forKey:@"_LSBundlePlugins"]; + + if(![workspace registerApplicationDictionary:dictToRegister]) + { + NSLog(@"Error: Unable to register %@", path); + } + } + else + { + NSURL* url = [NSURL fileURLWithPath:path]; + if(![workspace unregisterApplication:url]) + { + NSLog(@"Error: Unable to unregister %@", path); + } + } } \ No newline at end of file diff --git a/Installer/TrollInstaller/TrollInstaller/ViewController.m b/Installer/TrollInstaller/TrollInstaller/ViewController.m index 6508614..69ba7f1 100644 --- a/Installer/TrollInstaller/TrollInstaller/ViewController.m +++ b/Installer/TrollInstaller/TrollInstaller/ViewController.m @@ -285,6 +285,8 @@ int writeRemountPrivatePreboot(void) [self updateStatus:@"Done!"]; + NSLog(@"%@", helperOutput); + // Print installed message if(ret == 0) { diff --git a/Installer/TrollInstaller/TrollInstaller/exploit/IOGPU.c b/Installer/TrollInstaller/TrollInstaller/exploit/IOGPU.c index 4fb20fa..8b847cd 100644 --- a/Installer/TrollInstaller/TrollInstaller/exploit/IOGPU.c +++ b/Installer/TrollInstaller/TrollInstaller/exploit/IOGPU.c @@ -65,8 +65,9 @@ int IOGPU_get_command_queue_extra_refills_needed(void) || strstr(u.machine, "iPhone12,") || strstr(u.machine, "iPhone13,") || strstr(u.machine, "iPhone14,") - || strstr(u.machine, "iPad13,") || strstr(u.machine, "iPad7,") + || strstr(u.machine, "iPad12,") + || strstr(u.machine, "iPad13,") ) { return 1; @@ -74,10 +75,12 @@ int IOGPU_get_command_queue_extra_refills_needed(void) // iPhone 8, X // iPhone XS, XR // iPad Pro A12Z + // iPad 8 A12 else if ( strstr(u.machine, "iPhone10,") || strstr(u.machine, "iPhone11,") || strstr(u.machine, "iPad8,") + || strstr(u.machine, "iPad11,") ) { return 3; diff --git a/LICENSE b/LICENSE index 13a4c0d..d8cbdf5 100644 --- a/LICENSE +++ b/LICENSE @@ -10,6 +10,7 @@ License: MIT Files: Helper/uicache.m Copyright: Copyright (c) 2019 CoolStar, Modified work Copyright (c) 2020-2022 Procursus Team + Modified work Copyright (c) 2022 Lars Fröder License: BSD-4-Clause Files: Installer/TrollInstaller/exploit diff --git a/PersistenceHelper/Resources/Info.plist b/PersistenceHelper/Resources/Info.plist index d5c96d0..597db31 100644 --- a/PersistenceHelper/Resources/Info.plist +++ b/PersistenceHelper/Resources/Info.plist @@ -52,7 +52,7 @@ iPhoneOS CFBundleVersion - 1.0 + 1.0.3 LSRequiresIPhoneOS UIDeviceFamily diff --git a/PersistenceHelper/Resources/trollstorehelper b/PersistenceHelper/Resources/trollstorehelper deleted file mode 100755 index 7d5533e..0000000 Binary files a/PersistenceHelper/Resources/trollstorehelper and /dev/null differ diff --git a/PersistenceHelper/TSPHRootViewController.m b/PersistenceHelper/TSPHRootViewController.m index bbe5ed5..4fcbfb7 100644 --- a/PersistenceHelper/TSPHRootViewController.m +++ b/PersistenceHelper/TSPHRootViewController.m @@ -76,6 +76,19 @@ [refreshAppRegistrationsSpecifier setProperty:@YES forKey:@"enabled"]; refreshAppRegistrationsSpecifier.buttonAction = @selector(refreshAppRegistrations); [_specifiers addObject:refreshAppRegistrationsSpecifier]; + + PSSpecifier* uninstallTrollStoreSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Uninstall TrollStore" + target:self + set:nil + get:nil + detail:nil + cell:PSButtonCell + edit:nil]; + uninstallTrollStoreSpecifier.identifier = @"uninstallTrollStore"; + [uninstallTrollStoreSpecifier setProperty:@YES forKey:@"enabled"]; + [uninstallTrollStoreSpecifier setProperty:NSClassFromString(@"PSDeleteButtonCell") forKey:@"cellClass"]; + uninstallTrollStoreSpecifier.buttonAction = @selector(uninstallTrollStorePressed); + [_specifiers addObject:uninstallTrollStoreSpecifier]; } else { @@ -201,6 +214,22 @@ [downloadTask resume]; } +- (void)uninstallTrollStorePressed +{ + UIAlertController* uninstallWarningAlert = [UIAlertController alertControllerWithTitle:@"Warning" message:@"About to uninstall TrollStore and all of the apps installed by it. Continue?" preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; + [uninstallWarningAlert addAction:cancelAction]; + + UIAlertAction* continueAction = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) + { + spawnRoot(helperPath(), @[@"uninstall-trollstore"]); + [self reloadSpecifiers]; + }]; + [uninstallWarningAlert addAction:continueAction]; + + [self presentViewController:uninstallWarningAlert animated:YES completion:nil]; +} - (void)uninstallPersistenceHelperPressed { diff --git a/PersistenceHelper/control b/PersistenceHelper/control index 618e6b7..7315cc5 100644 --- a/PersistenceHelper/control +++ b/PersistenceHelper/control @@ -1,6 +1,6 @@ Package: com.opa334.trollstorehelper Name: TrollStore Helper -Version: 1.0 +Version: 1.0.3 Architecture: iphoneos-arm Description: Helper app to install and manage TrollStore! Maintainer: opa334 diff --git a/Store/Resources/Info.plist b/Store/Resources/Info.plist index 16d3172..a399920 100644 --- a/Store/Resources/Info.plist +++ b/Store/Resources/Info.plist @@ -50,7 +50,7 @@ iPhoneOS CFBundleVersion - 1.0.1 + 1.0.3 LSRequiresIPhoneOS UIDeviceFamily diff --git a/Store/TSApplicationsManager.h b/Store/TSApplicationsManager.h index 2d4220f..2b238f7 100644 --- a/Store/TSApplicationsManager.h +++ b/Store/TSApplicationsManager.h @@ -14,6 +14,7 @@ - (NSString*)displayNameForAppPath:(NSString*)appPath; - (NSError*)errorForCode:(int)code; +- (int)installIpa:(NSString*)pathToIpa force:(BOOL)force; - (int)installIpa:(NSString*)pathToIpa; - (int)uninstallApp:(NSString*)appId; diff --git a/Store/TSApplicationsManager.m b/Store/TSApplicationsManager.m index 979ca7d..957a3eb 100644 --- a/Store/TSApplicationsManager.m +++ b/Store/TSApplicationsManager.m @@ -67,7 +67,7 @@ errorDescription = @"Failed to create container for app bundle."; break; case 171: - errorDescription = @"A non-TrollStore app with the same identifier is already installed. If you are absolutely sure it is not, try refreshing icon cache in TrollStore settings or try rebooting your device."; + errorDescription = @"A non-TrollStore app with the same identifier is already installed. If you are absolutely sure it is not, you can force install it."; break; case 172: errorDescription = @"The app does not seem to contain an Info.plist"; @@ -78,13 +78,26 @@ return error; } -- (int)installIpa:(NSString*)pathToIpa +- (int)installIpa:(NSString*)pathToIpa force:(BOOL)force { - int ret = spawnRoot(helperPath(), @[@"install", pathToIpa]); + int ret; + if(force) + { + ret = spawnRoot(helperPath(), @[@"install", pathToIpa, @"force"]); + } + else + { + ret = spawnRoot(helperPath(), @[@"install", pathToIpa]); + } [[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationsChanged" object:nil]; return ret; } +- (int)installIpa:(NSString*)pathToIpa +{ + return [self installIpa:pathToIpa force:NO]; +} + - (int)uninstallApp:(NSString*)appId { int ret = spawnRoot(helperPath(), @[@"uninstall", appId]); diff --git a/Store/TSSceneDelegate.m b/Store/TSSceneDelegate.m index ae0db64..55b1a1d 100644 --- a/Store/TSSceneDelegate.m +++ b/Store/TSSceneDelegate.m @@ -5,7 +5,7 @@ @implementation TSSceneDelegate -- (void)handleURLContexts:(NSSet *)URLContexts scene:(UIWindowScene*)scene +- (void)doIPAInstall:(NSString*)ipaPath scene:(UIWindowScene*)scene force:(BOOL)force completion:(void (^)(void))completion { UIWindow* keyWindow = nil; for(UIWindow* window in scene.windows) @@ -17,6 +17,60 @@ } } + UIAlertController* infoAlert = [UIAlertController alertControllerWithTitle:@"Installing" message:@"" preferredStyle:UIAlertControllerStyleAlert]; + UIActivityIndicatorView* activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(5,5,50,50)]; + activityIndicator.hidesWhenStopped = YES; + activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleMedium; + [activityIndicator startAnimating]; + [infoAlert.view addSubview:activityIndicator]; + + [keyWindow.rootViewController presentViewController:infoAlert animated:YES completion:nil]; + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ + { + // Install IPA + int ret = [[TSApplicationsManager sharedInstance] installIpa:ipaPath force:force]; + NSError* error = [[TSApplicationsManager sharedInstance] errorForCode:ret]; + + NSLog(@"installed app! ret:%d, error: %@", ret, error); + dispatch_async(dispatch_get_main_queue(), ^ + { + [infoAlert dismissViewControllerAnimated:YES completion:^ + { + if(ret != 0) + { + UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Install Error %d", ret] message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) + { + if(ret == 171) + { + completion(); + } + }]; + if(ret == 171) + { + UIAlertAction* forceInstallAction = [UIAlertAction actionWithTitle:@"Force Installation" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) + { + [self doIPAInstall:ipaPath scene:scene force:YES completion:completion]; + }]; + [errorAlert addAction:forceInstallAction]; + } + [errorAlert addAction:closeAction]; + + [keyWindow.rootViewController presentViewController:errorAlert animated:YES completion:nil]; + } + + if(ret != 171) + { + completion(); + } + }]; + }); + }); +} + +- (void)handleURLContexts:(NSSet *)URLContexts scene:(UIWindowScene*)scene +{ for(UIOpenURLContext* context in URLContexts) { NSLog(@"openURLContexts %@", context.URL); @@ -39,43 +93,13 @@ respring(); exit(0); } - }; + }; if ([url.pathExtension isEqualToString:@"ipa"]) { - UIAlertController* infoAlert = [UIAlertController alertControllerWithTitle:@"Installing" message:@"" preferredStyle:UIAlertControllerStyleAlert]; - UIActivityIndicatorView* activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(5,5,50,50)]; - activityIndicator.hidesWhenStopped = YES; - activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleMedium; - [activityIndicator startAnimating]; - [infoAlert.view addSubview:activityIndicator]; - - [keyWindow.rootViewController presentViewController:infoAlert animated:YES completion:nil]; - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ - { - // Install IPA - int ret = [[TSApplicationsManager sharedInstance] installIpa:tmpCopyURL.path]; - NSError* error = [[TSApplicationsManager sharedInstance] errorForCode:ret]; - - NSLog(@"installed app! ret:%d, error: %@", ret, error); - dispatch_async(dispatch_get_main_queue(), ^ - { - [infoAlert dismissViewControllerAnimated:YES completion:^ - { - if(ret != 0) - { - UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Install Error %d", ret] message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil]; - [errorAlert addAction:closeAction]; - - [keyWindow.rootViewController presentViewController:errorAlert animated:YES completion:nil]; - } - - doneBlock(NO); - }]; - }); - }); + [self doIPAInstall:url.path scene:(UIWindowScene*)scene force:NO completion:^{ + doneBlock(NO); + }]; } else if([url.pathExtension isEqualToString:@"tar"]) { diff --git a/Store/control b/Store/control index 0238953..68b7e5c 100644 --- a/Store/control +++ b/Store/control @@ -1,6 +1,6 @@ Package: com.opa334.trollstore Name: TrollStore -Version: 1.0.1 +Version: 1.0.3 Architecture: iphoneos-arm Description: An awesome application! Maintainer: opa334 diff --git a/_compile/build_full.sh b/_compile/build_full.sh index 1ece343..67ac02d 100755 --- a/_compile/build_full.sh +++ b/_compile/build_full.sh @@ -36,6 +36,8 @@ make clean make package FINALPACKAGE=1 cd - +rm ../PersistenceHelper/Resources/trollstorehelper + cp ../PersistenceHelper/.theos/obj/TrollStorePersistenceHelper.app/TrollStorePersistenceHelper ./out/TrollStore.app/PersistenceHelper ldid -S -M -Kcert.p12 ./out/TrollStore.app/PersistenceHelper @@ -45,3 +47,19 @@ cd out COPYFILE_DISABLE=1 tar -czvf TrollStore.tar ./TrollStore.app rm -rf ./TrollStore.app cd - + +# Step five: compile installer +xcodebuild -project ../Installer/TrollInstaller/TrollInstaller.xcodeproj -scheme TrollInstaller -destination generic/platform=iOS -archivePath ./out/Installer.xcarchive archive + +if [[ -f "./out/Installer.xcarchive/Products/Applications/TrollInstaller.app/embedded.mobileprovision" ]]; then + rm ./out/Installer.xcarchive/Products/Applications/TrollInstaller.app/embedded.mobileprovision +fi + +ldid -s ./out/Installer.xcarchive/Products/Applications/TrollInstaller.app +mkdir ./out/Payload +mv ./out/Installer.xcarchive/Products/Applications/TrollInstaller.app ./out/Payload/TrollInstaller.app +cd out +zip -vr TrollInstaller.ipa Payload +cd - +rm -rf ./out/Payload +rm -rf ./out/Installer.xcarchive \ No newline at end of file