Created
November 21, 2023 17:18
-
-
Save 0xilis/be2ddaf88e512a676ef169136f79d660 to your computer and use it in GitHub Desktop.
iCloudSignShortcut.m
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Snoolie K / 0xilis | |
* 21 November 2023 (EST) | |
* iCloud Sign Shortcuts Example | |
*/ | |
#import <UIKit/UIKit.h> | |
@interface WFFileRepresentation : NSObject | |
@property (readonly, nonatomic) NSData *data; | |
+(id)fileWithData:(id)arg1 ofType:(id)arg2 proposedFilename:(id)arg3; | |
+(id)fileWithURL:(id)arg1 options:(long long)arg2; | |
+(id)fileWithURL:(id)arg1 options:(long long)arg2 ofType:(id)arg3; | |
+(id)proposedFilenameForFile:(id)arg1 ofType:(id)arg2; | |
+(id)fileWithURL:(id)arg1 options:(long long)arg2 ofType:(id)arg3 proposedFilename:(id)arg4; | |
@end | |
@interface WFWorkflowFileDescriptor : NSObject | |
@property (readonly, nonatomic) WFFileRepresentation *file; | |
-(id)initWithFile:(id)file name:(id)name; | |
@end | |
@interface WFWorkflowFile : NSObject | |
-(id)recordRepresentationWithError:(NSError*)err; | |
-(id)initWithDescriptor:(id)fileDesc error:(NSError*)err; | |
-(id)initWithFileData:(id)fileData name:(id)name error:(NSError*)err; | |
@end | |
@interface WFGallerySessionManager : NSObject | |
+ (id)sharedManager; | |
-(void)uploadWorkflow:(id)arg1 withName:(id)arg2 shortDescription:(id)arg3 longDescription:(id)arg4 private:(BOOL)arg5 completionHandler:(id)arg6; | |
@end | |
#define UNSIGNED_SHORTCUT_PATH "/Users/0xilis/Downloads/0/GitGub/WorkflowKit-Ent-Testing/unsignedShortcut.shortcut" | |
#define SIGNED_SHORTCUT_OUTPUT_PATH "/Users/0xilis/Downloads/0/GitGub/WorkflowKit-Ent-Testing/SIGNED_RESULT.shortcut" | |
/* Notice: recordRepresentationWithError: method requires com.apple.developer.icloud-services entitlement to list CloudKit. Other stuff after may also require entitlements, I'm not sure :P */ | |
int main(void) { | |
/* load WorkflowKit framework */ | |
NSBundle *container = [NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/WorkflowKit.framework"]; | |
if ([container load]) { | |
/* get classes */ | |
Class WFWorkflowFileDescriptorClass = objc_getClass("WFWorkflowFileDescriptor"); | |
Class WFWorkflowFileClass = objc_getClass("WFWorkflowFile"); | |
Class WFGallerySessionManagerClass = objc_getClass("WFGallerySessionManager"); | |
Class WFFileRepresentationClass = objc_getClass("WFFileRepresentation"); | |
/* get WFWorkflowRecord from file */ | |
WFFileRepresentation *fileRep = [WFFileRepresentationClass fileWithURL:[NSURL fileURLWithPath:@UNSIGNED_SHORTCUT_PATH] options:nil]; | |
WFWorkflowFileDescriptor *fileDesc = [[WFWorkflowFileDescriptorClass alloc] initWithFile:fileRep name:@"SnoolieShortcut"]; | |
WFWorkflowFile *wFile = [[WFWorkflowFileClass alloc] initWithDescriptor:fileDesc error:nil]; | |
WFWorkflowRecord *workflowRecord = [wFile recordRepresentationWithError:nil]; /* requires cloudkit entitlement */ | |
/* now actually sign shortcut */ | |
/* | |
* iOS 15 has two classes in relating to iCloud signing: | |
* WFiCloudShortcutFileExporter (the one actually used in the macOS CLI tool, outputs signed file) | |
* WFShortcutiCloudLinkExporter (outputs iCloud URL) | |
* However, both of these are just wrappers around WFGallerySessionManager | |
* since its methods exist in iOS 13/14, it can easily be backported to those versions | |
* here, I call uploadWorkflow: just like the normal implementation, however | |
* the normal implementation of WFiCloudShortcutFileExporter parses the file URL | |
* to get the shortcut identifier, then calls getWorkflowForIdentifier | |
* I'm not sure what this method does exactly, but it is on iOS 13, however I was worried it | |
* might not return signing information on iOS 13, so to be safe, instead I parse the iCloud | |
* URL that is returned, and use the iCloud API to retrive the signed shortcut file. | |
* Also, note I only tested this when being injected into the Shortcuts process :P. | |
*/ | |
WFGallerySessionManager *sharedManager = [WFGallerySessionManagerClass sharedManager]; | |
WFFileRepresentation *fileRep = [WFFileRepresentationClass fileWithURL:[NSURL fileURLWithPath:@UNSIGNED_SHORTCUT_PATH] options:nil]; | |
[sharedManager uploadWorkflow:workflowRecord withName:@"Signed Shortcut Test" shortDescription:nil longDescription:nil private:YES completionHandler:^(NSURL *iCloudURLToShortcut, id exportError) { | |
if (!exportError) { | |
if (iCloudURLToShortcut) { | |
/* | |
* iCloudURLToShortcut will be https://www.icloud.com/shortcuts/(shortcutid) | |
* We need to get https://www.icloud.com/shortcuts/api/records/(shortcutid) | |
* This will have the URL to the signed shortcut file. | |
*/ | |
NSString *apiURLString = [iCloudURLToShortcut.absoluteString stringByReplacingOccurrencesOfString:@"/shortcuts/" withString:@"/shortcuts/api/records/"]; | |
NSData *jsonData = [NSData dataWithContentsOfURL:[NSURL URLWithString:apiURLString]]; | |
NSError *jsonError = nil; | |
NSDictionary *apiResponse = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&jsonError]; | |
if (!jsonError) { | |
/* you probably should check for nil with these keys instead of just blindly trusting they all exist but eh, this is just a quick PoC */ | |
NSString *signedShortcutURL = apiResponse[@"fields"][@"signedShortcut"][@"value"][@"downloadURL"]; | |
NSURL *dataGetURL = [[NSURL alloc] initWithString:[signedShortcutURL stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding]]; | |
NSData *signedShortcutData = [NSData dataWithContentsOfURL:dataGetURL]; | |
[signedShortcutData writeToFile:@SIGNED_SHORTCUT_OUTPUT_PATH atomically:YES]; | |
} else { | |
/* handle json error */ | |
NSLog(@""ShortcutsCLI-iOS(jsonError): %@",jsonError); | |
} | |
} else { | |
/* both exportError and iCloudURLToShortcut are nil?? */ | |
NSLog(@"ShortcutsCLI-iOS Error: nil but no err?"); | |
} | |
} else { | |
/* Handle errors with iCloud uploading */ | |
NSLog(@"ShortcutsCLI-iOS Error: %@",exportError); | |
} | |
}]; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment