TrollStore/Shared/TSUtil.m

532 lines
14 KiB
Mathematica
Raw Normal View History

2022-09-02 23:19:48 +08:00
#import "TSUtil.h"
#import <Foundation/Foundation.h>
#import <spawn.h>
#import <sys/sysctl.h>
2022-09-30 03:00:11 +08:00
@interface PSAppDataUsagePolicyCache : NSObject
+ (instancetype)sharedInstance;
- (void)setUsagePoliciesForBundle:(NSString*)bundleId cellular:(BOOL)cellular wifi:(BOOL)wifi;
@end
2022-09-02 23:19:48 +08:00
#define POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE 1
extern int posix_spawnattr_set_persona_np(const posix_spawnattr_t* __restrict, uid_t, uint32_t);
extern int posix_spawnattr_set_persona_uid_np(const posix_spawnattr_t* __restrict, uid_t);
extern int posix_spawnattr_set_persona_gid_np(const posix_spawnattr_t* __restrict, uid_t);
2022-09-30 03:00:11 +08:00
void chineseWifiFixup(void)
{
NSBundle *bundle = [NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/SettingsCellular.framework"];
[bundle load];
2022-11-26 02:24:58 +08:00
PSAppDataUsagePolicyCache* policyCache = [NSClassFromString(@"PSAppDataUsagePolicyCache") sharedInstance];
if([policyCache respondsToSelector:@selector(setUsagePoliciesForBundle:cellular:wifi:)])
{
[policyCache setUsagePoliciesForBundle:NSBundle.mainBundle.bundleIdentifier cellular:true wifi:true];
}
2022-09-30 03:00:11 +08:00
}
void loadMCMFramework(void)
{
static dispatch_once_t onceToken;
dispatch_once (&onceToken, ^{
NSBundle* mcmBundle = [NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/MobileContainerManager.framework"];
[mcmBundle load];
});
}
extern char*** _NSGetArgv();
NSString* safe_getExecutablePath()
{
char* executablePathC = **_NSGetArgv();
return [NSString stringWithUTF8String:executablePathC];
}
#ifdef EMBEDDED_ROOT_HELPER
NSString* rootHelperPath(void)
{
return safe_getExecutablePath();
}
#else
NSString* rootHelperPath(void)
2022-09-02 23:19:48 +08:00
{
return [[NSBundle mainBundle].bundlePath stringByAppendingPathComponent:@"trollstorehelper"];
}
#endif
2022-09-02 23:19:48 +08:00
2022-12-01 07:37:01 +08:00
int fd_is_valid(int fd)
{
return fcntl(fd, F_GETFD) != -1 || errno != EBADF;
}
2022-09-10 02:22:34 +08:00
NSString* getNSStringFromFile(int fd)
{
NSMutableString* ms = [NSMutableString new];
ssize_t num_read;
char c;
2022-12-01 07:37:01 +08:00
if(!fd_is_valid(fd)) return @"";
2022-09-10 02:22:34 +08:00
while((num_read = read(fd, &c, sizeof(c))))
{
[ms appendString:[NSString stringWithFormat:@"%c", c]];
2022-12-01 07:37:01 +08:00
if(c == '\n') break;
2022-09-10 02:22:34 +08:00
}
return ms.copy;
}
void printMultilineNSString(NSString* stringToPrint)
{
NSCharacterSet *separator = [NSCharacterSet newlineCharacterSet];
NSArray* lines = [stringToPrint componentsSeparatedByCharactersInSet:separator];
for(NSString* line in lines)
{
NSLog(@"%@", line);
}
}
int spawnRoot(NSString* path, NSArray* args, NSString** stdOut, NSString** stdErr)
2022-09-02 23:19:48 +08:00
{
NSMutableArray* argsM = args.mutableCopy ?: [NSMutableArray new];
2022-12-01 07:37:01 +08:00
[argsM insertObject:path atIndex:0];
2022-09-02 23:19:48 +08:00
NSUInteger argCount = [argsM count];
char **argsC = (char **)malloc((argCount + 1) * sizeof(char*));
for (NSUInteger i = 0; i < argCount; i++)
{
argsC[i] = strdup([[argsM objectAtIndex:i] UTF8String]);
}
argsC[argCount] = NULL;
posix_spawnattr_t attr;
2022-09-10 02:22:34 +08:00
posix_spawnattr_init(&attr);
2022-09-02 23:19:48 +08:00
posix_spawnattr_set_persona_np(&attr, 99, POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE);
posix_spawnattr_set_persona_uid_np(&attr, 0);
posix_spawnattr_set_persona_gid_np(&attr, 0);
2022-09-10 02:22:34 +08:00
posix_spawn_file_actions_t action;
posix_spawn_file_actions_init(&action);
int outErr[2];
if(stdErr)
{
pipe(outErr);
posix_spawn_file_actions_adddup2(&action, outErr[1], STDERR_FILENO);
posix_spawn_file_actions_addclose(&action, outErr[0]);
}
int out[2];
if(stdOut)
{
pipe(out);
posix_spawn_file_actions_adddup2(&action, out[1], STDOUT_FILENO);
posix_spawn_file_actions_addclose(&action, out[0]);
}
2022-09-02 23:19:48 +08:00
pid_t task_pid;
int status = -200;
2022-09-10 02:22:34 +08:00
int spawnError = posix_spawn(&task_pid, [path UTF8String], &action, &attr, (char* const*)argsC, NULL);
2022-09-02 23:19:48 +08:00
posix_spawnattr_destroy(&attr);
for (NSUInteger i = 0; i < argCount; i++)
{
free(argsC[i]);
}
free(argsC);
if(spawnError != 0)
{
NSLog(@"posix_spawn error %d\n", spawnError);
return spawnError;
}
2022-12-01 07:37:01 +08:00
__block volatile BOOL _isRunning = YES;
NSMutableString* outString = [NSMutableString new];
NSMutableString* errString = [NSMutableString new];
dispatch_semaphore_t sema = 0;
dispatch_queue_t logQueue;
if(stdOut || stdErr)
{
logQueue = dispatch_queue_create("com.opa334.TrollStore.LogCollector", NULL);
sema = dispatch_semaphore_create(0);
int outPipe = out[0];
int outErrPipe = outErr[0];
__block BOOL outEnabled = (BOOL)stdOut;
__block BOOL errEnabled = (BOOL)stdErr;
dispatch_async(logQueue, ^
{
while(_isRunning)
{
@autoreleasepool
{
if(outEnabled)
{
[outString appendString:getNSStringFromFile(outPipe)];
}
if(errEnabled)
{
[errString appendString:getNSStringFromFile(outErrPipe)];
}
}
}
dispatch_semaphore_signal(sema);
});
}
2022-09-02 23:19:48 +08:00
do
{
if (waitpid(task_pid, &status, 0) != -1) {
2022-09-03 09:08:59 +08:00
NSLog(@"Child status %d", WEXITSTATUS(status));
2022-09-02 23:19:48 +08:00
} else
{
perror("waitpid");
2022-12-01 07:37:01 +08:00
_isRunning = NO;
2022-09-02 23:19:48 +08:00
return -222;
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
2022-09-10 02:22:34 +08:00
2022-12-01 07:37:01 +08:00
_isRunning = NO;
if(stdOut || stdErr)
2022-09-10 02:22:34 +08:00
{
2022-12-01 07:37:01 +08:00
if(stdOut)
{
close(out[1]);
}
if(stdErr)
{
close(outErr[1]);
}
2022-09-10 02:22:34 +08:00
2022-12-01 07:37:01 +08:00
// wait for logging queue to finish
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
if(stdOut)
{
*stdOut = outString.copy;
}
if(stdErr)
{
*stdErr = errString.copy;
}
2022-09-10 02:22:34 +08:00
}
2022-12-01 07:37:01 +08:00
2022-09-02 23:19:48 +08:00
return WEXITSTATUS(status);
}
void enumerateProcessesUsingBlock(void (^enumerator)(pid_t pid, NSString* executablePath, BOOL* stop))
{
static int maxArgumentSize = 0;
if (maxArgumentSize == 0) {
size_t size = sizeof(maxArgumentSize);
if (sysctl((int[]){ CTL_KERN, KERN_ARGMAX }, 2, &maxArgumentSize, &size, NULL, 0) == -1) {
perror("sysctl argument size");
maxArgumentSize = 4096; // Default
}
}
int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL};
struct kinfo_proc *info;
size_t length;
int count;
if (sysctl(mib, 3, NULL, &length, NULL, 0) < 0)
return;
if (!(info = malloc(length)))
return;
if (sysctl(mib, 3, info, &length, NULL, 0) < 0) {
free(info);
return;
}
count = length / sizeof(struct kinfo_proc);
for (int i = 0; i < count; i++) {
@autoreleasepool {
pid_t pid = info[i].kp_proc.p_pid;
if (pid == 0) {
continue;
}
size_t size = maxArgumentSize;
char* buffer = (char *)malloc(length);
if (sysctl((int[]){ CTL_KERN, KERN_PROCARGS2, pid }, 3, buffer, &size, NULL, 0) == 0) {
NSString* executablePath = [NSString stringWithCString:(buffer+sizeof(int)) encoding:NSUTF8StringEncoding];
BOOL stop = NO;
enumerator(pid, executablePath, &stop);
if(stop)
{
free(buffer);
break;
}
}
free(buffer);
}
}
free(info);
}
2022-11-20 22:33:54 +08:00
void killall(NSString* processName, BOOL softly)
2022-09-02 23:19:48 +08:00
{
enumerateProcessesUsingBlock(^(pid_t pid, NSString* executablePath, BOOL* stop)
{
if([executablePath.lastPathComponent isEqualToString:processName])
{
2022-11-20 22:33:54 +08:00
if(softly)
{
kill(pid, SIGTERM);
}
else
{
kill(pid, SIGKILL);
}
2022-09-02 23:19:48 +08:00
}
});
}
void respring(void)
{
2022-11-20 22:33:54 +08:00
killall(@"SpringBoard", YES);
2022-09-02 23:19:48 +08:00
exit(0);
}
2023-01-21 20:52:39 +08:00
void github_fetchLatestVersion(NSString* repo, void (^completionHandler)(NSString* latestVersion))
2022-09-02 23:19:48 +08:00
{
2023-01-21 20:52:39 +08:00
NSString* urlString = [NSString stringWithFormat:@"https://api.github.com/repos/%@/releases/latest", repo];
NSURL* githubLatestAPIURL = [NSURL URLWithString:urlString];
2022-09-22 23:38:58 +08:00
NSURLSessionDataTask* task = [NSURLSession.sharedSession dataTaskWithURL:githubLatestAPIURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
if(!error)
{
if ([response isKindOfClass:[NSHTTPURLResponse class]])
{
NSError *jsonError;
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
if (!jsonError)
{
completionHandler(jsonResponse[@"tag_name"]);
}
}
}
}];
[task resume];
}
2023-01-21 20:52:39 +08:00
void fetchLatestTrollStoreVersion(void (^completionHandler)(NSString* latestVersion))
{
github_fetchLatestVersion(@"opa334/TrollStore", completionHandler);
}
void fetchLatestLdidVersion(void (^completionHandler)(NSString* latestVersion))
{
github_fetchLatestVersion(@"opa334/ldid", completionHandler);
}
NSArray* trollStoreInstalledAppContainerPaths()
{
NSMutableArray* appContainerPaths = [NSMutableArray new];
NSString* appContainersPath = @"/var/containers/Bundle/Application";
NSError* error;
NSArray* containers = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:appContainersPath error:&error];
if(error)
{
NSLog(@"error getting app bundles paths %@", error);
}
if(!containers) return nil;
for(NSString* container in containers)
{
NSString* containerPath = [appContainersPath stringByAppendingPathComponent:container];
BOOL isDirectory = NO;
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:containerPath isDirectory:&isDirectory];
if(exists && isDirectory)
{
NSString* trollStoreMark = [containerPath stringByAppendingPathComponent:@"_TrollStore"];
if([[NSFileManager defaultManager] fileExistsAtPath:trollStoreMark])
{
NSString* trollStoreApp = [containerPath stringByAppendingPathComponent:@"TrollStore.app"];
if(![[NSFileManager defaultManager] fileExistsAtPath:trollStoreApp])
{
[appContainerPaths addObject:containerPath];
}
}
}
}
return appContainerPaths.copy;
}
NSArray* trollStoreInstalledAppBundlePaths()
{
NSMutableArray* appPaths = [NSMutableArray new];
for(NSString* containerPath in trollStoreInstalledAppContainerPaths())
{
NSArray* items = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:containerPath error:nil];
if(!items) return nil;
for(NSString* item in items)
{
if([item.pathExtension isEqualToString:@"app"])
{
[appPaths addObject:[containerPath stringByAppendingPathComponent:item]];
}
}
}
return appPaths.copy;
}
NSString* trollStorePath()
{
loadMCMFramework();
NSError* mcmError;
MCMAppContainer* appContainer = [NSClassFromString(@"MCMAppContainer") containerWithIdentifier:@"com.opa334.TrollStore" createIfNecessary:NO existed:NULL error:&mcmError];
if(!appContainer) return nil;
return appContainer.url.path;
}
NSString* trollStoreAppPath()
{
return [trollStorePath() stringByAppendingPathComponent:@"TrollStore.app"];
}
2022-11-30 06:46:01 +08:00
BOOL isRemovableSystemApp(NSString* appId)
{
return [[NSFileManager defaultManager] fileExistsAtPath:[@"/System/Library/AppSignatures" stringByAppendingPathComponent:appId]];
}
LSApplicationProxy* findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE allowedTypes)
{
__block LSApplicationProxy* outProxy;
void (^searchBlock)(LSApplicationProxy* appProxy) = ^(LSApplicationProxy* appProxy)
{
if(appProxy.installed && !appProxy.restricted)
{
if([appProxy.bundleURL.path hasPrefix:@"/private/var/containers"])
{
NSURL* trollStorePersistenceMarkURL = [appProxy.bundleURL URLByAppendingPathComponent:@".TrollStorePersistenceHelper"];
if([trollStorePersistenceMarkURL checkResourceIsReachableAndReturnError:nil])
{
outProxy = appProxy;
}
}
}
};
if(allowedTypes & PERSISTENCE_HELPER_TYPE_USER)
{
[[LSApplicationWorkspace defaultWorkspace] enumerateApplicationsOfType:0 block:searchBlock];
}
if(allowedTypes & PERSISTENCE_HELPER_TYPE_SYSTEM)
{
[[LSApplicationWorkspace defaultWorkspace] enumerateApplicationsOfType:1 block:searchBlock];
}
return outProxy;
2022-10-30 06:45:30 +08:00
}
SecStaticCodeRef getStaticCodeRef(NSString *binaryPath)
{
if(binaryPath == nil)
{
return NULL;
}
CFURLRef binaryURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (__bridge CFStringRef)binaryPath, kCFURLPOSIXPathStyle, false);
if(binaryURL == NULL)
{
NSLog(@"[getStaticCodeRef] failed to get URL to binary %@", binaryPath);
return NULL;
}
SecStaticCodeRef codeRef = NULL;
OSStatus result;
result = SecStaticCodeCreateWithPathAndAttributes(binaryURL, kSecCSDefaultFlags, NULL, &codeRef);
CFRelease(binaryURL);
if(result != errSecSuccess)
{
NSLog(@"[getStaticCodeRef] failed to create static code for binary %@", binaryPath);
return NULL;
}
return codeRef;
}
NSDictionary* dumpEntitlements(SecStaticCodeRef codeRef)
{
if(codeRef == NULL)
{
NSLog(@"[dumpEntitlements] attempting to dump entitlements without a StaticCodeRef");
return nil;
}
CFDictionaryRef signingInfo = NULL;
OSStatus result;
result = SecCodeCopySigningInformation(codeRef, kSecCSRequirementInformation, &signingInfo);
if(result != errSecSuccess)
{
NSLog(@"[dumpEntitlements] failed to copy signing info from static code");
return nil;
}
NSDictionary *entitlementsNSDict = nil;
CFDictionaryRef entitlements = CFDictionaryGetValue(signingInfo, kSecCodeInfoEntitlementsDict);
if(entitlements == NULL)
{
NSLog(@"[dumpEntitlements] no entitlements specified");
}
else if(CFGetTypeID(entitlements) != CFDictionaryGetTypeID())
{
NSLog(@"[dumpEntitlements] invalid entitlements");
}
else
{
entitlementsNSDict = (__bridge NSDictionary *)(entitlements);
NSLog(@"[dumpEntitlements] dumped %@", entitlementsNSDict);
}
CFRelease(signingInfo);
return entitlementsNSDict;
}
NSDictionary* dumpEntitlementsFromBinaryAtPath(NSString *binaryPath)
{
// This function is intended for one-shot checks. Main-event functions should retain/release their own SecStaticCodeRefs
if(binaryPath == nil)
{
return nil;
}
SecStaticCodeRef codeRef = getStaticCodeRef(binaryPath);
if(codeRef == NULL)
{
return nil;
}
NSDictionary *entitlements = dumpEntitlements(codeRef);
CFRelease(codeRef);
return entitlements;
}
NSDictionary* dumpEntitlementsFromBinaryData(NSData* binaryData)
{
NSDictionary* entitlements;
NSString* tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString];
NSURL* tmpURL = [NSURL fileURLWithPath:tmpPath];
if([binaryData writeToURL:tmpURL options:NSDataWritingAtomic error:nil])
{
entitlements = dumpEntitlementsFromBinaryAtPath(tmpPath);
[[NSFileManager defaultManager] removeItemAtURL:tmpURL error:nil];
}
return entitlements;
2022-09-22 23:38:58 +08:00
}