Skip to content

Instantly share code, notes, and snippets.

@ChiChou
Created January 20, 2026 17:46
Show Gist options
  • Select an option

  • Save ChiChou/22d48d5353191a0eab51988d6eece5d9 to your computer and use it in GitHub Desktop.

Select an option

Save ChiChou/22d48d5353191a0eab51988d6eece5d9 to your computer and use it in GitHub Desktop.
Load private frameworks without crash
#import <Foundation/Foundation.h>
#import <libkern/OSCacheControl.h>
#import <pthread/pthread.h>
#import <signal.h>
#import <execinfo.h>
#import <mach/exception.h>
#import <mach/exception_types.h>
#import <mach/mach.h>
#import <mach/mach_vm.h>
#import <mach/port.h>
#import <mach/thread_status.h>
#import <mach/vm_prot.h>
static uintptr_t load_images_patch = 0;
static uintptr_t ctor_patch = 0;
@interface Trap : NSObject
+ (void)load;
@end
uintptr_t get_caller(void) {
void *addr[3];
int nframes = backtrace(addr, sizeof(addr) / sizeof(*addr));
if (nframes <= 2)
return 0;
uint8_t *caller = addr[2];
#if defined(__arm64__)
uint32_t *blraaz = (uint32_t *)caller - 1;
return (uintptr_t)blraaz;
#elif defined(__x86_64__)
uint8_t *call_r15 = caller - 3;
if (call_r15[0] == 0x41 && call_r15[1] == 0xFF && call_r15[2] == 0xD7)
return (uintptr_t)call_r15;
uint8_t *call_rbx = caller - 2;
if (call_rbx[0] == 0xFF && call_rbx[1] == 0xD3)
return (uintptr_t)call_rbx;
#else
#error "Architecture not supported"
#endif
return 0;
}
__attribute__((constructor)) static void auto_load(void) {
ctor_patch = get_caller();
}
@implementation Trap
+ (void)load {
load_images_patch = get_caller();
}
@end
kern_return_t bp(uintptr_t address) {
kern_return_t kr;
mach_port_t thread = mach_thread_self();
#if defined(__arm64__)
arm_debug_state64_t s;
mach_msg_type_number_t count = ARM_DEBUG_STATE64_COUNT;
kr = thread_get_state(thread, ARM_DEBUG_STATE64, (thread_state_t)&s, &count);
if (kr != KERN_SUCCESS) {
mach_port_deallocate(mach_task_self(), thread);
return kr;
}
s.__bvr[0] = (uint64_t)address;
s.__bcr[0] = 0x1e5;
kr = thread_set_state(thread, ARM_DEBUG_STATE64, (thread_state_t)&s, count);
#elif defined(__x86_64__)
x86_debug_state64_t s;
mach_msg_type_number_t count = x86_DEBUG_STATE64_COUNT;
kr = thread_get_state(thread, x86_DEBUG_STATE64, (thread_state_t)&s, &count);
if (kr != KERN_SUCCESS) {
mach_port_deallocate(mach_task_self(), thread);
return kr;
}
s.__dr0 = (uint64_t)address;
s.__dr7 &= ~(0xF0003);
s.__dr7 |= 0x1;
kr = thread_set_state(thread, x86_DEBUG_STATE64, (thread_state_t)&s, count);
#else
#error "Architecture not supported"
#endif
mach_port_deallocate(mach_task_self(), thread);
return kr;
}
kern_return_t patch_code(uint64_t target_addr, const void *code,
size_t length) {
kern_return_t kr;
mach_port_t task = mach_task_self();
vm_size_t page_size = sysconf(_SC_PAGESIZE);
mach_vm_address_t region_start =
(mach_vm_address_t)target_addr & ~(page_size - 1);
mach_vm_address_t region_end =
(mach_vm_address_t)(target_addr + length + page_size - 1) &
~(page_size - 1);
vm_size_t region_size = region_end - region_start;
vm_offset_t patch_offset = (vm_offset_t)target_addr - region_start;
mach_vm_address_t temp_buffer = 0;
kr = mach_vm_allocate(task, &temp_buffer, region_size, VM_FLAGS_ANYWHERE);
if (kr != KERN_SUCCESS)
return kr;
memcpy((void *)temp_buffer, (void *)region_start, region_size);
memcpy((void *)(temp_buffer + patch_offset), code, length);
kr = mach_vm_protect(task, temp_buffer, region_size, FALSE,
VM_PROT_READ | VM_PROT_EXECUTE);
if (kr != KERN_SUCCESS) {
mach_vm_deallocate(task, temp_buffer, region_size);
return kr;
}
vm_prot_t cur_prot, max_prot;
kr = mach_vm_remap(task, &region_start, region_size, 0, VM_FLAGS_OVERWRITE,
task, temp_buffer, FALSE, &cur_prot, &max_prot,
VM_INHERIT_SHARE);
mach_vm_deallocate(task, temp_buffer, region_size);
sys_icache_invalidate((void *)target_addr, length);
return kr;
}
int main(int argc, const char *argv[]) {
@autoreleasepool {
#if defined(__arm64__)
uint32_t nop = 0xd503201f;
patch_code(load_images_patch, &nop, sizeof(nop));
patch_code(ctor_patch, &nop, sizeof(nop));
#elif defined(__x86_64__)
char nops[] = "\x90\x90\x90\x90";
patch_code(load_images_patch, nops, 3);
patch_code(ctor_patch, nops, 2);
#endif
// todo: load list from dyld_shared_cache
NSString *parent = @"/System/Library/PrivateFrameworks/";
NSError *err = nil;
NSArray *list =
[[NSFileManager defaultManager] contentsOfDirectoryAtPath:parent
error:&err];
for (NSString *item in list) {
NSString *full = [parent stringByAppendingPathComponent:item];
NSBundle *bundle = [NSBundle bundleWithPath:full];
NSLog(@"load %@", item);
[bundle load];
}
}
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment