// // nitoInstaller.m // nitotvinstaller // // Created by Kevin Bradley on 1/31/18. // Copyright © 2018 Kevin Bradley. All rights reserved. // #include #include #include #include #ifdef ARM #include // NSObject #endif #import "nitoInstaller.h" #include //#include #include #define CORE_SERVICE_FRAMEWORK "/System/Library/Frameworks/MobileCoreServices.framework/MobileCoreServices" NSObject * workspace; // Intentionally void * so as to NOT involve @interface files @interface LSApplicationWorkspace: NSObject - (id)allApplications; - (void)openApplicationWithBundleID:(NSString *)string; - (id)defaultWorkspace; @end void get_string(char *string); @implementation nitoInstaller - (NSString *)airPlayVersionNumber { NSDictionary *infoDictionary = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/PrivateFrameworks/AirPlaySupport.framework/Info.plist"]; return infoDictionary[@"CFBundleVersion"]; } + (NSString *)getTextWithPrompt:(NSString *)thePrompt { NSString *prompt = [NSString stringWithFormat:@"\n%@ ", thePrompt]; fprintf(stdout, "%s", [prompt UTF8String]); NSString *thestring = [NSString.alloc initWithData: [NSFileHandle.fileHandleWithStandardInput availableData] encoding:NSUTF8StringEncoding]; return [thestring stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; } - (NSString *)tarPathForCurrentMode { NSString *path = nil; switch (self.versionState) { case InstallVersionStateNine: path= @"/usr/bin/tar"; break; case InstallVersionStateTenOne: path= @"/tmp/usr/bin/tar"; //need to verify break; case InstallVersionStateTenTwo: path= @"/usr/bin/tar"; break; case InstallVersionStateEleven: path= @"/jb/usr/bin/tar"; break; default: break; } return path; } - (NSString *)bootstrapPathForVersion:(NSString *)swVers { NSString *bootstrapPath = @"https://nitosoft.com/ATV4/bootstraps/"; { OurLog(@"srcvers: %@", swVers);//352.18.1 == 11.0, 353.50 = 11.1 if ([swVers compare:@"352.18.1" options:NSNumericSearch] != NSOrderedAscending) { OurLog(@"11.x or greater!"); self.versionState = InstallVersionStateEleven; bootstrapPath = [bootstrapPath stringByAppendingString:@"bootstrap11.tar"]; } else if([swVers compare:@"320.20.1" options:NSNumericSearch] != NSOrderedAscending) { OurLog(@"10.2.2 or greater!"); self.versionState = InstallVersionStateTenTwo; bootstrapPath = [bootstrapPath stringByAppendingString:@"bootstrap11.tar"]; } else if([swVers compare:@"301.44.3" options:NSNumericSearch] != NSOrderedAscending) { OurLog(@"10.0 or greater"); self.versionState = InstallVersionStateTenOne; bootstrapPath = [bootstrapPath stringByAppendingString:@"bootstrap11.tar"]; } else { OurLog(@"9.0 or greater?"); self.versionState = InstallVersionStateNine; bootstrapPath = [bootstrapPath stringByAppendingString:@"bootstrap9.tar"]; } } return bootstrapPath; } + (int)returnForProcess:(NSString *)call { if (call==nil) return 0; char line[200]; FILE* fp = popen([call UTF8String], "r"); //NSMutableArray *lines = [[NSMutableArray alloc]init]; if (fp) { while (fgets(line, sizeof line, fp)) { NSString *s = [NSString stringWithCString:line encoding:NSUTF8StringEncoding]; s = [s stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; OurLog(@"%@",s); // [lines addObject:s]; } } int returnStatus = pclose(fp); return returnStatus; } int main(int argc, const char * argv[]) { NSString *runPath = [[NSFileManager defaultManager] currentDirectoryPath]; nitoInstaller *installer = [nitoInstaller new]; //checking for dpkg status file might not be the most elegant check, but if its there we should have bootstrap installed NSString *fileCheck = @"/var/lib/dpkg/status"; OurLog(@"\n\nWelcome to the nitoTV 2.0 & bootstrap installer script! Version 1.1"); OurLog(@"\nWe will detect your tvOS version and install the compatible bootstrap nitoTV, upon completion nitoTV should appear on your AppleTV\n\n"); if ([[NSFileManager defaultManager] fileExistsAtPath:fileCheck]) { char c; printf("\nIt appears you have already installed nitoTV and the bootstrap, it is NOT recommended to do this again, are you sure you want to continue? [y/n]?"); c=getchar(); while(c!='y' && c!='n') { if (c!='\n'){ printf("[y/n]"); } c=getchar(); } if (c == 'n') { OurLog(@"\nsmart move... exiting\n"); return 0; } else if (c == 'y') { OurLog(@"\nits your funeral....\n"); } } [installer installPackageInPath:runPath]; CFRunLoopRun(); return 0; } - (void)openApp:(NSString *)bundleID; { Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace"); if (!LSApplicationWorkspace_class) {fprintf(stderr,"Unable to get Workspace class\n"); } workspace = [LSApplicationWorkspace_class performSelector:@selector (defaultWorkspace)]; if (!workspace) {fprintf(stderr,"Unable to get Workspace\n"); } void *CS_handle = dlopen (CORE_SERVICE_FRAMEWORK, RTLD_NOW); if (!CS_handle) { fprintf(stderr,"Can't find %s!\n", CORE_SERVICE_FRAMEWORK); return; } if (workspace){ [workspace performSelector:@selector(openApplicationWithBundleID:) withObject:(id) bundleID ]; } } - (void)installPackageInPath:(NSString *)thePath { NSString *bootstrapPath = [self bootstrapPathForVersion:[self airPlayVersionNumber]]; NSURL *url = [NSURL URLWithString:bootstrapPath]; OurLog(@"Downloading file: %@", bootstrapPath); NSString *downloadLocation = [thePath stringByAppendingPathComponent:bootstrapPath.lastPathComponent]; self.downloader = [URLDownloader new]; [self.downloader downloadFileWithURL:url toLocation:downloadLocation withCredential:nil completed:^(NSString *downloadedFile) { OurLog(@"Download complete: %@", downloadedFile); OurLog(@"Installing..."); NSString *tarPath = [self tarPathForCurrentMode]; OurLog(@"Extracting tar..."); NSString *installString = [NSString stringWithFormat:@"%@ fxpv %@ -C / ; echo \"Starting Substrate\" ; /usr/libexec/substrate ; echo \"running uicache...\" ; /usr/bin/uicache ; /usr/bin/bash /usr/libexec/nito/firmware.sh ; rm /var/mobile/Library/Preferences/Featured.plist", tarPath, downloadedFile]; OurLog(@"install string: %@", installString); [nitoInstaller returnForProcess:installString]; if (self.versionState == InstallVersionStateNine) { [nitoInstaller returnForProcess:@"/usr/bin/killall -9 PineBoard HeadBoard lsd nitoTV"]; sleep(35); } else { sleep(10); } [self openApp:@"com.nito.nitoTV4"]; CFRunLoopStop(CFRunLoopGetCurrent()); }]; } @end