From 2ac6bc280fcc092cc58b30f4fafdf508fc6a3a29 Mon Sep 17 00:00:00 2001 From: Dhinak G <17605561+dhinakg@users.noreply.github.com> Date: Thu, 30 Nov 2023 19:51:51 -0500 Subject: [PATCH 01/15] Add code to check and arm developer mode --- RootHelper/devmode.h | 4 + RootHelper/devmode.m | 139 ++++++++++++++++++++++++++ RootHelper/main.m | 55 +++++++++- TrollStore/TSApplicationsManager.m | 6 ++ TrollStore/TSInstallationController.m | 67 ++++++++----- 5 files changed, 244 insertions(+), 27 deletions(-) create mode 100644 RootHelper/devmode.h create mode 100644 RootHelper/devmode.m diff --git a/RootHelper/devmode.h b/RootHelper/devmode.h new file mode 100644 index 0000000..9343b61 --- /dev/null +++ b/RootHelper/devmode.h @@ -0,0 +1,4 @@ +#import + +BOOL checkDeveloperMode(void); +BOOL armDeveloperMode(BOOL* alreadyEnabled); \ No newline at end of file diff --git a/RootHelper/devmode.m b/RootHelper/devmode.m new file mode 100644 index 0000000..7020311 --- /dev/null +++ b/RootHelper/devmode.m @@ -0,0 +1,139 @@ +@import Foundation; + +// Types +typedef NSObject* xpc_object_t; +typedef xpc_object_t xpc_connection_t; +typedef void (^xpc_handler_t)(xpc_object_t object); + +// Serialization +extern CFTypeRef _CFXPCCreateCFObjectFromXPCObject(xpc_object_t xpcattrs); +extern xpc_object_t _CFXPCCreateXPCObjectFromCFObject(CFTypeRef attrs); +extern xpc_object_t _CFXPCCreateXPCMessageWithCFObject(CFTypeRef obj); +extern CFTypeRef _CFXPCCreateCFObjectFromXPCMessage(xpc_object_t obj); + +// Communication +extern xpc_connection_t xpc_connection_create_mach_service(const char* name, dispatch_queue_t targetq, uint64_t flags); +extern void xpc_connection_set_event_handler(xpc_connection_t connection, xpc_handler_t handler); +extern void xpc_connection_resume(xpc_connection_t connection); +extern void xpc_connection_send_message_with_reply(xpc_connection_t connection, xpc_object_t message, dispatch_queue_t replyq, xpc_handler_t handler); +extern xpc_object_t xpc_connection_send_message_with_reply_sync(xpc_connection_t connection, xpc_object_t message); +extern xpc_object_t xpc_dictionary_get_value(xpc_object_t xdict, const char *key); + +typedef enum { + kAMFIActionArm = 0, + kAMFIActionDisable = 1, + kAMFIActionStatus = 2, +} AMFIXPCAction; + +xpc_connection_t startConnection(void) { + xpc_connection_t connection = xpc_connection_create_mach_service("com.apple.amfi.xpc", NULL, 0); + if (!connection) { + NSLog(@"[startXPCConnection] Failed to create XPC connection to amfid"); + return nil; + } + xpc_connection_set_event_handler(connection, ^(xpc_object_t event) { + }); + xpc_connection_resume(connection); + return connection; +} + +NSDictionary* sendXPCRequest(xpc_connection_t connection, AMFIXPCAction action) { + xpc_object_t message = _CFXPCCreateXPCMessageWithCFObject((__bridge CFDictionaryRef) @{@"action": @(action)}); + xpc_object_t replyMsg = xpc_connection_send_message_with_reply_sync(connection, message); + if (!replyMsg) { + NSLog(@"[sendXPCRequest] got no reply from amfid"); + return nil; + } + + xpc_object_t replyObj = xpc_dictionary_get_value(replyMsg, "cfreply"); + if (!replyObj) { + NSLog(@"[sendXPCRequest] got reply but no cfreply"); + return nil; + } + + NSDictionary* asCF = (__bridge NSDictionary*)_CFXPCCreateCFObjectFromXPCMessage(replyObj); + return asCF; +} + +BOOL getDeveloperModeState(xpc_connection_t connection) { + NSDictionary* reply = sendXPCRequest(connection, kAMFIActionStatus); + if (!reply) { + NSLog(@"[getDeveloperModeState] failed to get reply"); + return NO; + } + + NSObject* success = reply[@"success"]; + if (!success || ![success isKindOfClass:[NSNumber class]] || ![(NSNumber*)success boolValue]) { + NSLog(@"[getDeveloperModeState] request failed with error %@", reply[@"error"]); + return NO; + } + + NSObject* status = reply[@"status"]; + if (!status || ![status isKindOfClass:[NSNumber class]]) { + NSLog(@"[getDeveloperModeState] request succeeded but no status"); + return NO; + } + + return [(NSNumber*)status boolValue]; +} + +BOOL setDeveloperModeState(xpc_connection_t connection, BOOL enable) { + NSDictionary* reply = sendXPCRequest(connection, enable ? kAMFIActionArm : kAMFIActionDisable); + if (!reply) { + NSLog(@"[setDeveloperModeState] failed to get reply"); + return NO; + } + + NSObject* success = reply[@"success"]; + if (!success || ![success isKindOfClass:[NSNumber class]] || ![(NSNumber*)success boolValue]) { + NSLog(@"[setDeveloperModeState] request failed with error %@", reply[@"error"]); + return NO; + } + + return YES; +} + +BOOL checkDeveloperMode(void) { + // Developer mode does not exist before iOS 16 + if (@available(iOS 16, *)) { + xpc_connection_t connection = startConnection(); + if (!connection) { + NSLog(@"[checkDeveloperMode] failed to start connection"); + // Assume it's disabled + return NO; + } + + return getDeveloperModeState(connection); + } else { + return YES; + } +} + +BOOL armDeveloperMode(BOOL* alreadyEnabled) { + // Developer mode does not exist before iOS 16 + if (@available(iOS 16, *)) { + xpc_connection_t connection = startConnection(); + if (!connection) { + NSLog(@"[armDeveloperMode] failed to start connection"); + return NO; + } + + BOOL enabled = getDeveloperModeState(connection); + if (alreadyEnabled) { + *alreadyEnabled = enabled; + } + + if (enabled) { + // NSLog(@"[armDeveloperMode] already enabled"); + return YES; + } + + BOOL success = setDeveloperModeState(connection, YES); + if (!success) { + NSLog(@"[armDeveloperMode] failed to arm"); + return NO; + } + } + + return YES; +} diff --git a/RootHelper/main.m b/RootHelper/main.m index 74dd0ab..a07d769 100644 --- a/RootHelper/main.m +++ b/RootHelper/main.m @@ -10,6 +10,7 @@ #import #import #import +#import "devmode.h" #ifndef EMBEDDED_ROOT_HELPER #import "codesign.h" #import "coretrust_bug.h" @@ -564,6 +565,10 @@ int signApp(NSString* appPath) } } + // On iOS 16+, any binary with get-task-allow requires developer mode to be enabled, so we will check + // while we're at it + BOOL requiresDevMode = NO; + NSURL* fileURL; NSDirectoryEnumerator *enumerator; @@ -608,6 +613,14 @@ int signApp(NSString* appPath) if (!entitlementsToUse) entitlementsToUse = [NSMutableDictionary new]; + // Developer mode does not exist before iOS 16 + if (@available(iOS 16, *)){ + NSObject *getTaskAllowO = entitlementsToUse[@"get-task-allow"]; + if (getTaskAllowO && [getTaskAllowO isKindOfClass:[NSNumber class]]) { + requiresDevMode |= [(NSNumber *)getTaskAllowO boolValue]; + } + } + NSObject *containerRequiredO = entitlementsToUse[@"com.apple.private.security.container-required"]; BOOL containerRequired = YES; if (containerRequiredO && [containerRequiredO isKindOfClass:[NSNumber class]]) { @@ -681,6 +694,11 @@ int signApp(NSString* appPath) } } + if (requiresDevMode) { + // Postpone trying to enable dev mode until after the app is (successfully) installed + return 180; + } + return 0; } #endif @@ -764,10 +782,19 @@ int installApp(NSString* appPackagePath, BOOL sign, BOOL force, BOOL isTSUpdate, applyPatchesToInfoDictionary(appBundleToInstallPath); } + BOOL requiresDevMode = NO; + if(sign) { int signRet = signApp(appBundleToInstallPath); - if(signRet != 0) return signRet; + // 180: app requires developer mode; non-fatal + if(signRet != 0) { + if (signRet == 180) { + requiresDevMode = YES; + } else { + return signRet; + } + }; } MCMAppContainer* appContainer = [MCMAppContainer containerWithIdentifier:appId createIfNecessary:NO existed:nil error:nil]; @@ -910,6 +937,23 @@ int installApp(NSString* appPackagePath, BOOL sign, BOOL force, BOOL isTSUpdate, NSURL* updatedAppURL = findAppURLInBundleURL(appContainer.url); fixPermissionsOfAppBundle(updatedAppURL.path); registerPath(updatedAppURL.path, 0, YES); + + // Handle developer mode after installing and registering the app, to ensure that we + // don't arm developer mode but then fail to install the app + if (requiresDevMode) { + BOOL alreadyEnabled = NO; + if (armDeveloperMode(&alreadyEnabled)) { + if (!alreadyEnabled) { + NSLog(@"[installApp] app requires developer mode and we have successfully armed it"); + // non-fatal + return 180; + } + } else { + NSLog(@"[installApp] failed to arm developer mode"); + // fatal + return 181; + } + } return 0; } @@ -1460,6 +1504,15 @@ int MAIN_NAME(int argc, char *argv[], char *envp[]) setTSURLSchemeState(newState, nil); } } + else if([cmd isEqualToString:@"check-dev-mode"]) + { + ret = checkDeveloperMode(); + } + else if([cmd isEqualToString:@"arm-dev-mode"]) + { + // assumes that checkDeveloperMode() has already been called + ret = armDeveloperMode(NULL); + } NSLog(@"trollstorehelper returning %d", ret); return ret; diff --git a/TrollStore/TSApplicationsManager.m b/TrollStore/TSApplicationsManager.m index 5b77e9f..5f504fc 100644 --- a/TrollStore/TSApplicationsManager.m +++ b/TrollStore/TSApplicationsManager.m @@ -74,6 +74,12 @@ extern NSUserDefaults* trollStoreUserDefaults(); case 179: errorDescription = @"The app you tried to install has the same identifier as a system app already installed on the device. The installation has been prevented to protect you from possible bootloops or other issues."; break; + case 180: + errorDescription = @"The app was installed successfully, but requires developer mode to be enabled to run."; + break; + case 181: + errorDescription = @"Failed to enable developer mode."; + break; } NSError* error = [NSError errorWithDomain:TrollStoreErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : errorDescription}]; diff --git a/TrollStore/TSInstallationController.m b/TrollStore/TSInstallationController.m index 0b3e674..5b2ad21 100644 --- a/TrollStore/TSInstallationController.m +++ b/TrollStore/TSInstallationController.m @@ -32,42 +32,57 @@ extern NSUserDefaults* trollStoreUserDefaults(void); { [TSPresentationDelegate stopActivityWithCompletion:^ { - if(ret != 0) - { + if (ret == 0) { + // success + if(completionBlock) completionBlock(YES, nil); + } else if (ret == 171) { + // recoverable error UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Install Error %d", ret] message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) { - if(ret == 171) - { - if(completionBlock) completionBlock(NO, error); - } + if(completionBlock) completionBlock(NO, error); }]; [errorAlert addAction:closeAction]; - if(ret == 171) + UIAlertAction* forceInstallAction = [UIAlertAction actionWithTitle:@"Force Installation" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) { - UIAlertAction* forceInstallAction = [UIAlertAction actionWithTitle:@"Force Installation" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) - { - [self handleAppInstallFromFile:pathToIPA forceInstall:YES completion:completionBlock]; - }]; - [errorAlert addAction:forceInstallAction]; - } - else - { - UIAlertAction* copyLogAction = [UIAlertAction actionWithTitle:@"Copy Debug Log" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) - { - UIPasteboard* pasteboard = [UIPasteboard generalPasteboard]; - pasteboard.string = log; - }]; - [errorAlert addAction:copyLogAction]; - } + [self handleAppInstallFromFile:pathToIPA forceInstall:YES completion:completionBlock]; + }]; + [errorAlert addAction:forceInstallAction]; [TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil]; - } + } else if (ret == 180) { + // non-fatal informative message + UIAlertController* rebootNotification = [UIAlertController alertControllerWithTitle:@"Reboot Required" message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) + { + if(completionBlock) completionBlock(YES, nil); + }]; + [rebootNotification addAction:closeAction]; - if(ret != 171) - { - if(completionBlock) completionBlock((BOOL)error, error); + UIAlertAction* rebootAction = [UIAlertAction actionWithTitle:@"Reboot Now" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) + { + if(completionBlock) completionBlock(YES, nil); + }]; + [rebootNotification addAction:rebootAction]; + + [TSPresentationDelegate presentViewController:rebootNotification animated:YES completion:nil]; + } else { + // unrecoverable error + UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Install Error %d", ret] message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil]; + [errorAlert addAction:closeAction]; + + UIAlertAction* copyLogAction = [UIAlertAction actionWithTitle:@"Copy Debug Log" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) + { + UIPasteboard* pasteboard = [UIPasteboard generalPasteboard]; + pasteboard.string = log; + }]; + [errorAlert addAction:copyLogAction]; + + [TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil]; + + if(completionBlock) completionBlock(NO, error); } }]; }); From f57326e0a4949163170eab3bd77c1ece699fae40 Mon Sep 17 00:00:00 2001 From: Dhinak G <17605561+dhinakg@users.noreply.github.com> Date: Thu, 30 Nov 2023 19:52:00 -0500 Subject: [PATCH 02/15] Show developer mode status in app info --- TrollStore/TSAppInfo.m | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/TrollStore/TSAppInfo.m b/TrollStore/TSAppInfo.m index 952be1a..3900481 100644 --- a/TrollStore/TSAppInfo.m +++ b/TrollStore/TSAppInfo.m @@ -1012,6 +1012,23 @@ extern UIImage* imageWithSize(UIImage* image, CGSize size); } }]; + // Check if any bundles have get-task-allow, which require dev mode enabled on 16+ + __block BOOL needsDevMode = NO; + if (@available(iOS 16.0, *)) { + [self enumerateAllEntitlements:^(NSString *key, NSObject *value, BOOL *stop) + { + if([key isEqualToString:@"get-task-allow"]) + { + NSNumber* valueNum = (NSNumber*)value; + if(valueNum && [valueNum isKindOfClass:NSNumber.class]) + { + hasPersonaMngmt = valueNum.boolValue; + if(hasPersonaMngmt) *stop = YES; + } + } + }]; + } + NSMutableParagraphStyle* leftAlignment = [[NSMutableParagraphStyle alloc] init]; leftAlignment.alignment = NSTextAlignmentLeft; @@ -1068,6 +1085,7 @@ extern UIImage* imageWithSize(UIImage* image, CGSize size); [description appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"\nBundle Identifier: %@", bundleId] attributes:bodyAttributes]]; [description appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"\nVersion: %@", version] attributes:bodyAttributes]]; [description appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"\nSize: %@", sizeString] attributes:bodyAttributes]]; + [description appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"\nRequires Developer Mode: %@", needsDevMode ? @"Yes" : @"No"] attributes:bodyAttributes]]; [description appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n\nSandboxing" attributes:headerAttributes]]; if(isUnsandboxed) From c130a04ff529dd395e50070958e45f29b0826a27 Mon Sep 17 00:00:00 2001 From: Dhinak G <17605561+dhinakg@users.noreply.github.com> Date: Thu, 30 Nov 2023 20:19:06 -0500 Subject: [PATCH 03/15] Fix copy & paste typo --- TrollStore/TSAppInfo.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TrollStore/TSAppInfo.m b/TrollStore/TSAppInfo.m index 3900481..a4b7dfe 100644 --- a/TrollStore/TSAppInfo.m +++ b/TrollStore/TSAppInfo.m @@ -1022,8 +1022,8 @@ extern UIImage* imageWithSize(UIImage* image, CGSize size); NSNumber* valueNum = (NSNumber*)value; if(valueNum && [valueNum isKindOfClass:NSNumber.class]) { - hasPersonaMngmt = valueNum.boolValue; - if(hasPersonaMngmt) *stop = YES; + needsDevMode = valueNum.boolValue; + if(needsDevMode) *stop = YES; } } }]; From 5eecb677a7bd16ca887db61fc323da51e9b1320f Mon Sep 17 00:00:00 2001 From: Dhinak G <17605561+dhinakg@users.noreply.github.com> Date: Thu, 30 Nov 2023 20:34:56 -0500 Subject: [PATCH 04/15] Document the actions --- RootHelper/devmode.m | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/RootHelper/devmode.m b/RootHelper/devmode.m index 7020311..42761d8 100644 --- a/RootHelper/devmode.m +++ b/RootHelper/devmode.m @@ -20,9 +20,10 @@ extern xpc_object_t xpc_connection_send_message_with_reply_sync(xpc_connection_t extern xpc_object_t xpc_dictionary_get_value(xpc_object_t xdict, const char *key); typedef enum { - kAMFIActionArm = 0, - kAMFIActionDisable = 1, - kAMFIActionStatus = 2, + kAMFIActionArm = 0, // Trigger a prompt asking the user to enable developer mode on the next reboot + // (regardless of current state) + kAMFIActionDisable = 1, // Disable developer mode if it's currently enabled. Takes effect immediately. + kAMFIActionStatus = 2, // Returns a dict: {success: bool, status: bool, armed: bool} } AMFIXPCAction; xpc_connection_t startConnection(void) { From a56bf738bd4dae2e6ea8761d0812689b66e82cc1 Mon Sep 17 00:00:00 2001 From: Dhinak G <17605561+dhinakg@users.noreply.github.com> Date: Sat, 2 Dec 2023 23:45:41 -0500 Subject: [PATCH 05/15] Add in other restricted entitlements --- RootHelper/main.m | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/RootHelper/main.m b/RootHelper/main.m index a07d769..6f99537 100644 --- a/RootHelper/main.m +++ b/RootHelper/main.m @@ -565,8 +565,8 @@ int signApp(NSString* appPath) } } - // On iOS 16+, any binary with get-task-allow requires developer mode to be enabled, so we will check - // while we're at it + // On iOS 16+, binaries with certain entitlements requires developer mode to be enabled, so we'll check + // while we're fixing entitlements BOOL requiresDevMode = NO; NSURL* fileURL; @@ -615,9 +615,21 @@ int signApp(NSString* appPath) // Developer mode does not exist before iOS 16 if (@available(iOS 16, *)){ - NSObject *getTaskAllowO = entitlementsToUse[@"get-task-allow"]; - if (getTaskAllowO && [getTaskAllowO isKindOfClass:[NSNumber class]]) { - requiresDevMode |= [(NSNumber *)getTaskAllowO boolValue]; + if (!requiresDevMode) { + for (NSString* restrictedEntitlementKey in @[ + @"get-task-allow", + @"task_for_pid-allow", + @"com.apple.system-task-ports", + @"com.apple.system-task-ports.control", + @"com.apple.system-task-ports.token.control", + @"com.apple.private.cs.debugger" + ]) { + NSObject *restrictedEntitlement = entitlementsToUse[restrictedEntitlementKey]; + if (restrictedEntitlement && [restrictedEntitlement isKindOfClass:[NSNumber class]]) { + requiresDevMode |= [(NSNumber *)restrictedEntitlement boolValue]; + break; + } + } } } From afb45b110eff6957aaa42ee759a2845c3ae4b02e Mon Sep 17 00:00:00 2001 From: Dhinak G <17605561+dhinakg@users.noreply.github.com> Date: Thu, 21 Dec 2023 21:38:47 -0500 Subject: [PATCH 06/15] Fix issue in entitlement checking --- RootHelper/main.m | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/RootHelper/main.m b/RootHelper/main.m index 6f99537..565fdb8 100644 --- a/RootHelper/main.m +++ b/RootHelper/main.m @@ -625,9 +625,8 @@ int signApp(NSString* appPath) @"com.apple.private.cs.debugger" ]) { NSObject *restrictedEntitlement = entitlementsToUse[restrictedEntitlementKey]; - if (restrictedEntitlement && [restrictedEntitlement isKindOfClass:[NSNumber class]]) { - requiresDevMode |= [(NSNumber *)restrictedEntitlement boolValue]; - break; + if (restrictedEntitlement && [restrictedEntitlement isKindOfClass:[NSNumber class]] && [(NSNumber *)restrictedEntitlement boolValue]) { + requiresDevMode = YES; } } } From d502576e1fd21916d7e0ecc88473fb8b256c51dd Mon Sep 17 00:00:00 2001 From: Dhinak G <17605561+dhinakg@users.noreply.github.com> Date: Thu, 21 Dec 2023 21:39:31 -0500 Subject: [PATCH 07/15] Remove dev mode from app info --- TrollStore/TSAppInfo.m | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/TrollStore/TSAppInfo.m b/TrollStore/TSAppInfo.m index a4b7dfe..5e8017c 100644 --- a/TrollStore/TSAppInfo.m +++ b/TrollStore/TSAppInfo.m @@ -1012,23 +1012,6 @@ extern UIImage* imageWithSize(UIImage* image, CGSize size); } }]; - // Check if any bundles have get-task-allow, which require dev mode enabled on 16+ - __block BOOL needsDevMode = NO; - if (@available(iOS 16.0, *)) { - [self enumerateAllEntitlements:^(NSString *key, NSObject *value, BOOL *stop) - { - if([key isEqualToString:@"get-task-allow"]) - { - NSNumber* valueNum = (NSNumber*)value; - if(valueNum && [valueNum isKindOfClass:NSNumber.class]) - { - needsDevMode = valueNum.boolValue; - if(needsDevMode) *stop = YES; - } - } - }]; - } - NSMutableParagraphStyle* leftAlignment = [[NSMutableParagraphStyle alloc] init]; leftAlignment.alignment = NSTextAlignmentLeft; @@ -1085,8 +1068,6 @@ extern UIImage* imageWithSize(UIImage* image, CGSize size); [description appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"\nBundle Identifier: %@", bundleId] attributes:bodyAttributes]]; [description appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"\nVersion: %@", version] attributes:bodyAttributes]]; [description appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"\nSize: %@", sizeString] attributes:bodyAttributes]]; - [description appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"\nRequires Developer Mode: %@", needsDevMode ? @"Yes" : @"No"] attributes:bodyAttributes]]; - [description appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n\nSandboxing" attributes:headerAttributes]]; if(isUnsandboxed) { From f1f42778d8e0123752407fa9b4e7d5cadbe69eb5 Mon Sep 17 00:00:00 2001 From: Dhinak G <17605561+dhinakg@users.noreply.github.com> Date: Thu, 21 Dec 2023 23:21:26 -0500 Subject: [PATCH 08/15] Invert output from RootHelper --- RootHelper/main.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/RootHelper/main.m b/RootHelper/main.m index 565fdb8..48b58ef 100644 --- a/RootHelper/main.m +++ b/RootHelper/main.m @@ -1517,12 +1517,13 @@ int MAIN_NAME(int argc, char *argv[], char *envp[]) } else if([cmd isEqualToString:@"check-dev-mode"]) { - ret = checkDeveloperMode(); + // switch the result, so 0 is enabled, and 1 is disabled/error + ret = !checkDeveloperMode(); } else if([cmd isEqualToString:@"arm-dev-mode"]) { // assumes that checkDeveloperMode() has already been called - ret = armDeveloperMode(NULL); + ret = !armDeveloperMode(NULL); } NSLog(@"trollstorehelper returning %d", ret); From 8dc50d755596a1b608cd5fcb7c7cec654b12891f Mon Sep 17 00:00:00 2001 From: Dhinak G <17605561+dhinakg@users.noreply.github.com> Date: Fri, 22 Dec 2023 00:08:23 -0500 Subject: [PATCH 09/15] Add reboot code --- TrollStore/TSApplicationsManager.m | 2 +- TrollStore/TSInstallationController.m | 1 + TrollStore/TSSettingsListController.h | 1 + TrollStore/TSSettingsListController.m | 60 +++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 1 deletion(-) diff --git a/TrollStore/TSApplicationsManager.m b/TrollStore/TSApplicationsManager.m index 5f504fc..5ce2010 100644 --- a/TrollStore/TSApplicationsManager.m +++ b/TrollStore/TSApplicationsManager.m @@ -75,7 +75,7 @@ extern NSUserDefaults* trollStoreUserDefaults(); errorDescription = @"The app you tried to install has the same identifier as a system app already installed on the device. The installation has been prevented to protect you from possible bootloops or other issues."; break; case 180: - errorDescription = @"The app was installed successfully, but requires developer mode to be enabled to run."; + errorDescription = @"The app was installed successfully, but requires developer mode to be enabled to run. After rebooting, select \"Turn On\" to enable developer mode."; break; case 181: errorDescription = @"Failed to enable developer mode."; diff --git a/TrollStore/TSInstallationController.m b/TrollStore/TSInstallationController.m index 5b2ad21..fef966b 100644 --- a/TrollStore/TSInstallationController.m +++ b/TrollStore/TSInstallationController.m @@ -63,6 +63,7 @@ extern NSUserDefaults* trollStoreUserDefaults(void); UIAlertAction* rebootAction = [UIAlertAction actionWithTitle:@"Reboot Now" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) { if(completionBlock) completionBlock(YES, nil); + spawnRoot(rootHelperPath(), @[@"reboot"], nil, nil); }]; [rebootNotification addAction:rebootAction]; diff --git a/TrollStore/TSSettingsListController.h b/TrollStore/TSSettingsListController.h index fc573ee..9cc397e 100644 --- a/TrollStore/TSSettingsListController.h +++ b/TrollStore/TSSettingsListController.h @@ -5,5 +5,6 @@ PSSpecifier* _installPersistenceHelperSpecifier; NSString* _newerVersion; NSString* _newerLdidVersion; + BOOL _devModeEnabled; } @end \ No newline at end of file diff --git a/TrollStore/TSSettingsListController.m b/TrollStore/TSSettingsListController.m index 6a97299..6e46dd7 100644 --- a/TrollStore/TSSettingsListController.m +++ b/TrollStore/TSSettingsListController.m @@ -55,6 +55,15 @@ extern NSUserDefaults* trollStoreUserDefaults(void); } }); //} + + if (@available(iOS 16, *)) + { + _devModeEnabled = spawnRoot(rootHelperPath(), @[@"check-dev-mode"], nil, nil) == 0; + } + else + { + _devModeEnabled = YES; + } } - (NSMutableArray*)specifiers @@ -82,6 +91,26 @@ extern NSUserDefaults* trollStoreUserDefaults(void); [_specifiers addObject:updateTrollStoreSpecifier]; } + if(!_devModeEnabled) + { + PSSpecifier* enableDevModeGroupSpecifier = [PSSpecifier emptyGroupSpecifier]; + enableDevModeGroupSpecifier.name = @"Developer Mode"; + [enableDevModeGroupSpecifier setProperty:@"Some apps require developer mode enabled to launch. This requires a reboot to take effect." forKey:@"footerText"]; + [_specifiers addObject:enableDevModeGroupSpecifier]; + + PSSpecifier* enableDevModeSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Enable Developer Mode" + target:self + set:nil + get:nil + detail:nil + cell:PSButtonCell + edit:nil]; + enableDevModeSpecifier.identifier = @"enableDevMode"; + [enableDevModeSpecifier setProperty:@YES forKey:@"enabled"]; + enableDevModeSpecifier.buttonAction = @selector(enableDevModePressed); + [_specifiers addObject:enableDevModeSpecifier]; + } + PSSpecifier* utilitiesGroupSpecifier = [PSSpecifier emptyGroupSpecifier]; utilitiesGroupSpecifier.name = @"Utilities"; [utilitiesGroupSpecifier setProperty:@"If an app does not immediately appear after installation, respring here and it should appear afterwards." forKey:@"footerText"]; @@ -369,6 +398,37 @@ extern NSUserDefaults* trollStoreUserDefaults(void); [TSInstallationController installLdid]; } +- (void)enableDevModePressed +{ + int ret = spawnRoot(rootHelperPath(), @[@"arm-dev-mode"], nil, nil); + + if (ret == 0) { + UIAlertController* rebootNotification = [UIAlertController alertControllerWithTitle:@"Reboot Required" + message:@"After rebooting, select \"Turn On\" to enable developer mode." + preferredStyle:UIAlertControllerStyleAlert + ]; + UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) + { + [self reloadSpecifiers]; + }]; + [rebootNotification addAction:closeAction]; + + UIAlertAction* rebootAction = [UIAlertAction actionWithTitle:@"Reboot Now" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) + { + spawnRoot(rootHelperPath(), @[@"reboot"], nil, nil); + }]; + [rebootNotification addAction:rebootAction]; + + [TSPresentationDelegate presentViewController:rebootNotification animated:YES completion:nil]; + } else { + UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Error %d", ret] message:@"Failed to enable developer mode." preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil]; + [errorAlert addAction:closeAction]; + + [TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil]; + } +} + - (void)installPersistenceHelperPressed { NSMutableArray* appCandidates = [NSMutableArray new]; From 28aab08decc8017fcfb58ebcd19154f29eb5b474 Mon Sep 17 00:00:00 2001 From: Dhinak G <17605561+dhinakg@users.noreply.github.com> Date: Fri, 22 Dec 2023 00:27:09 -0500 Subject: [PATCH 10/15] Fix last --- RootHelper/main.m | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/RootHelper/main.m b/RootHelper/main.m index 48b58ef..8a62337 100644 --- a/RootHelper/main.m +++ b/RootHelper/main.m @@ -50,6 +50,8 @@ typedef CFDictionaryRef (*_CFPreferencesCopyMultipleWithContainerType)(CFArrayRe BOOL _installPersistenceHelper(LSApplicationProxy* appProxy, NSString* sourcePersistenceHelper, NSString* sourceRootHelper); +extern int reboot3(uint64_t flags, ...); + NSArray* applicationsWithGroupId(NSString* groupId) { LSEnumerator* enumerator = [LSEnumerator enumeratorForApplicationProxiesWithOptions:0]; @@ -1525,6 +1527,11 @@ int MAIN_NAME(int argc, char *argv[], char *envp[]) // assumes that checkDeveloperMode() has already been called ret = !armDeveloperMode(NULL); } + else if([cmd isEqualToString:@"reboot"]) + { + sync(); + ret = reboot3(0); // do a normal reboot + } NSLog(@"trollstorehelper returning %d", ret); return ret; From eed1d42792d4dcd47abe45e1020a74ea2ca2f952 Mon Sep 17 00:00:00 2001 From: Dhinak G <17605561+dhinakg@users.noreply.github.com> Date: Sat, 30 Dec 2023 13:23:54 -0500 Subject: [PATCH 11/15] Fix rebooting reboot3 requires platformization. Use FrontBoardServices to do it instead --- RootHelper/devmode.m | 2 ++ RootHelper/entitlements.plist | 4 ++++ RootHelper/main.m | 8 ++++---- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/RootHelper/devmode.m b/RootHelper/devmode.m index 42761d8..241ad0e 100644 --- a/RootHelper/devmode.m +++ b/RootHelper/devmode.m @@ -63,6 +63,8 @@ BOOL getDeveloperModeState(xpc_connection_t connection) { return NO; } + NSLog(@"[getDeveloperModeState] got reply %@", reply); + NSObject* success = reply[@"success"]; if (!success || ![success isKindOfClass:[NSNumber class]] || ![(NSNumber*)success boolValue]) { NSLog(@"[getDeveloperModeState] request failed with error %@", reply[@"error"]); diff --git a/RootHelper/entitlements.plist b/RootHelper/entitlements.plist index 70c45bc..3319af0 100644 --- a/RootHelper/entitlements.plist +++ b/RootHelper/entitlements.plist @@ -44,5 +44,9 @@ Uninstall UpdatePlaceholderMetadata + com.apple.private.amfi.developer-mode-control + + com.apple.frontboard.shutdown + diff --git a/RootHelper/main.m b/RootHelper/main.m index 8a62337..c178ce9 100644 --- a/RootHelper/main.m +++ b/RootHelper/main.m @@ -21,6 +21,7 @@ #endif #import +#import #import #ifdef EMBEDDED_ROOT_HELPER @@ -50,8 +51,6 @@ typedef CFDictionaryRef (*_CFPreferencesCopyMultipleWithContainerType)(CFArrayRe BOOL _installPersistenceHelper(LSApplicationProxy* appProxy, NSString* sourcePersistenceHelper, NSString* sourceRootHelper); -extern int reboot3(uint64_t flags, ...); - NSArray* applicationsWithGroupId(NSString* groupId) { LSEnumerator* enumerator = [LSEnumerator enumeratorForApplicationProxiesWithOptions:0]; @@ -1529,8 +1528,9 @@ int MAIN_NAME(int argc, char *argv[], char *envp[]) } else if([cmd isEqualToString:@"reboot"]) { - sync(); - ret = reboot3(0); // do a normal reboot + [[FBSSystemService sharedService] reboot]; + // Give the system some time to reboot + sleep(1); } NSLog(@"trollstorehelper returning %d", ret); From 34744681898dd21b6f331735d70fa9a5cab8c6e2 Mon Sep 17 00:00:00 2001 From: Dhinak G <17605561+dhinakg@users.noreply.github.com> Date: Sat, 30 Dec 2023 13:32:02 -0500 Subject: [PATCH 12/15] Fix dev mode specifier always showing up --- TrollStore/TSSettingsListController.m | 1 + 1 file changed, 1 insertion(+) diff --git a/TrollStore/TSSettingsListController.m b/TrollStore/TSSettingsListController.m index 6e46dd7..d9037cd 100644 --- a/TrollStore/TSSettingsListController.m +++ b/TrollStore/TSSettingsListController.m @@ -64,6 +64,7 @@ extern NSUserDefaults* trollStoreUserDefaults(void); { _devModeEnabled = YES; } + [self reloadSpecifiers]; } - (NSMutableArray*)specifiers From fa948c0646e75c70078400650dd7605bd8b21e5d Mon Sep 17 00:00:00 2001 From: Dhinak G <17605561+dhinakg@users.noreply.github.com> Date: Sat, 30 Dec 2023 13:38:09 -0500 Subject: [PATCH 13/15] Fix last --- RootHelper/entitlements.plist | 4 ++-- TrollStore/TSAppInfo.m | 1 + TrollStore/TSInstallationController.m | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/RootHelper/entitlements.plist b/RootHelper/entitlements.plist index 3319af0..2a0d842 100644 --- a/RootHelper/entitlements.plist +++ b/RootHelper/entitlements.plist @@ -46,7 +46,7 @@ com.apple.private.amfi.developer-mode-control - com.apple.frontboard.shutdown - + com.apple.frontboard.shutdown + diff --git a/TrollStore/TSAppInfo.m b/TrollStore/TSAppInfo.m index 5e8017c..952be1a 100644 --- a/TrollStore/TSAppInfo.m +++ b/TrollStore/TSAppInfo.m @@ -1068,6 +1068,7 @@ extern UIImage* imageWithSize(UIImage* image, CGSize size); [description appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"\nBundle Identifier: %@", bundleId] attributes:bodyAttributes]]; [description appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"\nVersion: %@", version] attributes:bodyAttributes]]; [description appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"\nSize: %@", sizeString] attributes:bodyAttributes]]; + [description appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n\nSandboxing" attributes:headerAttributes]]; if(isUnsandboxed) { diff --git a/TrollStore/TSInstallationController.m b/TrollStore/TSInstallationController.m index fef966b..5c4d2eb 100644 --- a/TrollStore/TSInstallationController.m +++ b/TrollStore/TSInstallationController.m @@ -51,7 +51,7 @@ extern NSUserDefaults* trollStoreUserDefaults(void); [errorAlert addAction:forceInstallAction]; [TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil]; - } else if (ret == 180) { + } else if (ret == 182) { // non-fatal informative message UIAlertController* rebootNotification = [UIAlertController alertControllerWithTitle:@"Reboot Required" message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) From 9f9fd76310ccee0c700c92bf4195f8158bab0b96 Mon Sep 17 00:00:00 2001 From: Dhinak G <17605561+dhinakg@users.noreply.github.com> Date: Sat, 30 Dec 2023 13:40:05 -0500 Subject: [PATCH 14/15] Add FrontBoardServices to Makefiles --- RootHelper/Makefile | 2 +- TrollHelper/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RootHelper/Makefile b/RootHelper/Makefile index 2aeb2fc..b9c936f 100644 --- a/RootHelper/Makefile +++ b/RootHelper/Makefile @@ -14,6 +14,6 @@ trollstorehelper_CODESIGN_FLAGS = --entitlements entitlements.plist trollstorehelper_INSTALL_PATH = /usr/local/bin trollstorehelper_LIBRARIES = archive trollstorehelper_FRAMEWORKS = CoreTelephony -trollstorehelper_PRIVATE_FRAMEWORKS = SpringBoardServices BackBoardServices MobileContainerManager +trollstorehelper_PRIVATE_FRAMEWORKS = SpringBoardServices BackBoardServices MobileContainerManager FrontBoardServices include $(THEOS_MAKE_PATH)/tool.mk diff --git a/TrollHelper/Makefile b/TrollHelper/Makefile index 0a01c68..812e4e3 100644 --- a/TrollHelper/Makefile +++ b/TrollHelper/Makefile @@ -34,7 +34,7 @@ ifeq ($(EMBEDDED_ROOT_HELPER),1) TrollStorePersistenceHelper_CFLAGS += -DEMBEDDED_ROOT_HELPER=1 TrollStorePersistenceHelper_FILES += $(wildcard ../RootHelper/*.m) TrollStorePersistenceHelper_LIBRARIES += archive -TrollStorePersistenceHelper_PRIVATE_FRAMEWORKS += SpringBoardServices BackBoardServices +TrollStorePersistenceHelper_PRIVATE_FRAMEWORKS += SpringBoardServices BackBoardServices FrontBoardServices endif include $(THEOS_MAKE_PATH)/application.mk \ No newline at end of file From c1090cf7904b33c7af5d8c2f4468d7bb7cb0ada9 Mon Sep 17 00:00:00 2001 From: Dhinak G <17605561+dhinakg@users.noreply.github.com> Date: Sat, 30 Dec 2023 14:15:26 -0500 Subject: [PATCH 15/15] Change close buttons to `UIAlertActionStyleCancel` --- TrollStore/TSInstallationController.m | 2 +- TrollStore/TSSettingsListController.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TrollStore/TSInstallationController.m b/TrollStore/TSInstallationController.m index 5c4d2eb..4caf61f 100644 --- a/TrollStore/TSInstallationController.m +++ b/TrollStore/TSInstallationController.m @@ -54,7 +54,7 @@ extern NSUserDefaults* trollStoreUserDefaults(void); } else if (ret == 182) { // non-fatal informative message UIAlertController* rebootNotification = [UIAlertController alertControllerWithTitle:@"Reboot Required" message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) + UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action) { if(completionBlock) completionBlock(YES, nil); }]; diff --git a/TrollStore/TSSettingsListController.m b/TrollStore/TSSettingsListController.m index d9037cd..3b3efca 100644 --- a/TrollStore/TSSettingsListController.m +++ b/TrollStore/TSSettingsListController.m @@ -408,7 +408,7 @@ extern NSUserDefaults* trollStoreUserDefaults(void); message:@"After rebooting, select \"Turn On\" to enable developer mode." preferredStyle:UIAlertControllerStyleAlert ]; - UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) + UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action) { [self reloadSpecifiers]; }];