frida-objc-bridge
Version:
Objective-C runtime interop from Frida
259 lines (210 loc) • 7.47 kB
JavaScript
import {getApi} from './api.js';
const code = `
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;
isa = GSIZE_TO_POINTER (GPOINTER_TO_SIZE (isa) & OBJC_ISA_MASK);
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;
},
};
}