#import #import "unarchive.h" @import Foundation; #import "uicache.h" #import #import #import #import "path.h" #import "CoreServices.h" #import #define kCFPreferencesNoContainer CFSTR("kCFPreferencesNoContainer") typedef CFPropertyListRef (*_CFPreferencesCopyValueWithContainerType)(CFStringRef key, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef containerPath); typedef void (*_CFPreferencesSetValueWithContainerType)(CFStringRef key, CFPropertyListRef value, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef containerPath); typedef Boolean (*_CFPreferencesSynchronizeWithContainerType)(CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef containerPath); typedef CFArrayRef (*_CFPreferencesCopyKeyListWithContainerType)(CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef containerPath); typedef CFDictionaryRef (*_CFPreferencesCopyMultipleWithContainerType)(CFArrayRef keysToFetch, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef containerPath); extern char*** _NSGetArgv(); NSString* safe_getExecutablePath() { char* executablePathC = **_NSGetArgv(); return [NSString stringWithUTF8String:executablePathC]; } NSDictionary* infoDictionaryForAppPath(NSString* appPath) { NSString* infoPlistPath = [appPath stringByAppendingPathComponent:@"Info.plist"]; return [NSDictionary dictionaryWithContentsOfFile:infoPlistPath]; } NSString* appIdForAppPath(NSString* appPath) { return infoDictionaryForAppPath(appPath)[@"CFBundleIdentifier"]; } NSString* appPathForAppId(NSString* appId, NSError** error) { NSString* appPath = [TROLLSTORE_APPLICATIONS_PATH stringByAppendingPathComponent:appId]; NSArray* items = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:appPath error:error]; if(!items) return nil; for(NSString* item in items) { if([item.pathExtension isEqualToString:@"app"]) { return [appPath stringByAppendingPathComponent:item]; } } return nil; } static void dump_file_content(int fd) { ssize_t num_read; char line_buf[256]; int cur_pos = 0; for (;;) { char c; num_read = read(fd, &c, sizeof(c)); if(num_read <= 0) if(c == '\n' || cur_pos >= 255 || num_read <= 0) { line_buf[cur_pos] = '\n'; NSLog(@"%s", (char*)line_buf); if(c == '\n') cur_pos++; if(num_read > 0) { continue; } else { break; } } line_buf[cur_pos++] = c; } } BOOL signApp(NSString* appPath, NSError** error) { NSString* ldidPath = [[safe_getExecutablePath() stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"ldid"]; if(![[NSFileManager defaultManager] fileExistsAtPath:ldidPath]) { NSLog(@"WARNING: ldid not found, not signing application"); return NO; } NSString* certPath = [TROLLSTORE_MAIN_PATH stringByAppendingPathComponent:@"TrollStore.app/cert.p12"]; NSString* certArg = [@"-K" stringByAppendingPathComponent:certPath]; int out[2]; posix_spawn_file_actions_t action; posix_spawn_file_actions_init(&action); pipe(out); posix_spawn_file_actions_adddup2(&action, out[1], STDERR_FILENO); posix_spawn_file_actions_addclose(&action, out[0]); char* args[] = { "ldid", "-S", "-M", (char*)certArg.UTF8String, (char*)appPath.UTF8String, NULL }; NSLog(@"%@ ldid -S -M %@ %@", ldidPath, certArg, appPath); pid_t task_pid; int status = -200; int spawnError = posix_spawn(&task_pid, [ldidPath UTF8String], &action, NULL, args, NULL); if(spawnError != 0) { NSLog(@"posix_spawn error %d\n", spawnError); return spawnError; } waitpid(task_pid, &status, WEXITED); NSLog(@"ldid exited with status %d", status); waitpid(task_pid, &status, 0); NSLog(@"ldid exited with status %d", status); NSLog(@"ldid output:"); close(out[1]); dump_file_content(out[0]); NSLog(@"end ldid output:"); return status == 0; } BOOL installApp(NSString* appPath, NSString* appId, BOOL sign, NSError** error) { if(sign) { // if it fails to sign, we don't care signApp(appPath, error); } BOOL existed; NSError* mcmError; MCMAppContainer* appContainer = [objc_getClass("MCMAppContainer") containerWithIdentifier:appId createIfNecessary:YES existed:&existed error:&mcmError]; NSLog(@"installApp appContainer: %@, mcmError: %@", appContainer, mcmError); if(!appContainer || mcmError) { if(error) *error = mcmError; return NO; } //TODO: if TrollStore, preserve by moving it into appPath if needed ldid if needed NSURL* trollStoreMarkURL = [appContainer.url URLByAppendingPathComponent:@"_TrollStore"]; if(existed) { // trying to update an app not installed by TrollStore... bailing out if(![trollStoreMarkURL checkResourceIsReachableAndReturnError:nil]) { NSLog(@"installApp already installed and not a TrollStore app... bailing out"); return NO; } else { // update existing app... clean old app directory NSLog(@"installApp found existing TrollStore app, cleaning directory"); NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:appContainer.url includingPropertiesForKeys:nil options:0 errorHandler:nil]; NSURL* fileURL; while(fileURL = [enumerator nextObject]) { [[NSFileManager defaultManager] removeItemAtURL:fileURL error:nil]; } } } [[NSFileManager defaultManager] createFileAtPath:trollStoreMarkURL.path contents:[NSData data] attributes:nil]; NSString* newAppPath = [appContainer.url.path stringByAppendingPathComponent:appPath.lastPathComponent]; NSLog(@"installApp new app path: %@", newAppPath); BOOL suc = [[NSFileManager defaultManager] copyItemAtPath:appPath toPath:newAppPath error:error]; NSLog(@"installApp copied app? %d, adding to uicache now...", suc); registerPath((char*)newAppPath.UTF8String, 0); return YES; } BOOL uninstallApp(NSString* appId, NSError** error) { NSString* appPath = appPathForAppId(appId, error); if(!appPath) return NO; registerPath((char*)appPath.UTF8String, 1); return [[NSFileManager defaultManager] removeItemAtPath:[appPath stringByDeletingLastPathComponent] error:error]; } BOOL installIpa(NSString* ipaPath, NSError** error) { BOOL suc = NO; NSString* tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString]; suc = [[NSFileManager defaultManager] createDirectoryAtPath:tmpPath withIntermediateDirectories:NO attributes:nil error:error]; if(!suc) return NO; extract(ipaPath, tmpPath); NSString* tmpPayloadPath = [tmpPath stringByAppendingPathComponent:@"Payload"]; NSArray* items = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:tmpPayloadPath error:error]; if(!items) return NO; NSString* tmpAppPath; for(NSString* item in items) { if([item.pathExtension isEqualToString:@"app"]) { tmpAppPath = [tmpPayloadPath stringByAppendingPathComponent:item]; break; } } if(!tmpAppPath) return NO; NSString* appId = appIdForAppPath(tmpAppPath); suc = installApp(tmpAppPath, appId, YES, error); [[NSFileManager defaultManager] removeItemAtPath:tmpAppPath error:nil]; return suc; } void uninstallAllApps(void) { NSArray* items = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:TROLLSTORE_APPLICATIONS_PATH error:nil]; for(NSString* appId in items) { NSString* appPath = appPathForAppId(appId, nil); registerPath((char*)appPath.UTF8String, 1); } [[NSFileManager defaultManager] removeItemAtPath:TROLLSTORE_ROOT_PATH error:nil]; } BOOL uninstallTrollStore(void) { NSString* trollStore = [TROLLSTORE_MAIN_PATH stringByAppendingPathComponent:@"TrollStore.app"]; if(![[NSFileManager defaultManager] fileExistsAtPath:trollStore]) return NO; registerPath((char*)trollStore.UTF8String, 1); return [[NSFileManager defaultManager] removeItemAtPath:trollStore error:nil]; } BOOL installTrollStore(NSString* pathToTar) { _CFPreferencesSetValueWithContainerType _CFPreferencesSetValueWithContainer = (_CFPreferencesSetValueWithContainerType)dlsym(RTLD_DEFAULT, "_CFPreferencesSetValueWithContainer"); _CFPreferencesSynchronizeWithContainerType _CFPreferencesSynchronizeWithContainer = (_CFPreferencesSynchronizeWithContainerType)dlsym(RTLD_DEFAULT, "_CFPreferencesSynchronizeWithContainer"); _CFPreferencesSetValueWithContainer(CFSTR("SBShowNonDefaultSystemApps"), kCFBooleanTrue, CFSTR("com.apple.springboard"), CFSTR("mobile"), kCFPreferencesAnyHost, kCFPreferencesNoContainer); _CFPreferencesSynchronizeWithContainer(CFSTR("com.apple.springboard"), CFSTR("mobile"), kCFPreferencesAnyHost, kCFPreferencesNoContainer); if(![[NSFileManager defaultManager] fileExistsAtPath:pathToTar]) return NO; if(![pathToTar.pathExtension isEqualToString:@"tar"]) return NO; NSString* tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString]; BOOL suc = [[NSFileManager defaultManager] createDirectoryAtPath:tmpPath withIntermediateDirectories:NO attributes:nil error:nil]; if(!suc) return NO; extract(pathToTar, tmpPath); NSLog(@"installTrollStore extracted %@ to %@", pathToTar, tmpPath); NSString* tmpTrollStore = [tmpPath stringByAppendingPathComponent:@"TrollStore.app"]; if(![[NSFileManager defaultManager] fileExistsAtPath:tmpTrollStore]) return NO; NSLog(@"installTrollStore temp TrollStore path: %@", tmpTrollStore); NSString* tmpTrollStoreMain = [tmpTrollStore stringByAppendingPathComponent:@"TrollStore"]; NSString* tmpTrollStoreRootHelper = [tmpTrollStore stringByAppendingPathComponent:@"trollstorehelper"]; NSString* tmpTrollStoreLdid = [tmpTrollStore stringByAppendingPathComponent:@"ldid"]; // make executable chmod(tmpTrollStoreMain.UTF8String, 0755); chmod(tmpTrollStoreRootHelper.UTF8String, 0755); chmod(tmpTrollStoreLdid.UTF8String, 0755); // set owners chown(tmpTrollStoreMain.UTF8String, 33, 33); chown(tmpTrollStoreRootHelper.UTF8String, 0, 0); // set root helper binary owner to root chown(tmpTrollStoreLdid.UTF8String, 0, 0); NSLog(@"installTrollStore extracted and prepared TrollStore app, now installing..."); installApp(tmpTrollStore, @"com.apple.TrollStore", NO, nil); [[NSFileManager defaultManager] removeItemAtPath:tmpPath error:nil]; return YES; } int main(int argc, char *argv[], char *envp[]) { @autoreleasepool { if(argc <= 1) return -1; NSLog(@"trollstore helper go, uid: %d, gid: %d", getuid(), getgid()); NSLog(@"ok %d", argc); NSBundle* mcmBundle = [NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/MobileContainerManager.framework"]; [mcmBundle load]; BOOL suc = NO; NSError* error; NSString* cmd = [NSString stringWithUTF8String:argv[1]]; if([cmd isEqualToString:@"install"]) { if(argc <= 2) return -2; NSString* ipaPath = [NSString stringWithUTF8String:argv[2]]; suc = installIpa(ipaPath, &error); } else if([cmd isEqualToString:@"uninstall"]) { if(argc <= 2) return -2; NSString* appId = [NSString stringWithUTF8String:argv[2]]; suc = uninstallApp(appId, &error); } else if([cmd isEqualToString:@"install-trollstore"]) { if(argc <= 2) return -2; NSString* tsTar = [NSString stringWithUTF8String:argv[2]]; suc = installTrollStore(tsTar); NSLog(@"installed troll store? %d", suc); } else if([cmd isEqualToString:@"uninstall-trollstore"]) { uninstallTrollStore(); uninstallAllApps(); } if(!suc) { NSLog(@"error: %@", error); } return !suc; } }