mirror of https://github.com/opa334/TrollStore.git
Add code to check and arm developer mode
This commit is contained in:
parent
6094bc024f
commit
2ac6bc280f
|
@ -0,0 +1,4 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
BOOL checkDeveloperMode(void);
|
||||
BOOL armDeveloperMode(BOOL* alreadyEnabled);
|
|
@ -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;
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
#import <sys/utsname.h>
|
||||
#import <mach-o/loader.h>
|
||||
#import <mach-o/fat.h>
|
||||
#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;
|
||||
|
|
|
@ -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}];
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}];
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue