def generate_cstring_dict(target, command, options): if options.module: module_name = options.module module = target.FindModule(lldb.SBFileSpec(module_name)) if not module.IsValid(): result.SetError( "Unable to open module name '{}', to see list of images use 'image list -b'" .format(module_name)) return modules = [module] else: modules = target.modules return_string = '' error = lldb.SBError() prog = re.compile(command) for m in modules: section = ds.getSection(m, '__TEXT.__cstring') if section is None: continue data = section.data dataArray = section.data.sint8s sectionAddress = section.addr.GetLoadAddress(target) moduleString = '' indices = [ i for i, x in enumerate(dataArray) if x > 1 and dataArray[i - 1] == 0 and x != 0 ] returnDict = {} for i in indices: cString = data.GetString(error, i) if prog.search(cString): returnDict[hex(sectionAddress + i)] = cString if len(returnDict) == 0: continue if options.module_summary: return_string += '{} hits in: {}\n'.format(str(len(returnDict)), m.file.basename) else: moduleString = '\n' + ds.attrStr( '****************************************************', 'cyan') + '\n{} hits in: {}'.format( str(len(returnDict)), ds.attrStr(m.file.basename, 'red')) + '\n' + ds.attrStr( '****************************************************', 'cyan') + '\n' for k, v in returnDict.iteritems(): if options.load_address: moduleString += ds.attrStr('[' + k + ']', 'yellow') + ' ' moduleString += ds.attrStr(v, 'cyan') + '\n' return_string += moduleString return return_string
def processStackTraceStringFromAddresses(frameAddresses, target, options = None): frame_string = '' for index, frameAddr in enumerate(frameAddresses): addr = target.ResolveLoadAddress(frameAddr) prevAddr = target.ResolveLoadAddress(frameAddr - 1) symbol = addr.symbol name = symbol.name offset_str = '' offset = addr.GetLoadAddress(target) - addr.symbol.addr.GetLoadAddress(target) if options and options.source: resolvedAddr = prevAddr if prevAddr.GetLineEntry().GetLine() != 0 else addr if resolvedAddr.GetLineEntry().IsValid(): lineEntry = resolvedAddr.GetLineEntry() line = lineEntry.GetLine() if lineEntry.GetLine() != 0 else "?" column = lineEntry.GetColumn() if lineEntry.GetColumn() != 0 else "?" fileName = resolvedAddr.GetCompileUnit().file.GetFilename() method_str = ds.attrStr('{}:{}:{}'.format(fileName, line, column), 'yellow') else: method_str = ds.attrStr('?', 'red') else: method_str = ds.attrStr(str(name), 'yellow') if not symbol.IsSynthetic() else ds.attrStr(str(name), 'red') if offset > 0: offset_str = '+ {}'.format(offset) i = ds.attrStr('frame #{:<2}: 0x{:012x} '.format(index, addr.GetLoadAddress(target)), 'grey') frame_string += '{} {}`{} {}\n'.format(i, ds.attrStr(str(addr.module.file.basename), 'cyan'), method_str, offset_str) return frame_string
def handle_command(debugger, command, result, internal_dict): ''' Disassemble with colors! Terminal only ''' command_args = shlex.split(command, posix=False) target = ds.getTarget() parser = generate_option_parser() try: (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return if len(args) == 0: sym = ds.getFrame().GetSymbol() else: sym = ds.getTarget().ResolveLoadAddress(long(args[0], 16)).GetSymbol() instructions = sym.GetInstructions(target) output = ds.attrStr(sym.addr.module.file.basename + ', ' + sym.name, 'cyan') + '\n' counter = 0 if len(instructions) == 0: return startAddress = instructions.GetInstructionAtIndex( 0).GetAddress().GetLoadAddress(target) frame = ds.getFrame() for inst in instructions: line = ds.attrStr(str(counter).ljust(4), 'grey') counter += 1 offset = str(inst.addr.GetLoadAddress(target) - startAddress) branch = (ds.attrStr('*', 'yellow') if inst.is_branch else ' ') pc = ds.attrStr('-> ', 'grey') if frame.addr == inst.addr else ' ' loadaddr = ds.attrStr( hex(inst.addr.GetLoadAddress(target)) + (' <+' + offset + '>:').ljust(8), 'grey') mnemonic = ds.attrStr(inst.mnemonic.ljust(5), 'red') operands = ds.attrStr(inst.operands, 'bold') comments = ds.attrStr(inst.comment, 'green') if options.memory: tmp = ' '.join([ hex(i).replace('0x', '').zfill(2) for i in inst.GetData(lldb.target).uint8s ]) mem = ds.attrStr(tmp, 'cyan') else: mem = '' output += '{}{}{} {} {} {} {} {}\n'.format(pc, branch, line, loadaddr, mem, mnemonic, operands, comments) result.AppendMessage(output)
def tryMachOAddress(addr, target, options): returnDescription = "" section = addr.GetSection() if not section.IsValid(): return False, "" sectionName = section.GetName() tmpS = section while tmpS.GetParent().IsValid(): tmpS = tmpS.GetParent() sectionName = "{}.{}".format(tmpS.GetName(), sectionName) module = addr.GetModule() if module.IsValid(): sectionName = " `{}`{}".format( ds.attrStr(addr.GetModule().GetFileSpec().GetFilename(), 'cyan'), ds.attrStr(sectionName, 'yellow')) addrOffset = addr.GetLoadAddress(target) - section.GetLoadAddress(target) sectionName += " + {}".format(hex(addrOffset)) symbol = addr.GetSymbol() # Is it a known function? if symbol.IsValid(): returnDescription += " {} ".format( ds.attrStr(symbol.GetName(), 'yellow')) startAddr = symbol.GetStartAddress() # Symbol address offset, if any addrOffset = addr.GetLoadAddress(target) - startAddr.GetLoadAddress( target) returnDescription += " <+{}>".format(addrOffset) # Mangled function if options.verbose: if symbol.GetMangledName(): returnDescription += ", ({})".format(symbol.GetMangledName()) returnDescription += ", External: {}".format( "YES" if symbol.IsSynthetic() else "NO") # tpe = target.GetBasicType(lldb.eBasicTypeNullPtr).GetPointerType() # val = target.EvaluateExpression("(void *){}".format(addr.GetLoadAddress(target)), ds.genExpressionOptions()) # if val.IsValid(): # data = val.GetData() # k = ds.formatFromData(data, section, 1) # returnDescription += '{}'.format(k[1]) returnDescription += sectionName return True, returnDescription
def generateAddressInfo(addresses, options, target): outputStr = '' for a in addresses: symbol = a.symbol if symbol: symbolOffset = a.GetLoadAddress( target) - symbol.addr.GetLoadAddress(target) symbolAddress = hex(symbol.addr.GetLoadAddress(target)) outputStr += '[{}] {} + {}\n\n'.format( ds.attrStr(symbolAddress, 'yellow'), ds.attrStr(symbol.name, 'cyan'), symbolOffset) else: outputStr + 'error: ' return outputStr
def generate_return_string(target, frame, module_dict, options): return_string = '' for key in module_dict: count = len(module_dict[key]) if len(module_dict[key]) == 0: continue tmp = module_dict[key][0] if options.module_summary: return_string += str(count) + ' hits in: ' + key + '\n' continue return_string += ds.attrStr('****************************************************', 'cyan') + '\n' return_string += str(count) + ' hits in: ' + ds.attrStr(key, 'red') + '\n' return_string += ds.attrStr('****************************************************', 'cyan') + '\n' for symbol_context in module_dict[key]: if options.global_var or options.global_var_noeval: name = symbol_context.symbol.name if options.global_var: addr = hex(symbol_context.symbol.addr.GetLoadAddress(target)) val = frame.EvaluateExpression('*(void**)' + addr) name += '\n' + (val.description if val.description else '0x%010x' % val.unsigned) elif symbol_context.function.name is not None: name = symbol_context.function.name if options.mangled_name: mangledName = symbol_context.symbol.GetMangledName() name += ', ' + mangledName if mangledName else '[NONE]' elif symbol_context.symbol.name is not None: name = symbol_context.symbol.name if options.mangled_name: mangledName = symbol_context.symbol.GetMangledName() name += ', ' + mangledName if mangledName else '[NONE]' else: return_string += 'Can\'t find info for ' + str(symbol_context) + '\n\n' continue if options.load_address: str_addr = str(hex(symbol_context.GetSymbol().GetStartAddress().GetLoadAddress(target))) end_addr = str(hex(symbol_context.GetSymbol().GetEndAddress().GetLoadAddress(target))) return_string += ds.attrStr('[' + str_addr + '-' + end_addr + '] ', 'yellow') + name else: return_string += name return_string += '\n\n' return return_string
def processStackTraceStringFromAddresses(frameAddresses, target): frame_string = '' for index, frameAddr in enumerate(frameAddresses): addr = target.ResolveLoadAddress(frameAddr) symbol = addr.symbol name = symbol.name 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, hex(addr.GetLoadAddress(target))), 'grey') frame_string += '{} {}`{} {}\n'.format(i, ds.attrStr(str(addr.module.file.basename), 'cyan'), ds.attrStr(str(name), 'yellow') if not symbol.IsSynthetic() else ds.attrStr(str(name), 'red') , offset_str) return frame_string
def generateAddressInfo(addresses, options, target): outputStr = '' for a in addresses: symbol = a.symbol if symbol: symbolOffset = a.GetLoadAddress( target) - symbol.addr.GetLoadAddress(target) symbolAddress = hex(symbol.addr.GetLoadAddress(target)) if a.GetLineEntry().IsValid(): entry = a.GetLineEntry() sourceFile = entry.GetFileSpec().basename else: sourceFile = "No Source File" outputStr += '[{}] {} + {} ({})\n\n'.format( ds.attrStr(symbolAddress, 'yellow'), ds.attrStr(symbol.name, 'cyan'), symbolOffset, sourceFile) else: outputStr + 'error: ' return outputStr
def handle_command(debugger, command, exe_ctx, result, internal_dict): ''' Disassemble with colors! Terminal only ''' command_args = shlex.split(command, posix=False) target = exe_ctx.target parser = generate_option_parser() try: (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return output = '' if options.search_functions: query = options.search_functions symbol_context_list = target.FindGlobalFunctions( query, 0, lldb.eMatchTypeRegex) for symContext in symbol_context_list: output += generateAssemblyFromSymbol(symContext.symbol, options, exe_ctx) elif len(args) == 0: sym = exe_ctx.frame.symbol output += generateAssemblyFromSymbol(sym, options, exe_ctx) elif args[0].startswith('0x'): sym = target.ResolveLoadAddress(int(args[0], 16)).GetSymbol() output += generateAssemblyFromSymbol(sym, options, exe_ctx) elif args[0].isdigit(): sym = target.ResolveLoadAddress(int(args[0])).GetSymbol() output += generateAssemblyFromSymbol(sym, options, exe_ctx) else: cleanCommand = ' '.join(args) symList = target.FindGlobalFunctions(cleanCommand, 0, lldb.eMatchTypeNormal) if symList.GetSize() == 0: result.SetError( ds.attrStr( "Couldn't find any matches for \"{}\"".format( cleanCommand), 'red')) return sym = symList output += generateAssemblyFromSymbol( symList.GetContextAtIndex(0).symbol, options, exe_ctx) result.AppendMessage(output)
def parseSection(sections, options, target): output = '' for section in sections: # if section name = ds.getSectionName(section) loadAddr = section.addr.GetLoadAddress(target) addr = section.addr size = section.size data = section.data endAddr = loadAddr + size addr = section.addr if options.summary: moduleName = addr.module.file.basename # bug TODO figure why pagezero is wonky if name == '__PAGEZERO': loadAddr = 0 endAddr = size output += ds.attrStr( '[' + '{0:#016x}'.format(loadAddr) + '-' + '{0:#016x}'.format(endAddr) + '] ', 'cyan') output += ds.attrStr("{0:#012x}".format(size), 'grey') + ' ' output += ds.attrStr(moduleName, 'yellow') + '`' output += ds.attrStr(name, 'cyan') + '\n' continue returnType = ds.getSectionData(section, options.count) # Ok, I really need to rewrite this, but whatever if isinstance(returnType, tuple): (indeces, sectionData) = returnType for index, x in enumerate(sectionData): if options.count != 0 and index >= options.count: break if options.load_address: output += ds.attrStr(hex(loadAddr + indeces[index]), 'yellow') + ' ' output += ds.attrStr(str(x), 'cyan') + '\n' elif isinstance(returnType, str): output += returnType return output
def parseSection(sections, options): output = '' for section in sections: # if section name = ds.getSectionName(section) loadAddr = section.addr.GetLoadAddress(ds.getTarget()) addr = section.addr size = section.size data = section.data endAddr = loadAddr + size addr = section.addr if options.summary: moduleName = addr.module.file.basename # bug TODO figure why pagezero is wonky if name == '__PAGEZERO': loadAddr = 0 endAddr = size output += ds.attrStr('[' + '{0:#016x}'.format(loadAddr) + '-' + '{0:#016x}'.format(endAddr) + '] ', 'cyan') output += ds.attrStr("{0:#012x}".format(size), 'grey') + ' ' output += ds.attrStr(moduleName, 'yellow') + '`' output += ds.attrStr(name, 'cyan') + '\n' continue (indeces, sectionData) = ds.getSectionData(section, options.count) for index, x in enumerate(sectionData): if options.count != 0 and index >= options.count: break if options.load_address: output += ds.attrStr(hex(loadAddr + indeces[index]), 'yellow') + ' ' output += ds.attrStr(str(x), 'cyan') + '\n' return output
def generateAssemblyFromSymbol(sym, options, exe_ctx): target = exe_ctx.target frame = exe_ctx.GetFrame() instructions = sym.GetInstructions(target) output = ds.attrStr(str(sym.addr.module.file.basename) + ', ', 'cyan') + ds.attrStr(str(sym.name), 'yellow') + '\n' counter = 0 if len(instructions) == 0: return startAddress = instructions.GetInstructionAtIndex( 0).GetAddress().GetLoadAddress(target) branches = [] offsetSizeDict = {} grepSearch = False for inst in instructions: line = ds.attrStr(str(counter).ljust(4), 'grey') offset = str(inst.addr.GetLoadAddress(target) - startAddress) branch = (ds.attrStr('*', 'yellow') if inst.is_branch else ' ') pc = ds.attrStr('-> ', 'red') if frame.addr == inst.addr else ' ' loadaddr = ds.attrStr( hex(inst.addr.GetLoadAddress(target)) + (' <+' + offset + '>:').ljust(8), 'grey') mnemonic = ds.attrStr(inst.GetMnemonic(target).ljust(5), 'red') mnemonicStr = inst.GetMnemonic(target).ljust(5) if len(inst.GetOperands(target).split(',')) > 1: ops = inst.GetOperands(target).split(',') operands = ds.attrStr(ops[0], 'bold') + ', ' + ds.attrStr( ops[1], 'yellow') else: operands = ds.attrStr(inst.GetOperands(target), 'bold') comments = ds.attrStr(inst.GetComment(target), 'cyan') if options.grep_functions: if re.search(options.grep_functions, comments): grepSearch = True # TODO x64 only, need arm64 if 'rip' in inst.GetOperands(target): nextInst = instructions[counter + 1] m = re.search(r"(?<=\[).*(?=\])", inst.GetOperands(target)) pcComment = '' if m and nextInst: nextPCAddr = hex(nextInst.addr.GetLoadAddress(target)) commentLoadAddr = eval(m.group(0).replace('rip', nextPCAddr)) addr = target.ResolveLoadAddress(commentLoadAddr) showComments, modName = generateDescriptionByAddress( addr, target) if not showComments: comments = '' if modName in comments: comments = '' pcComment += ds.attrStr('; ' + modName, 'green') # interpreter.HandleCommand('image lookup -a ' + nextPCAddr, res) # # m = re.search('(?<=\().*(?=\s)', res.GetOutput()) # # if m: # # pcComment += ds.attrStr(' ' + m.group(0), 'green') # m = re.search('(?<=Summary\:\s).*$', res.GetOutput()) # if m: # pcComment += ds.attrStr(' sum:' + res.GetOutput(), 'blue') # res.Clear() else: pcComment = '' if 'call' in mnemonicStr and inst.GetOperands(target).startswith( '0x') and len(inst.GetOperands(target).split(',')) == 1: num = int(inst.GetOperands(target), 16) a = target.ResolveLoadAddress(num) pcComment = '; ' if '__stubs' in a.section.name: pcComment += '(__TEXT.__stubs) ' pcComment += a.symbol.name pcComment = ds.attrStr(pcComment, 'green') comments = '' match = re.search('(?<=\<\+)[0-9]+(?=\>)', inst.GetComment(target)) offsetSizeDict[offset] = counter if options.show_branch and inst.is_branch and match: branches.append((counter, int(match.group(0)))) if options.memory: tmp = ' '.join([ hex(i).replace('0x', '').zfill(2) for i in inst.GetData(lldb.target).uint8s ]) mem = ds.attrStr(tmp, 'cyan') else: mem = '' formatter = '{}' if options.show_branch else '' output += '{}{}{} {}{} {} {} {} {} {}\n'.format( pc, branch, line, formatter, loadaddr, mem, mnemonic, operands, comments, pcComment) counter += 1 if options.show_branch: branchLines = generateBranchLines(branches, counter, offsetSizeDict) for i, line in enumerate(output.split('\n')): output += line.format(branchLines[i]) + '\n' if options.grep_functions: if grepSearch: return output else: return '' return output + '\n'
def dclass(debugger, command, exe_ctx, result, internal_dict): ''' Dumps all the NSObject inherited classes in the process. If you give it a module, it will dump only the classes within that module. You can also filter out classes to only a certain type and can also generate a header file for a specific class. Example: # Dump ALL the NSObject classes within the process (lldb) dclass # Dump all the classes that are a UIViewController within the process (lldb) dclass -f UIViewController # Dump all the classes with the regex case insensitive search "viewcontroller" in the class name (lldb) dclass -r (?i)viewCoNtrolLer # Dump all the classes within the UIKit module (lldb) dclass -m UIKit # Dump all classes in CKConfettiEffect NSBundle that are UIView subclasses (lldb) dclass /System/Library/Messages/iMessageEffects/CKConfettiEffect.bundle/CKConfettiEffect -f UIView # Generate a header file for the class specified: (lldb) dclass -g UIView # Generate a protocol that you can cast an object to. Ideal when working with private classes at dev time (lldb) dclass -P UIView # Dump all classes and methods for a particular module, ideal for viewing changes in frameworks over time (lldb) dclass -o UIKit # Only dump classes whose superclass is of type class and in UIKit module. Ideal for going after specific classes (lldb) dclass -s NSObject -m UIKit ''' command_args = shlex.split(command, posix=False) parser = generate_option_parser() try: (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return if not args: # result.SetError('Usage: find NSObjectSubclass\n\nUse \'help find\' for more details') clean_command = None # return if not args and options.generate_header: result.SetError('Need to supply class for option') return else: clean_command = ('').join(args) res = lldb.SBCommandReturnObject() interpreter = debugger.GetCommandInterpreter() target = exe_ctx.target if options.dump_code_output: directory = '/tmp/{}_{}/'.format(target.executable.basename, datetime.datetime.now().time()) os.makedirs(directory) modules = target.modules if len(args) > 0 and args[0] == '__all': os.makedirs(directory + 'PrivateFrameworks') os.makedirs(directory + 'Frameworks') modules = [ i for i in target.modules if '/usr/lib/' not in i.file.fullpath and '__lldb_' not in i.file.fullpath ] outputMsg = "Dumping all private Objective-C frameworks" elif len(args) > 0 and args[0]: module = target.module[args[0]] if module is None: result.SetError( "Unable to open module name '{}', to see list of images use 'image list -b'" .format(args[0])) return modules = [module] outputMsg = "Dumping all private Objective-C frameworks" else: modules = [target.module[target.executable.fullpath]] for module in modules: command_script = generate_module_header_script( options, module.file.fullpath.replace('//', '/')) interpreter.HandleCommand( 'expression -lobjc -O -u0 -- ' + command_script, res) # debugger.HandleCommand('expression -lobjc -O -- ' + command_script) if '/System/Library/PrivateFrameworks/' in module.file.fullpath: subdir = 'PrivateFrameworks/' elif '/System/Library/Frameworks/' in module.file.fullpath: subdir = 'Frameworks/' else: subdir = '' ds.create_or_touch_filepath( directory + subdir + module.file.basename + '.txt', res.GetOutput()) print('Written output to: ' + directory + '... opening file') os.system('open -R ' + directory) return if options.module is not None: module = target.FindModule(lldb.SBFileSpec(options.module)) if not module.IsValid(): result.SetError( "Unable to open module name '{}', to see list of images use 'image list -b'" .format(str(options.module))) return if options.conforms_to_protocol is not None: interpreter.HandleCommand( 'expression -lobjc -O -- (id)NSProtocolFromString(@\"{}\")'.format( options.conforms_to_protocol), res) if 'nil' in res.GetOutput() or not res.GetOutput(): result.SetError("No such Protocol name '{}'".format( options.conforms_to_protocol)) return res.Clear() if options.generate_header or options.generate_protocol: command_script = generate_header_script(options, clean_command) else: command_script = generate_class_dump(target, options, clean_command) if options.generate_header or options.generate_protocol: interpreter.HandleCommand( 'expression -lobjc -O -- (Class)NSClassFromString(@\"{}\")'.format( clean_command), res) if 'nil' in res.GetOutput(): result.SetError( 'Can\'t find class named "{}". Womp womp...'.format( clean_command)) return res.Clear() if options.generate_protocol: filepath = "/tmp/DS_" + clean_command + "Protocol.h" else: filepath = "/tmp/" + clean_command + ".h" interpreter.HandleCommand('expression -lobjc -O -- ' + command_script, res) # debugger.HandleCommand('expression -lobjc -O -g -- ' + command_script) if res.GetError(): result.SetError(res.GetError()) return contents = res.GetOutput() ds.create_or_touch_filepath(filepath, contents) print('Written output to: ' + filepath + '... opening file') os.system('open -R ' + filepath) else: msg = "Dumping protocols" if options.search_protocols else "Dumping classes" result.AppendMessage(ds.attrStr(msg, 'cyan')) interpreter.HandleCommand('expression -lobjc -O -- ' + command_script, res) # debugger.HandleCommand('expression -lobjc -O -g -- ' + command_script) if res.GetError(): result.SetError(ds.attrStr(res.GetError(), 'red')) return result.AppendMessage( ds.attrStr( '************************************************************', 'cyan')) if res.Succeeded(): result.AppendMessage(res.GetOutput())
def generate_return_string(target, frame, module_dict, options): return_string = '' shouldGetSummary = True if (len(module_dict) < 32) else False for key in module_dict: count = len(module_dict[key]) if len(module_dict[key]) == 0: continue tmp = module_dict[key][0] if options.module_summary: return_string += str(count) + ' hits in: ' + key + '\n' continue return_string += ds.attrStr( '****************************************************', 'cyan') + '\n' return_string += str(count) + ' hits in: ' + ds.attrStr(key, 'red') + '\n' return_string += ds.attrStr( '****************************************************', 'cyan') + '\n' module = target.module[key] for symbol_context in module_dict[key]: if options.global_var or options.global_var_noeval: name = symbol_context.symbol.name if options.global_var: addr = hex( symbol_context.symbol.addr.GetLoadAddress(target)) if shouldGetSummary: val = module.FindFirstGlobalVariable(target, name) if not val: # TODO get variable size val = frame.EvaluateExpression('*(void**)' + addr) descp = val.description if val.summary: name += '\n' + val.summary elif descp and descp != '<object returned empty description>' and descp != '<nil>': name += '\n' + descp else: name += '\n' + ('0x%010x' % val.unsigned) else: # TODO get variable size val = frame.EvaluateExpression('*(void**)' + addr) name += '\n' + (val.description if val.description else '0x%010x' % val.unsigned) elif symbol_context.function.name is not None: name = symbol_context.function.name if options.mangled_name: mangledName = symbol_context.symbol.GetMangledName() name += ', ' + mangledName if mangledName else '[NONE]' if options.source_info: lineEntry = symbol_context.GetSymbol().GetStartAddress( ).GetLineEntry() if lineEntry.IsValid(): name += '\n' + lineEntry.file.fullpath + ':' + str( lineEntry.line) elif symbol_context.symbol.name is not None: name = symbol_context.symbol.name if options.mangled_name: mangledName = symbol_context.symbol.GetMangledName() name += ', ' + mangledName if mangledName else '[NONE]' if options.source_info: lineEntry = symbol_context.GetSymbol().GetStartAddress( ).GetLineEntry() if lineEntry.IsValid(): name += '\n' + lineEntry.file.fullpath + ':' + str( lineEntry.line) else: return_string += 'Can\'t find info for ' + str( symbol_context) + '\n\n' continue if options.load_address: str_addr = str( hex(symbol_context.GetSymbol().GetStartAddress(). GetLoadAddress(target))) end_addr = str( hex(symbol_context.GetSymbol().GetEndAddress(). GetLoadAddress(target))) return_string += ds.attrStr( '[' + str_addr + '-' + end_addr + '] ', 'yellow') + name else: return_string += name return_string += '\n\n' return return_string
def search(debugger, command, exe_ctx, result, internal_dict): ''' Finds all subclasses of a class. This class must by dynamic (aka inherit from a NSObject class). Currently doesn't work with NSString or NSNumber (tagged pointer objects). NOTE: This script will leak memory Examples: # Find all UIViews and subclasses of UIViews find UIView # Find all UIStatusBar instances find UIStatusBar # Find all UIViews, ignore subclasses find UIView -e # Find all instances of UIViews (and subclasses) where tag == 5 find UIView -c "[obj tag] == 5" ''' if not ds.isProcStopped(): result.SetError( ds.attrStr( 'You must have the process suspended in order to execute this command', 'red')) return command_args = shlex.split(command) parser = generate_option_parser() try: (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return if not args: result.SetError( 'Usage: find NSObjectSubclass\n\nUse \'help find\' for more details' ) return clean_command = ('').join(args) res = lldb.SBCommandReturnObject() interpreter = debugger.GetCommandInterpreter() if options.module: target = exe_ctx.target module = target.FindModule(lldb.SBFileSpec(options.module)) if not module.IsValid(): result.SetError( "Unable to open module name '{}', to see list of images use 'image list -b'" .format(options.module)) return options.module = generate_module_search_sections_string(module, target) if options.pointer_reference: objectiveC_class = '(uintptr_t *){}'.format(clean_command) if options.pointer_reference and (options.exact_match or options.module or options.module or options.condition or options.perform_action): result.SetError( "Can only use the --pointer_reference with --barebones") else: interpreter.HandleCommand( 'expression -lobjc -O -- (Class)NSClassFromString(@\"{}\")'.format( clean_command), res) if 'nil' in res.GetOutput(): result.SetError( 'Can\'t find class named "{}". Womp womp...'.format( clean_command)) return objectiveC_class = 'NSClassFromString(@"{}")'.format(clean_command) command_script = get_command_script(objectiveC_class, options) # print command_script # return expr_options = lldb.SBExpressionOptions() expr_options.SetIgnoreBreakpoints(True) expr_options.SetFetchDynamicValue(lldb.eNoDynamicValues) expr_options.SetTimeoutInMicroSeconds(30 * 1000 * 1000) # 30 second timeout expr_options.SetTryAllThreads(False) expr_options.SetTrapExceptions(False) expr_options.SetUnwindOnError(True) expr_options.SetGenerateDebugInfo(True) expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus) expr_options.SetCoerceResultToId(True) # expr_options.SetAutoApplyFixIts(True) frame = exe_ctx.frame if frame is None: result.SetError( 'You must have the process suspended in order to execute this command' ) return # debugger.HandleCommand('po ' + command_script) # debugger.HandleCommand('expression -lobjc++ -g -O -- ' + command_script) # return # print(command_script) expr_sbvalue = frame.EvaluateExpression(command_script, expr_options) if not expr_sbvalue.error.success: result.SetError("\n**************************************\nerror: " + str(expr_sbvalue.error)) return val = lldb.value(expr_sbvalue) count = val.count.sbvalue.unsigned global s s = val if count > 100: result.AppendWarning( 'Exceeded 100 hits, try narrowing your search with the --condition option' ) count = 100 if options.pointer_reference: for i in range(count): v = val.values[i].sbvalue offset = val.offsets[i].sbvalue.unsigned val_description = ds.attrStr(str( v.GetTypeName()), 'cyan') + ' [' + ds.attrStr( str(v.GetValue()), 'yellow') + ']' + ' + ' + ds.attrStr( str(offset), 'yellow') result.AppendMessage(val_description) else: if options.barebones: for i in range(count): v = val.values[i].sbvalue val_description = ds.attrStr(str( v.GetTypeName()), 'cyan') + ' [' + ds.attrStr( str(v.GetValue()), 'yellow') + ']' result.AppendMessage(val_description) else: for i in range(count): v = val.values[i].sbvalue if not v.description: continue desc = v.description result.AppendMessage(desc + '\n')
def lookup(debugger, command, exe_ctx, result, internal_dict): ''' Perform a regular expression search for stuff in an executable # Find all methods that contain the phrase viewDidLoad (lldb) lookup viewDidLoad # Find a summary of all the modules that have a (known) function containing the phrase viewDidLoad (lldb) lookup viewDidLoad -s # Search for Objective-C code in a stripped module (i.e. in SpringBoard) (lldb) loo -x StocksFramework . # Search for Objective-C code containing the case insensitive phrase init inside a stripped main bundle (lldb) lookup -X (?i)init # Search for all hardcoded, embeded `char *` inside an executable containing the phrase *http* inside UIKit (lldb) lookup -S http -m UIKit # Dump all the md5'd keys in libMobileGestalt along w/ the address in memory (lldb) loo -S ^[a-zA-Z0-9\+]{22,22}$ -m libMobileGestalt.dylib -l # Dump all the global bss code referenced by DWARF. Ideal for accessing `static` variables when not in scope (lldb) lookup . -g HonoluluArt -l ''' if not ds.isProcStopped(): result.SetError( ds.attrStr( 'You must have the process suspended in order to execute this command', 'red')) return command_args = shlex.split(command, posix=False) parser = generate_option_parser() try: (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return clean_command = ('').join(args) target = exe_ctx.target frame = exe_ctx.frame if options.stripped_executable is not None or options.stripped_executable_main: expr_options = lldb.SBExpressionOptions() expr_options.SetIgnoreBreakpoints(False) expr_options.SetFetchDynamicValue(lldb.eDynamicCanRunTarget) expr_options.SetTimeoutInMicroSeconds(30 * 1000 * 1000) # 30 second timeout expr_options.SetTryAllThreads(True) expr_options.SetUnwindOnError(False) expr_options.SetGenerateDebugInfo(True) expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus) expr_options.SetCoerceResultToId(True) if frame is None: result.SetError( 'You must have the process suspended in order to execute this command' ) return if options.stripped_executable: module_name = options.stripped_executable module = target.module[module_name] if module is None: result.SetError('Couldn\'t find the module, "', module_name + '"') return command_script = generate_main_executable_class_address_script( module.file.dirname, options) else: command_script = generate_main_executable_class_address_script( None, options) # debugger.HandleCommand('expression -g -lobjc -O -- ' + command_script) # return expr_value = frame.EvaluateExpression(command_script, expr_options) output_description = str(expr_value.GetObjectDescription()) # result.AppendMessage(output_description) # print(output_description.split()) output = '\n\n'.join([ line for line in output_description.split('\n') if re.search(clean_command, line) ]) if options.create_breakpoint: m = re.findall('0x[a-fA-F0-9]+', output) result.AppendMessage( ds.attrStr("Creating breakpoints on all returned functions", 'red')) for k in m: hexAddr = int(k, 16) target.BreakpointCreateByAddress(hexAddr) if not ds.isXcode(): output = re.sub('0x[a-fA-F0-9]+', '\x1b\x5b33m\g<0>\x1b\x5b39m', output) output = re.sub('[\-|\+].*', '\033[36m\g<0>\033[0m', output) result.AppendMessage(output) return if options.strings: output = generate_cstring_dict(target, args[0], options) result.AppendMessage(output) return if options.module: module_name = options.module module = target.FindModule(lldb.SBFileSpec(module_name)) if not module.IsValid(): result.SetError( "Unable to open module name '{}', to see list of images use 'image list -b'" .format(module_name)) return module_dict = {} if options.global_var or options.global_var_noeval: module_name = options.global_var if options.global_var else options.global_var_noeval module = target.FindModule(lldb.SBFileSpec(module_name)) if not module.IsValid(): result.SetError( "Unable to open module name '{}', to see list of images use 'image list -b'" .format(module_name)) return symbol_context_list = [ i for i in module.get_symbols_array() if i.GetType() == lldb.eSymbolTypeData and i.addr.IsValid() and i.IsValid() ] else: symbol_context_list = target.FindGlobalFunctions( clean_command, 0, lldb.eMatchTypeRegex) for symbol_context in symbol_context_list: if options.global_var is not None or options.global_var_noeval is not None: key = symbol_context.addr.module.file.basename else: key = symbol_context.module.file.basename if options.module and key != options.module: continue if not key in module_dict: module_dict[key] = [] if options.global_var or options.global_var_noeval: if re.search(clean_command, symbol_context.name): module_dict[key].append( symbol_context.addr.GetSymbolContext( lldb.eSymbolContextEverything)) else: module_dict[key].append(symbol_context) return_string = generate_return_string(target, frame, module_dict, options) result.AppendMessage(return_string)
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 handle_command(debugger, command, result, internal_dict): ''' Wrapper (w/ color!) for LLDB's disassembly ''' interp = debugger.GetCommandInterpreter() res = lldb.SBCommandReturnObject() () interp.HandleCommand('disassemble ' + str(command), res) if res.GetError() is None: result.SetError(str(res.GetError())) return output = res.GetOutput() finalOutput = '' for i, line in enumerate(output.split('\n')): if i == 0: w = line.split('`') if len(w) >= 2: finalOutput += ds.attrStr(w[0], 'cyan') + '`' + ds.attrStr( w[1], 'yellow') + '\n' continue w = [i for i in line.split(':') if i] if not len(w): continue if len(w) >= 2: address = ds.attrStr(w[0].ljust(22) + ':', 'grey') else: address = '' code = w[1].split( None, 1 ) # 'mov r10, rcx ; lkjlkjlkj'.split(' ', 1)[1].split(',', 1)[1].split(';') mnemonic = ds.attrStr(code[0].ljust(6), 'red') # mov if len(code) > 1 and len(code[1].split(';')) >= 2: k = code[1].split(';') comment = ds.attrStr('; ' + k[1].strip(), 'cyan') else: k = code[0].split(';') comment = ds.attrString('; ' + k[1].strip(), 'cyan') if len(k) > 1 else '' if len(code) == 1: opcodes = code[0].split(';')[0] else: opcodes = code[1].split(';')[0] ops = opcodes.split(',', 1) # # print('code: ' + str(code)) # # print('opcodes '+ str(opcodes)) # print(opcodes) if len(ops) == 1: # something like 'ret' op1 = ops[0] op2 = '' elif len(ops) == 2: op1 = ops[0] op2 = ops[1] else: op1 = '' op2 = '' # op1 = ds.attrStr(op1 # op1 = op1.ljust(6) op1 = op1 if op2 == '' else str(op1 + ',').ljust(6) op2 = ds.attrStr(op2, 'yellow') finalOutput += '{} {} {}{}{}\n'.format(address, mnemonic, op1, op2, comment) # Uncomment if you are expecting at least one argument # clean_command = shlex.split(args[0])[0] result.AppendMessage(finalOutput)
def parseSection(sections, options, target): output = '' # sections is a list here if len(sections) > 0: for section in sections: name = ds.getSectionName(section) loadAddr = section.addr.GetLoadAddress(target) addr = section.addr size = section.size data = section.data endAddr = loadAddr + size addr = section.addr if options.summary: moduleName = addr.module.file.basename if name == '__PAGEZERO': loadAddr = 0 endAddr = size output += ds.attrStr('[' + '{0:#016x}'.format(loadAddr) + '-' + '{0:#016x}'.format(endAddr) + '] ', 'cyan') output += ds.attrStr("{0:#012x}".format(size), 'grey') + ' ' output += ds.attrStr(moduleName, 'yellow') + '`' output += ds.attrStr(name, 'cyan') + '\n' continue returnType = ds.getSectionData(section, options.count) if options.filter is not None: prog = re.compile(options.filter.strip('"')) else: prog = None # Ok, I really need to rewrite this, but whatever if isinstance(returnType, tuple): if len(returnType) == 3: (indeces, sectionData, descriptions) = returnType else: (indeces, sectionData) = returnType descriptions = None # Filter option for index, x in enumerate(sectionData): if options.filter: if prog.search(x) is None: continue if options.count != 0 and index >= options.count: break if options.load_address: if isinstance(indeces[index], tuple): output += ds.attrStr("[" + hex(loadAddr + indeces[index][0]) + '-' + hex(loadAddr + indeces[index][0] + indeces[index][1]) + "]", 'yellow') + ' ' else: output += ds.attrStr(hex(loadAddr + indeces[index]), 'yellow') + ' ' if descriptions != None and descriptions[index] != None: output += "{} ".format(str(descriptions[index])) output += ds.attrStr(str(x), 'cyan') + '\n' elif isinstance(returnType, str): output += returnType return output