mirror of https://github.com/opa334/TrollStore.git
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;
|
||
|
}
|
||
|
}
|