diff --git a/CanardDumper/Makefile b/CanardDumper/Makefile index 8079fcc..967c033 100644 --- a/CanardDumper/Makefile +++ b/CanardDumper/Makefile @@ -1,5 +1,5 @@ TARGET = iphone:latest:15.0 -ARCHS = arm64 arm64e +ARCHS = arm64 INSTALL_TARGET_PROCESSES = fr.lecanardenchaine.app include $(THEOS)/makefiles/common.mk diff --git a/CanardDumper/Tweak.x b/CanardDumper/Tweak.x index a1badee..53a8f3b 100644 --- a/CanardDumper/Tweak.x +++ b/CanardDumper/Tweak.x @@ -1,11 +1,22 @@ #import #import #import -#import -#import -#import + +#pragma mark - Constants + +static NSString *const kCanardLogTag = @"[CanardDumper]"; +static NSString *const kCanardDumpDirectoryName = @"CanardDumps"; + +#pragma mark - State + +static NSString *CanardArchivePassword; +static int CanardLastSavedReadSize = -1; + +#pragma mark - UI Helpers static UIViewController *CanardTopViewController(UIViewController *viewController) { + if (!viewController) return nil; + if (viewController.presentedViewController) { return CanardTopViewController(viewController.presentedViewController); } @@ -20,108 +31,122 @@ static UIViewController *CanardTopViewController(UIViewController *viewControlle static void CanardPresentShareSheet(NSString *filePath) { dispatch_async(dispatch_get_main_queue(), ^{ - UIApplication *application = [UIApplication sharedApplication]; - UIWindow *window = application.keyWindow; - if (!window) { - for (UIWindow *candidate in application.windows) { - if (candidate.isKeyWindow) { - window = candidate; - break; - } + UIWindow *keyWindow = nil; + for (UIWindow *window in [UIApplication sharedApplication].windows) { + if (window.isKeyWindow) { + keyWindow = window; + break; } } - UIViewController *viewController = CanardTopViewController(window.rootViewController); - if (!viewController) { - NSLog(@"[CanardDumper] Unable to present file share sheet"); + UIViewController *topVC = CanardTopViewController(keyWindow.rootViewController); + if (!topVC) { + NSLog(@"%@ Unable to present share sheet", kCanardLogTag); return; } NSURL *fileURL = [NSURL fileURLWithPath:filePath]; UIActivityViewController *shareController = [[UIActivityViewController alloc] initWithActivityItems:@[fileURL] applicationActivities:nil]; - UIPopoverPresentationController *popover = shareController.popoverPresentationController; - popover.sourceView = viewController.view; - popover.sourceRect = CGRectMake(CGRectGetMidX(viewController.view.bounds), CGRectGetMidY(viewController.view.bounds), 0, 0); - [viewController presentViewController:shareController animated:YES completion:nil]; + shareController.popoverPresentationController.sourceView = topVC.view; + shareController.popoverPresentationController.sourceRect = CGRectMake( + CGRectGetMidX(topVC.view.bounds), + CGRectGetMidY(topVC.view.bounds), + 0, 0 + ); + [topVC presentViewController:shareController animated:YES completion:nil]; }); } -static NSString *CanardArchivePassword; -static int CanardLastSavedReadSize = -1; +#pragma mark - File Helpers -static NSString *CanardPasswordFileComponent(NSString *password) { - if (password.length == 0) { - return @"unknown"; +static NSString *CanardSanitizedFilename(NSString *password) { + if (password.length == 0) return @"unknown"; + + NSCharacterSet *invalidChars = [NSCharacterSet characterSetWithCharactersInString:@"/:\\?%*|\"<>\n\r\t"]; + NSString *sanitized = [[password componentsSeparatedByCharactersInSet:invalidChars] componentsJoinedByString:@"_"]; + return sanitized.length > 0 ? sanitized : @"unknown"; +} + +static NSString *CanardDumpDirectory() { + NSString *docsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; + return [docsPath stringByAppendingPathComponent:kCanardDumpDirectoryName]; +} + +static bool CanardSaveData(NSData *data, NSString *password) { + NSFileManager *fm = [NSFileManager defaultManager]; + NSString *dumpDir = CanardDumpDirectory(); + + if (![fm fileExistsAtPath:dumpDir]) { + [fm createDirectoryAtPath:dumpDir withIntermediateDirectories:YES attributes:nil error:nil]; } - NSCharacterSet *invalidCharacters = [NSCharacterSet characterSetWithCharactersInString:@"/:\\?%*|\"<>\n\r\t"]; - NSString *safePassword = [[password componentsSeparatedByCharactersInSet:invalidCharacters] componentsJoinedByString:@"_"]; - return safePassword.length > 0 ? safePassword : @"unknown"; + NSString *fileName = [NSString stringWithFormat:@"file_%@.pdf", CanardSanitizedFilename(password)]; + NSString *filePath = [dumpDir stringByAppendingPathComponent:fileName]; + + if ([data writeToFile:filePath atomically:YES]) { + NSLog(@"%@ Saved to %@ (%lu bytes)", kCanardLogTag, filePath, (unsigned long)data.length); + CanardPresentShareSheet(filePath); + return YES; + } + + NSLog(@"%@ Failed to save to %@", kCanardLogTag, filePath); + return NO; } +#pragma mark - DlyArchiveReader Hooks + %hook DlyArchiveReader --(BOOL)setUpArchiveError:(id *)error { - NSLog(@"[CanardDumper] DlyArchiveReader setUpArchiveError: error=%p", error); +- (bool)setUpArchiveError:(id *)error { + NSLog(@"%@ setUpArchiveError: %p", kCanardLogTag, error); return %orig; } --(NSString *)password { +- (NSString *)password { NSString *pwd = %orig; CanardArchivePassword = [pwd copy]; - NSLog(@"[CanardDumper] DlyArchiveReader password: %@", pwd); + NSLog(@"%@ password: %@", kCanardLogTag, pwd); return pwd; } --(int)getDocumentSize { +- (int)getDocumentSize { int size = %orig; - NSLog(@"[CanardDumper] DlyArchiveReader getDocumentSize: %d", size); + NSLog(@"%@ getDocumentSize: %d", kCanardLogTag, size); return size; } --(NSData *)readDataAt:(int)offset withSize:(int)size { - NSLog(@"[CanardDumper] DlyArchiveReader readDataAt: offset=%d withSize=%d", offset, size); - NSArray *callStack = [NSThread callStackSymbols]; - NSLog(@"[CanardDumper] Backtrace:\n%@", [callStack componentsJoinedByString:@"\n"]); - NSData *data = %orig; - if (data.length > 0 && size != CanardLastSavedReadSize) { - NSFileManager *fm = [NSFileManager defaultManager]; - NSString *docsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; - NSString *dumpDir = [docsPath stringByAppendingPathComponent:@"CanardDumps"]; - if (![fm fileExistsAtPath:dumpDir]) { - [fm createDirectoryAtPath:dumpDir withIntermediateDirectories:YES attributes:nil error:nil]; - } +- (NSData *)readDataAt:(int)offset withSize:(int)size { + NSLog(@"%@ readDataAt: offset=%d size=%d", kCanardLogTag, offset, size); + NSLog(@"%@ %@", kCanardLogTag, [[NSThread callStackSymbols] componentsJoinedByString:@"\n"]); - NSString *passwordComponent = CanardPasswordFileComponent(CanardArchivePassword); - NSString *fileName = [NSString stringWithFormat:@"file_%@.pdf", passwordComponent]; - NSString *filePath = [dumpDir stringByAppendingPathComponent:fileName]; - if ([data writeToFile:filePath atomically:YES]) { - CanardLastSavedReadSize = size; - NSLog(@"[CanardDumper] Saved readDataAt data to %@ (%lu bytes)", filePath, (unsigned long)data.length); - CanardPresentShareSheet(filePath); - } else { - NSLog(@"[CanardDumper] Failed to save readDataAt data to %@", filePath); - } + NSData *data = %orig; + + if (data.length > 0 && size != CanardLastSavedReadSize) { + CanardLastSavedReadSize = size; + CanardSaveData(data, CanardArchivePassword); } + return data; } %end +#pragma mark - DlyCoreArchive Hooks + %hook DlyCoreArchive --(instancetype)initWithArchivePath:(NSString *)path error:(NSError **)error { - NSLog(@"[CanardDumper] DlyCoreArchive initWithArchivePath: %@ error=%p", path, error); +- (instancetype)initWithArchivePath:(NSString *)path error:(NSError **)error { + NSLog(@"%@ initWithArchivePath: %@ error:%p", kCanardLogTag, path, error); return %orig; } -+(instancetype)newWithArchivePath:(NSString *)path error:(NSError **)error { - NSLog(@"[CanardDumper] DlyCoreArchive newWithArchivePath: %@ error=%p", path, error); ++ (instancetype)newWithArchivePath:(NSString *)path error:(NSError **)error { + NSLog(@"%@ newWithArchivePath: %@ error:%p", kCanardLogTag, path, error); return %orig; } --(BOOL)openArchiveWithType:(NSUInteger)type error:(id *)error { - NSLog(@"[CanardDumper] DlyCoreArchive openArchiveWithType: %lu error=%p", (unsigned long)type, error); +- (bool)openArchiveWithType:(NSUInteger)type error:(id *)error { + NSLog(@"%@ openArchiveWithType: %lu error:%p", kCanardLogTag, (unsigned long)type, error); return %orig; }