def tryHeapAddress(addr, target, options): returnDescription = "" cleanCommand = 'void * ptr = (void *){};'.format(addr.GetLoadAddress(target)) cleanCommand += 'BOOL verboseMode = {};'.format("YES" if options.verbose else "NO") cleanCommand += r''' @import Foundation; NSMutableString *retString; if ((void*)malloc_zone_from_ptr(ptr)) { retString = (NSMutableString*)[[NSMutableString alloc] initWithFormat:@"%p heap pointer, (0x%x bytes), zone: %p", ptr, (size_t)malloc_good_size((size_t)malloc_size(ptr)), (void*)malloc_zone_from_ptr(ptr)]; } retString ? retString : nil; ''' val = target.EvaluateExpression(cleanCommand, ds.genExpressionOptions()) if val.GetError().success == False or val.GetValueAsUnsigned() == 0 or val.description is None: return False, "" returnDescription += val.description return True, returnDescription
def processStackTraceStringFromAddresses(frameAddresses, target, options=None): frame_string = '' startAddresses = [ target.ResolveLoadAddress(f).symbol.addr.GetLoadAddress(target) for f in frameAddresses ] script = generateExecutableMethodsScript(startAddresses) # New content start 1 methods = target.EvaluateExpression(script, ds.genExpressionOptions()) charPointerType = target.FindFirstType( "char").GetPointerType().GetArrayType(len(frameAddresses)) methods = methods.Cast(charPointerType) methodsVal = lldb.value(methods) # New content end 1 # Enumerate each of the SBFrames in address list pointerType = target.FindFirstType("char").GetPointerType() for index, frameAddr in enumerate(frameAddresses): addr = target.ResolveLoadAddress(frameAddr) symbol = addr.symbol # New content start 2 if symbol.synthetic: # 1 children = methodsVal.sbvalue.GetNumChildren() # 4 name = ds.attrStr(symbol.name + r' ... unresolved womp womp', 'redd') # 2 loadAddr = symbol.addr.GetLoadAddress(target) # 3 k = str(methodsVal[index]).split('"') # 5 if len(k) >= 2: name = ds.attrStr(k[1], 'bold') # 6 else: name = ds.attrStr(str(symbol.name), 'yellow') # 7 # New content end 2 offset_str = '' offset = addr.GetLoadAddress(target) - addr.symbol.addr.GetLoadAddress( target) if offset > 0: offset_str = '+ {}'.format(offset) i = ds.attrStr('frame #{:<2}:'.format(index), 'grey') if options and options.address: frame_string += '{} {}`{} {}\n'.format( ds.attrStr(hex(addr.GetLoadAddress(target)), 'grey'), ds.attrStr(str(addr.module.file.basename), 'cyan'), ds.attrStr(str(name), 'yellow'), ds.attrStr(offset_str, 'grey')) else: frame_string += '{} {} {}`{} {}\n'.format( i, ds.attrStr(str(hex(addr.GetLoadAddress(target))), 'grey'), ds.attrStr(str(addr.module.file.basename), 'cyan'), name, ds.attrStr(str(offset_str), 'grey')) return frame_string
def tryHeapAddress(addr, target, options): returnDescription = "" cleanCommand = 'const void * ptr = (const void *){};'.format( addr.GetLoadAddress(target)) cleanCommand += 'BOOL verboseMode = {};'.format( "YES" if options.verbose else "NO") cleanCommand += r''' #ifndef _MALLOC_MALLOC_H_ typedef 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); struct malloc_introspection_t *introspect; unsigned version; void *(*memalign)(struct _malloc_zone_t *zone, size_t alignment, size_t size); void (*free_definite_size)(struct _malloc_zone_t *zone, void *ptr, size_t size); size_t (*pressure_relief)(struct _malloc_zone_t *zone, size_t goal); } malloc_zone_t; extern malloc_zone_t* malloc_zone_from_ptr ( const void * ptr ); extern size_t malloc_size ( const void * ptr ); extern size_t malloc_good_size(size_t size); extern const char* malloc_get_zone_name ( malloc_zone_t * zone ) ; #endif // _MALLOC_MALLOC_H_ NSMutableString *retString; if (malloc_zone_from_ptr(ptr)) { retString = (NSString*)[[NSMutableString alloc] initWithFormat:@"heap pointer found in %s, (%d bytes)", malloc_get_zone_name(malloc_zone_from_ptr(ptr)), malloc_good_size(malloc_size(ptr))]; //*****************************************************************************/ #pragma mark - Ivars //*****************************************************************************/ #define FAST_DATA_MASK 0x00007ffffffffff8UL typedef struct ivar_t { #if __x86_64__ // *offset was originally 64-bit on some x86_64 platforms. // We read and write only 32 bits of it. // Some metadata provides all 64 bits. This is harmless for unsigned // little-endian values. // Some code uses all 64 bits. class_addIvar() over-allocates the // offset for their benefit. #endif int32_t *offset; const char *name; const char *type; // alignment is sometimes -1; use alignment() instead uint32_t alignment_raw; uint32_t size; } ivar_t; typedef struct ivar_list_t { uint32_t entsizeAndFlags; uint32_t count; ivar_t *first; } ivar_list_t; typedef struct class_ro_t { uint32_t flags; uint32_t instanceStart; uint32_t instanceSize; #ifdef __LP64__ uint32_t reserved; #endif const uint8_t * ivarLayout; const char * name; /* method_list_t */ void * baseMethodList; /* protocol_list_t */ void * baseProtocols; ivar_list_t * ivars; uint8_t * weakIvarLayout; /* property_list_t */ void *baseProperties; } class_ro_t; typedef struct class_rw_t { uint32_t flags; uint32_t version; const class_ro_t *ro; /* method_array_t */ void* methods; // redefined from method_array_t /* property_array_t */ void* properties; // redefined from property_array_t /* protocol_list_t */ void* protocols; // redefined from protocol_array_t struct dsobjc_class* firstSubclass; struct dsobjc_class* nextSiblingClass; char *demangledName; } class_rw_t; typedef struct dsobjc_class { struct dsobjc_class* isa; struct dsobjc_class* superclass; void *_buckets; // formerly cache pointer and vtable uint32_t _mask; uint32_t _occupied; uintptr_t bits; class_rw_t *ds_data() { return (class_rw_t *)(bits & FAST_DATA_MASK); } } dsobjc_class; dsobjc_class *dsclass = (dsobjc_class*)object_getClass((id)ptr); if (dsclass) { (void)[retString appendString:(NSString*)[[NSString alloc] initWithFormat:@"\nin %@:\n", (id)NSStringFromClass((id)object_getClass((id)ptr))]]; ivar_list_t *bivar = dsclass->ds_data()->ro->ivars; if (bivar) { for (int i = 0; i < bivar->count; i++) { ivar_t *dsiv = (ivar_t *)(&bivar->first); int sz = dsiv[i].size; int32_t offset = *(int32_t *)dsiv[i].offset; char *baseAddr = (char *)ptr; char *dstype = (char *)dsiv[i].type; char *dsname = (char *)dsiv[i].name; // no clue what would be this size if (sz == 1) { [retString appendString:(NSString*)[[NSString alloc] initWithFormat:@" %s %-30s\n\t%s\n", dstype, dsname, *((uint8_t *)(char *)ptr + (offset))]]; // no clue what would be this size } else if (sz == 2) { [retString appendString:(NSString*)[[NSString alloc] initWithFormat:@" %s %-30s %p\n", dstype, dsname, *((uint16_t *)(char *)ptr + (offset))]]; } else if (sz == 4) { uint32_t* offsetAddr32 = *(uint32_t**)(baseAddr + offset); if ((int)strcmp(dstype, "b1")==0 || (int)strcmp(dstype, "b2") ==0 ) { // Its a bool [retString appendString:(NSString*)[[NSString alloc] initWithFormat:@"+0x%-5x%s (%s): %s\n", offset, dsname, dstype, *(BOOL*)(baseAddr + offset) ? "YES" : "NO"]]; } else { [retString appendString:(NSString*)[[NSString alloc] initWithFormat:@"+0x%-5x%s (%s): 0x%012lx\n", offset, dsname, dstype, offsetAddr32]]; } } else if (sz == 8) { uint64_t* offsetAddr64 = *(uint64_t**)(baseAddr + offset); NSMutableString *tmpType = nil; if (dstype[0] == '@' && strlen(dstype) > 1) { tmpType = (NSString*)[(NSString*)[(NSString*)[(NSString*)[NSString stringWithUTF8String:dstype] stringByReplacingOccurrencesOfString:@"@" withString:@""] stringByReplacingOccurrencesOfString:@"\"" withString:@""] stringByAppendingString:@"*"]; } else if (dstype[0] == '@') { tmpType = @"id"; } else { tmpType = (NSString*)[NSString stringWithUTF8String:dstype]; } if (offsetAddr64) { if ((char*)object_getClassName((id)offsetAddr64)) { if (verboseMode) { [retString appendString:(NSString*)[[NSString alloc] initWithFormat:@"+0x%-5x%s (%@): [0x%012lx] %@\n", offset, dsname, tmpType, offsetAddr64, (NSString*)[(id _Nullable)offsetAddr64 debugDescription] ]]; } else { [retString appendString:(NSString*)[[NSString alloc] initWithFormat:@"+0x%-5x%s (%@): %@\n", offset, dsname, tmpType, (NSString*)[(id _Nullable)offsetAddr64 debugDescription] ]]; } } else { [retString appendString:(NSString*)[[NSString alloc] initWithFormat:@"+0x%-5x%s (%@): 0x%012lx\n", offset, dsname, tmpType, offsetAddr64]]; } } else { [retString appendString:(NSString*)[[NSString alloc] initWithFormat:@"+0x%-5x%s (%@): nil\n", offset, dsname, tmpType]]; } } } } } } else { retString = nil; } // 0x00007ffffffffff8UL retString ''' # lldb.debugger.HandleCommand("exp -l objc -- " + cleanCommand) # return False, returnDescription val = target.EvaluateExpression(cleanCommand, ds.genExpressionOptions()) if val.GetValueAsUnsigned() == 0: return False, "" returnDescription += '{}'.format(val.description) return True, returnDescription