UNPKG

frida-objc-bridge

Version:

Objective-C runtime interop from Frida

259 lines (210 loc) 7.47 kB
import {getApi} from './api.js'; const code = `#include <glib.h> #include <ptrauth.h> #define KERN_SUCCESS 0 #define MALLOC_PTR_IN_USE_RANGE_TYPE 1 #if defined (HAVE_I386) && GLIB_SIZEOF_VOID_P == 8 # define OBJC_ISA_MASK 0x7ffffffffff8ULL #elif defined (HAVE_ARM64) # define OBJC_ISA_MASK 0xffffffff8ULL #endif typedef struct _ChooseContext ChooseContext; typedef struct _malloc_zone_t malloc_zone_t; typedef struct _malloc_introspection_t malloc_introspection_t; typedef struct _vm_range_t vm_range_t; typedef gpointer Class; typedef int kern_return_t; typedef guint mach_port_t; typedef mach_port_t task_t; typedef guintptr vm_offset_t; typedef guintptr vm_size_t; typedef vm_offset_t vm_address_t; struct _ChooseContext { GHashTable * classes; GArray * matches; }; struct _malloc_zone_t { void * reserved1; void * reserved2; size_t (* size) (struct _malloc_zone_t * zone, const void * ptr); void * (* malloc) (struct _malloc_zone_t * zone, size_t size); void * (* calloc) (struct _malloc_zone_t * zone, size_t num_items, size_t size); void * (* valloc) (struct _malloc_zone_t * zone, size_t size); void (* free) (struct _malloc_zone_t * zone, void * ptr); void * (* realloc) (struct _malloc_zone_t * zone, void * ptr, size_t size); void (* destroy) (struct _malloc_zone_t * zone); const char * zone_name; unsigned (* batch_malloc) (struct _malloc_zone_t * zone, size_t size, void ** results, unsigned num_requested); void (* batch_free) (struct _malloc_zone_t * zone, void ** to_be_freed, unsigned num_to_be_freed); malloc_introspection_t * introspect; }; typedef kern_return_t (* memory_reader_t) (task_t remote_task, vm_address_t remote_address, vm_size_t size, void ** local_memory); typedef void (* vm_range_recorder_t) (task_t task, void * user_data, unsigned type, vm_range_t * ranges, unsigned count); typedef kern_return_t (* enumerator_func) (task_t task, void * user_data, unsigned type_mask, vm_address_t zone_address, memory_reader_t reader, vm_range_recorder_t recorder); struct _malloc_introspection_t { enumerator_func enumerator; }; struct _vm_range_t { vm_address_t address; vm_size_t size; }; extern int objc_getClassList (Class * buffer, int buffer_count); extern Class class_getSuperclass (Class cls); extern size_t class_getInstanceSize (Class cls); extern kern_return_t malloc_get_all_zones (task_t task, memory_reader_t reader, vm_address_t ** addresses, unsigned * count); static void collect_subclasses (Class klass, GHashTable * result); static void collect_matches_in_ranges (task_t task, void * user_data, unsigned type, vm_range_t * ranges, unsigned count); static kern_return_t read_local_memory (task_t remote_task, vm_address_t remote_address, vm_size_t size, void ** local_memory); extern mach_port_t selfTask; gpointer * choose (Class * klass, gboolean consider_subclasses, guint * count) { ChooseContext ctx; GHashTable * classes; vm_address_t * malloc_zone_addresses; unsigned malloc_zone_count, i; classes = g_hash_table_new_full (NULL, NULL, NULL, NULL); ctx.classes = classes; ctx.matches = g_array_new (FALSE, FALSE, sizeof (gpointer)); if (consider_subclasses) collect_subclasses (klass, classes); else g_hash_table_insert (classes, klass, GSIZE_TO_POINTER (class_getInstanceSize (klass))); malloc_zone_count = 0; malloc_get_all_zones (selfTask, read_local_memory, &malloc_zone_addresses, &malloc_zone_count); for (i = 0; i != malloc_zone_count; i++) { vm_address_t zone_address = malloc_zone_addresses[i]; malloc_zone_t * zone = (malloc_zone_t *) zone_address; enumerator_func enumerator; if (zone != NULL && zone->introspect != NULL && (enumerator = (ptrauth_strip (zone->introspect, ptrauth_key_asda))->enumerator) != NULL) { enumerator = ptrauth_sign_unauthenticated ( ptrauth_strip (enumerator, ptrauth_key_asia), ptrauth_key_asia, 0); enumerator (selfTask, &ctx, MALLOC_PTR_IN_USE_RANGE_TYPE, zone_address, read_local_memory, collect_matches_in_ranges); } } g_hash_table_unref (classes); *count = ctx.matches->len; return (gpointer *) g_array_free (ctx.matches, FALSE); } void destroy (gpointer mem) { g_free (mem); } static void collect_subclasses (Class klass, GHashTable * result) { Class * classes; int count, i; count = objc_getClassList (NULL, 0); classes = g_malloc (count * sizeof (gpointer)); count = objc_getClassList (classes, count); for (i = 0; i != count; i++) { Class candidate = classes[i]; Class c; c = candidate; do { if (c == klass) { g_hash_table_insert (result, candidate, GSIZE_TO_POINTER (class_getInstanceSize (candidate))); break; } c = class_getSuperclass (c); } while (c != NULL); } g_free (classes); } static void collect_matches_in_ranges (task_t task, void * user_data, unsigned type, vm_range_t * ranges, unsigned count) { ChooseContext * ctx = user_data; GHashTable * classes = ctx->classes; unsigned i; for (i = 0; i != count; i++) { const vm_range_t * range = &ranges[i]; gconstpointer candidate = GSIZE_TO_POINTER (range->address); gconstpointer isa; guint instance_size; isa = *(gconstpointer *) candidate; #ifdef OBJC_ISA_MASK isa = GSIZE_TO_POINTER (GPOINTER_TO_SIZE (isa) & OBJC_ISA_MASK); #endif instance_size = GPOINTER_TO_UINT (g_hash_table_lookup (classes, isa)); if (instance_size != 0 && range->size >= instance_size) { g_array_append_val (ctx->matches, candidate); } } } static kern_return_t read_local_memory (task_t remote_task, vm_address_t remote_address, vm_size_t size, void ** local_memory) { *local_memory = (void *) remote_address; return KERN_SUCCESS; } `; const {pointerSize} = Process; let cachedModule = null; export function get() { if (cachedModule === null) cachedModule = compileModule(); return cachedModule; } function compileModule() { const { objc_getClassList, class_getSuperclass, class_getInstanceSize, } = getApi(); const selfTask = Memory.alloc(4); selfTask.writeU32(Module.getGlobalExportByName('mach_task_self_').readU32()); const cm = new CModule(code, { objc_getClassList, class_getSuperclass, class_getInstanceSize, malloc_get_all_zones: Process.getModuleByName('/usr/lib/system/libsystem_malloc.dylib').getExportByName('malloc_get_all_zones'), selfTask, }); const _choose = new NativeFunction(cm.choose, 'pointer', ['pointer', 'bool', 'pointer']); const _destroy = new NativeFunction(cm.destroy, 'void', ['pointer']); return { handle: cm, choose(klass, considerSubclasses) { const result = []; const countPtr = Memory.alloc(4); const matches = _choose(klass, considerSubclasses ? 1 : 0, countPtr); try { const count = countPtr.readU32(); for (let i = 0; i !== count; i++) result.push(matches.add(i * pointerSize).readPointer()); } finally { _destroy(matches); } return result; }, }; }