Created
October 28, 2024 01:27
-
-
Save EthanArbuckle/c91b6f690710607a06bb7082b7932838 to your computer and use it in GitHub Desktop.
objc object search
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <objc/runtime.h> | |
#import <mach/mach.h> | |
#import <malloc/malloc.h> | |
#import <dlfcn.h> | |
typedef struct { | |
Class targetClass; | |
void (^block)(id object, BOOL isSubclass, BOOL *stop); | |
dispatch_semaphore_t semaphore; | |
BOOL shouldStop; | |
} EnumerationContext; | |
static kern_return_t fast_read_memory(task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory) { | |
*local_memory = (void *)remote_address; | |
return KERN_SUCCESS; | |
} | |
static kern_return_t safe_read_memory(vm_address_t address, vm_size_t size, void *buffer) { | |
vm_size_t dataCount; | |
return vm_read_overwrite(mach_task_self_, address, size, (vm_address_t)buffer, &dataCount); | |
} | |
static BOOL is_valid_objc_address(vm_address_t address) { | |
uintptr_t potentialIsa = 0; | |
if (safe_read_memory(address, sizeof(uintptr_t), &potentialIsa) != KERN_SUCCESS) { | |
return NO; | |
} | |
Dl_info info; | |
if (dladdr((const void *)potentialIsa, &info) == 0) { | |
return NO; | |
} | |
const char *className = info.dli_sname; | |
if (className != NULL && strlen(className) > 13 && objc_getClass(className + 13) != NULL) { | |
return YES; | |
} | |
return NO; | |
} | |
static void range_callback(task_t task, void *context, unsigned type, vm_range_t *ranges, unsigned count) { | |
EnumerationContext *enumContext = (EnumerationContext *)context; | |
for (unsigned i = 0; i < count; i++) { | |
void *ptr = (void *)ranges[i].address; | |
if (is_valid_objc_address((vm_address_t)ptr) == 0) { | |
continue; | |
} | |
id obj = (__bridge id)ptr; | |
Class cls = object_getClass(obj); | |
BOOL isSubclass = (cls != enumContext->targetClass) && class_getSuperclass(cls) == enumContext->targetClass; | |
if (cls == enumContext->targetClass || isSubclass) { | |
BOOL stop = NO; | |
enumContext->block(obj, isSubclass, &stop); | |
if (stop) { | |
enumContext->shouldStop = YES; | |
dispatch_semaphore_signal(enumContext->semaphore); | |
return; | |
} | |
} | |
} | |
} | |
dispatch_semaphore_t forEveryInstanceOfClass(Class targetClass, void (^block)(id object, BOOL isSubclass, BOOL *stop)) { | |
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); | |
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ | |
vm_address_t *zones = NULL; | |
unsigned int zoneCount = 0; | |
EnumerationContext context = { | |
.targetClass = targetClass, | |
.block = block, | |
.semaphore = semaphore, | |
.shouldStop = NO | |
}; | |
kern_return_t result = malloc_get_all_zones(mach_task_self_, fast_read_memory, &zones, &zoneCount); | |
if (result == KERN_SUCCESS) { | |
for (unsigned int i = 0; i < zoneCount && !context.shouldStop; i++) { | |
malloc_zone_t *zone = (malloc_zone_t *)zones[i]; | |
if (zone->introspect && zone->introspect->enumerator) { | |
zone->introspect->enumerator(TASK_NULL, &context, MALLOC_PTR_IN_USE_RANGE_TYPE, (vm_address_t)zone, fast_read_memory, range_callback); | |
} | |
} | |
} else { | |
NSLog(@"Failed to get malloc zones"); | |
} | |
if (!context.shouldStop) { | |
dispatch_semaphore_signal(semaphore); | |
} | |
}); | |
return semaphore; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment