TrollStore/Helper/main.m-system

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;
}
}