mirror of
				https://github.com/opa334/TrollStore.git
				synced 2025-11-04 15:42:35 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			352 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			352 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
#import <stdio.h>
 | 
						|
#import "unarchive.h"
 | 
						|
@import Foundation;
 | 
						|
#import "uicache.h"
 | 
						|
#import <sys/stat.h>
 | 
						|
#import <dlfcn.h>
 | 
						|
#import <spawn.h>
 | 
						|
#import "path.h"
 | 
						|
#import "CoreServices.h"
 | 
						|
#import <objc/runtime.h>
 | 
						|
 | 
						|
#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;
 | 
						|
	}
 | 
						|
}
 |