def run(self, arguments, options): element = arguments[0] language = fb.currentLanguage() if element == "__default__": element = ( "XCUIApplication()" if language == lldb.eLanguageTypeSwift else "(XCUIApplication *)[[XCUIApplication alloc] init]" ) if language == lldb.eLanguageTypeSwift: print( fb.evaluateExpressionValue( "{}.debugDescription".format(element), language=language ) .GetObjectDescription() .replace("\\n", "\n") .replace("\\'", "'") .strip(' "\n\t') ) else: print( fb.evaluateExpressionValue( "[{} debugDescription]".format(element) ).GetObjectDescription() )
def loadChiselIfNecessary(self): target = lldb.debugger.GetSelectedTarget() if target.module['Chisel']: return True path = self.chiselLibraryPath() if not os.path.exists(path): print 'Chisel library missing: ' + path return False module = fb.evaluateExpressionValue('(void*)dlopen("{}", 2)'.format(path)) if module.unsigned != 0 or target.module['Chisel']: return True # `errno` is a macro that expands to a call to __error(). In development, # lldb was not getting a correct value for `errno`, so `__error()` is used. errno = fb.evaluateExpressionValue('*(int*)__error()').value error = fb.evaluateExpressionValue('(char*)dlerror()') if errno == 50: # KERN_CODESIGN_ERROR from <mach/kern_return.h> print 'Error loading Chisel: Code signing failure; Must re-run codesign' elif error.unsigned != 0: print 'Error loading Chisel: ' + error.summary elif errno != 0: error = fb.evaluateExpressionValue('(char*)strerror({})'.format(errno)) if error.unsigned != 0: print 'Error loading Chisel: ' + error.summary else: print 'Error loading Chisel (errno {})'.format(errno) else: print 'Unknown error loading Chisel' return False
def run(self, arguments, options): control = fb.evaluateInputExpression(arguments[0]) targets = fb.evaluateObjectExpression( "[[{control} allTargets] allObjects]".format(control=control) ) targetCount = fb.evaluateIntegerExpression( "[{targets} count]".format(targets=targets) ) for index in range(0, targetCount): target = fb.evaluateObjectExpression( "[{targets} objectAtIndex:{index}]".format(targets=targets, index=index) ) actions = fb.evaluateObjectExpression( "[{control} actionsForTarget:{target} forControlEvent:0]".format( control=control, target=target ) ) targetDescription = fb.evaluateExpressionValue( "(id){target}".format(target=target) ).GetObjectDescription() actionsDescription = fb.evaluateExpressionValue( '(id)[{actions} componentsJoinedByString:@", "]'.format(actions=actions) ).GetObjectDescription() print( "{target}: {actions}".format( target=targetDescription, actions=actionsDescription ) )
def loadChiselIfNecessary(self): target = lldb.debugger.GetSelectedTarget() symbol_contexts = target.FindSymbols('PrintInstances', lldb.eSymbolTypeCode) if any(ctx.symbol.IsValid() for ctx in symbol_contexts): return True path = self.chiselLibraryPath() if not os.path.exists(path): print ('Chisel library missing: %s' %(path)) return False module = fb.evaluateExpressionValue('(void*)dlopen("{}", 2)'.format(path)) if module.unsigned != 0 or target.module['Chisel']: return True # `errno` is a macro that expands to a call to __error(). In development, # lldb was not getting a correct value for `errno`, so `__error()` is used. errno = fb.evaluateExpressionValue('*(int*)__error()').value error = fb.evaluateExpressionValue('(char*)dlerror()') if errno == 50: # KERN_CODESIGN_ERROR from <mach/kern_return.h> print ('Error loading Chisel: Code signing failure; Must re-run codesign') elif error.unsigned != 0: print ('Error loading Chisel: %s' %(error.summary)) elif errno != 0: error = fb.evaluateExpressionValue('(char*)strerror({})'.format(errno)) if error.unsigned != 0: print ('Error loading Chisel: %s' %(error.summary)) else: print ('Error loading Chisel (errno {})'.format(errno)) else: print ('Unknown error loading Chisel') return False
def import_uikit(): """ Import UIKit framework to the debugger """ global _uikit_imported if _uikit_imported: return _uikit_imported = True fb.evaluateExpressionValue("@import UIKit")
def run(self, arguments, options): object = fb.evaluateInputExpression(arguments[0]) ivarName = arguments[1] objectClass = fb.evaluateExpressionValue('(id)[(' + object + ') class]').GetObjectDescription() ivarTypeCommand = '((char *)ivar_getTypeEncoding((void*)object_getInstanceVariable((id){}, \"{}\", 0)))[0]'.format(object, ivarName) ivarTypeEncodingFirstChar = fb.evaluateExpression(ivarTypeCommand) result = fb.evaluateExpressionValue('(({} *)({}))->{}'.format(objectClass, object, ivarName)) print result.GetObjectDescription() if '@' in ivarTypeEncodingFirstChar else result
def run(self, arguments, options): object = fb.evaluateInputExpression(arguments[0]) ivarName = arguments[1] objectClass = fb.evaluateExpressionValue('(id)[(' + object + ') class]').GetObjectDescription() ivarTypeCommand = '((char *)ivar_getTypeEncoding((void*)object_getInstanceVariable((id){}, \"{}\", 0)))[0]'.format(object, ivarName) ivarTypeEncodingFirstChar = fb.evaluateExpression(ivarTypeCommand) result = fb.evaluateExpressionValue('(({} *)({}))->{}'.format(printCommand, objectClass, object, ivarName)) return result.GetObjectDescription() if '@' in ivarTypeEncodingFirstChar else result.GetValue()
def run(self, arguments, options): commandForObject = arguments[0] object = fb.evaluateObjectExpression(commandForObject) fb.evaluateExpressionValue('unsigned int $fblldb_ivar_count = 0', False) fb.evaluateExpressionValue('intptr_t *$fblldb_ivars = (intptr_t *)class_copyIvarList((Class)object_getClass({}), &$fblldb_ivar_count)'.format(object), False) ivarCount = int(fb.evaluateExpression('$fblldb_ivar_count')) for i in range(0, ivarCount): # TODO Unsure how to easily read the string properly, so just getting a summary and removing the quotes. ivarName = fb.evaluateExpressionValue('(char *)ivar_getName((void *)$fblldb_ivars[{}])'.format(i)).GetSummary()[1:-1] print '{} -> '.format(ivarName), FBPrintInstanceVariable().run([commandForObject, ivarName], options)
def _viewControllerDescription(viewController): vc = '(%s)' % (viewController) if fb.evaluateBooleanExpression('[(id)%s isViewLoaded]' % (vc)): result = fb.evaluateExpressionValue('(id)[[NSString alloc] initWithFormat:@"<%%@: %%p; view = <%%@; %%p>; frame = (%%g, %%g; %%g, %%g)>", (id)NSStringFromClass((id)[(id)%s class]), %s, (id)[(id)[(id)%s view] class], (id)[(id)%s view], ((CGRect)[(id)[(id)%s view] frame]).origin.x, ((CGRect)[(id)[(id)%s view] frame]).origin.y, ((CGRect)[(id)[(id)%s view] frame]).size.width, ((CGRect)[(id)[(id)%s view] frame]).size.height]' % (vc, vc, vc, vc, vc, vc, vc, vc)) else: result = fb.evaluateExpressionValue('(id)[[NSString alloc] initWithFormat:@"<%%@: %%p; view not loaded>", (id)NSStringFromClass((id)[(id)%s class]), %s]' % (vc, vc)) if result.GetError() is not None and str(result.GetError()) != 'success': return '[Error getting description.]' else: return result.GetObjectDescription()
def run(self, arguments, options): control = arguments[0] targets = fb.evaluateObjectExpression('[[{control} allTargets] allObjects]'.format(control=control)) targetCount = fb.evaluateIntegerExpression('[{targets} count]'.format(targets=targets)) for index in range(0, targetCount): target = fb.evaluateObjectExpression('[{targets} objectAtIndex:{index}]'.format(targets=targets, index=index)) actions = fb.evaluateObjectExpression('[{control} actionsForTarget:{target} forControlEvent:0]'.format(control=control, target=target)) targetDescription = fb.evaluateExpressionValue('(id){target}'.format(target=target)).GetObjectDescription() actionsDescription = fb.evaluateExpressionValue('(id)[{actions} componentsJoinedByString:@", "]'.format(actions=actions)).GetObjectDescription() print '{target}: {actions}'.format(target=targetDescription, actions=actionsDescription)
def run(self, arguments, options): if not self.loadChiselIfNecessary(): return if len(arguments) == 0 or not arguments[0].strip(): print 'Usage: findinstances <classOrProtocol> [<predicate>]; Run `help findinstances`' return query = arguments[0] predicate = arguments[1].strip() # Escape double quotes and backslashes. predicate = re.sub('([\\"])', r'\\\1', predicate) call = '(void)PrintInstances("{}", "{}")'.format(query, predicate) fb.evaluateExpressionValue(call)
def prettyPrintInvocation(frame, invocation): object = fb.evaluateExpression('(id)[(id)' + invocation + ' target]') selector = fb.evaluateExpressionValue('(char *)sel_getName((SEL)[(id)' + invocation + ' selector])').GetSummary() selector = re.sub(r'^"|"$', '', selector) objectClassValue = fb.evaluateExpressionValue('(id)object_getClass((id)' + object + ')') objectClass = objectClassValue.GetObjectDescription() description = fb.evaluateExpressionValue( '(id)' + invocation).GetObjectDescription() argDescriptions = description.splitlines(True)[4:] print 'NSInvocation: ' + invocation print 'self: ' + fb.evaluateExpression('(id)' + object) if len(argDescriptions) > 0: print '\n' + str(len(argDescriptions)) + ' Arguments:' if len( argDescriptions) > 1 else '\nArgument:' index = 2 for argDescription in argDescriptions: s = re.sub(r'argument [0-9]+: ', '', argDescription) lldb.debugger.HandleCommand('expr -l objc++ -- void *$v') lldb.debugger.HandleCommand('expr -l objc++ -- (void)[' + invocation + ' getArgument:&$v atIndex:' + str(index) + ']') address = findArgAdressAtIndexFromStackFrame(frame, index) encoding = s.split(' ')[0] description = ' '.join(s.split(' ')[1:]) readableString = argumentAsString(frame, address, encoding) if readableString: print readableString else: if encoding[0] == '{': encoding = encoding[1:] print( hex(address) + ', address of ' + encoding + ' ' + description).strip() index += 1
def run(self, arguments, options): commandForObject, ivarName = arguments object = fb.evaluateObjectExpression(commandForObject) valueVariableName = '$fblldb_{}_value'.format(ivarName) fb.evaluateExpressionValue('void *{} = NULL'.format(valueVariableName), False) ivarTypeCommand = '((char *)ivar_getTypeEncoding((void *)object_getInstanceVariable((id){}, \"{}\", &{})))[0]'.format(object, ivarName, valueVariableName) ivarTypeEncodingFirstChar = fb.evaluateExpression(ivarTypeCommand) printCommand = 'po' if ('@' in ivarTypeEncodingFirstChar) else 'p' lldb.debugger.HandleCommand('{} {}'.format(printCommand, valueVariableName)) fb.evaluateExpressionValue('{} = NULL'.format(valueVariableName))
def run(self, arguments, options): element = arguments[0] language = fb.currentLanguage() if element == '__default__': element = 'XCUIApplication()' if language == lldb.eLanguageTypeSwift else '(XCUIApplication *)[[XCUIApplication alloc] init]' if language == lldb.eLanguageTypeSwift: print fb.evaluateExpressionValue("{}.debugDescription".format(element), language=language) \ .GetObjectDescription() \ .replace("\\n", "\n") \ .replace("\\'", "'") \ .strip(' "\n\t') else: print fb.evaluateExpressionValue("[{} debugDescription]".format(element)).GetObjectDescription()
def run(self, arguments, options): first = None haystack = fb.evaluateExpressionValue('(id)[[[UIApplication sharedApplication] keyWindow] recursiveDescription]').GetObjectDescription() needle = arguments[0] allViews = re.findall('.* (0x[0-9a-fA-F]*);.*', haystack) for view in allViews: a11yLabel = fb.evaluateExpressionValue('(id)[(' + view + ') accessibilityLabel]').GetObjectDescription() if re.match(r'.*' + needle + '.*', a11yLabel, re.IGNORECASE): print('{} {}'.format(view, a11yLabel)) if first is None: first = view cmd = 'echo %s | tr -d "\n" | pbcopy' % first os.system(cmd)
def run(self, arguments, options, result): first = None haystack = fb.evaluateExpressionValue('(id)[[UIWindow keyWindow] recursiveDescription]').GetObjectDescription() needle = arguments[0] allViews = re.findall('.* (0x[0-9a-fA-F]*);.*', haystack) for view in allViews: a11yLabel = fb.evaluateExpressionValue('(id)[(' + view + ') accessibilityLabel]').GetObjectDescription() if re.match(r'.*' + needle + '.*', a11yLabel, re.IGNORECASE): fb.printResult(('{} {}'.format(view, a11yLabel)), result) if first == None: first = view cmd = 'echo %s | tr -d "\n" | pbcopy' % first os.system(cmd)
def printInvocationForFrame(frame): print(frame) symbolName = frame.GetSymbol().GetName() if not re.match(r"[-+]\s*\[.*\]", symbolName): return self = findArgAtIndexFromStackFrame(frame, 0) cmd = findArgAtIndexFromStackFrame(frame, 1) commandForSignature = ( "[(id)" + self + " methodSignatureForSelector:(char *)sel_getName((SEL)" + cmd + ")]") signatureValue = fb.evaluateExpressionValue("(id)" + commandForSignature) if (signatureValue.GetError() is not None and str(signatureValue.GetError()) != "success"): print( "My sincerest apologies. I couldn't find a method signature for the selector." # noqa B950 ) return signature = signatureValue.GetValue() arg0 = stackStartAddressInSelectedFrame(frame) commandForInvocation = ( "[NSInvocation _invocationWithMethodSignature:(id)" + signature + " frame:((void *)" + str(arg0) + ")]") invocation = fb.evaluateExpression("(id)" + commandForInvocation) if invocation: prettyPrintInvocation(frame, invocation) else: print(frame)
def run(self, arguments, options): selector = functionPreambleSelector() numberOfParameters = selector.count(":") if numberOfParameters == 0: print "Error: The current method does not accept any arguments." return if options.print_all: for index in xrange(numberOfParameters): argType = typeNameForParameterAtIndex(index) expressionForArgument = functionPreambleExpressionForObjectParameterAtIndex(index, argType) expressionValue = fb.evaluateExpressionValue(expressionForArgument) if argType == "id": argDescription = expressionValue.GetObjectDescription() elif argType == "SEL": argDescription = expressionValue.GetSummary() else: argDescription = expressionValue.GetValue() print "-" * 10, "ARG #{:d} ({:s})".format(index, argType), "-" * 10 print argDescription if index < numberOfParameters - 1: print "" else: index = int(arguments[0]) if index > numberOfParameters - 1: print "Error: Parameter index {:d} out of bounds. The current method accepts {:d} argument(s).".format(index, numberOfParameters) return t = arguments[1] if t == "none": t = typeNameForParameterAtIndex(index) expressionForArgument = functionPreambleExpressionForObjectParameterAtIndex(index, t) command = "po {}".format(expressionForArgument) lldb.debugger.HandleCommand(command)
def upwardsRecursiveDescription(view, maxDepth=0): if not fb.evaluateBooleanExpression('[(id)%s isKindOfClass:(Class)[UIView class]]' % view) and not fb.evaluateBooleanExpression('[(id)%s isKindOfClass:(Class)[NSView class]]' % view): return None currentView = view recursiveDescription = [] depth = 0 while currentView and (maxDepth <= 0 or depth <= maxDepth): depth += 1 viewDescription = fb.evaluateExpressionValue('(id)[%s debugDescription]' % (currentView)).GetObjectDescription() currentView = fb.evaluateExpression('(void*)[%s superview]' % (currentView)) try: if int(currentView, 0) == 0: currentView = None except: currentView = None if viewDescription: recursiveDescription.insert(0, viewDescription) if len(viewDescription) == 0: return None currentPrefix = "" builder = "" for viewDescription in recursiveDescription: builder += currentPrefix + viewDescription + "\n" currentPrefix += " | " return builder
def prettyPrintInvocation(frame, invocation): object = fb.evaluateExpression("(id)[(id)" + invocation + " target]") description = fb.evaluateExpressionValue("(id)" + invocation).GetObjectDescription() argDescriptions = description.splitlines(True)[4:] print "NSInvocation: " + invocation print "self: " + fb.evaluateExpression("(id)" + object) if len(argDescriptions) > 0: print "\n" + str(len(argDescriptions)) + " Arguments:" if len(argDescriptions) > 1 else "\nArgument:" index = 2 for argDescription in argDescriptions: s = re.sub(r"argument [0-9]+: ", "", argDescription) lldb.debugger.HandleCommand("expr void *$v") lldb.debugger.HandleCommand("expr (void)[" + invocation + " getArgument:&$v atIndex:" + str(index) + "]") address = findArgAdressAtIndexFromStackFrame(frame, index) encoding = s.split(" ")[0] description = " ".join(s.split(" ")[1:]) readableString = argumentAsString(frame, address, encoding) if readableString: print readableString else: if encoding[0] == "{": encoding = encoding[1:] print (hex(address) + ", address of " + encoding + " " + description).strip() index += 1
def run(self, arguments, options): tableView = tableViewInHierarchy() print( fb.evaluateExpressionValue( "(id)[(id)" + tableView + " visibleCells]" ).GetObjectDescription() )
def depth(self): """ :return: XCUIElement depth :rtype: lldb.SBValue """ return fb.evaluateExpressionValue("(int)[{} depth]".format( self.element_value))
def _showLayer(layer): layer = "(" + layer + ")" size = "((CGRect)[(id)" + layer + " bounds]).size" width = float(fb.evaluateExpression("(CGFloat)(" + size + ".width)")) height = float(fb.evaluateExpression("(CGFloat)(" + size + ".height)")) if width == 0.0 or height == 0.0: print("Nothing to see here - the size of this element is {} x {}.". format(width, height)) return fb.evaluateEffect("UIGraphicsBeginImageContextWithOptions(" + size + ", NO, 0.0)") fb.evaluateEffect( "[(id)" + layer + " renderInContext:(void *)UIGraphicsGetCurrentContext()]") result = fb.evaluateExpressionValue( "(UIImage *)UIGraphicsGetImageFromCurrentImageContext()") if result.GetError() is not None and str(result.GetError()) != "success": print(result.GetError()) else: image = result.GetValue() _showImage(image) fb.evaluateEffect("UIGraphicsEndImageContext()")
def run(self, arguments, options): element = arguments[0] language = fb.currentLanguage() if element == "__default__": element = ( "XCUIApplication()" if language == lldb.eLanguageTypeSwift else "(XCUIApplication *)[[XCUIApplication alloc] init]" ) # Evaluate object element_sbvalue = fb.evaluateExpressionValue( "{}".format(element), language=language ) """:type: lldb.SBValue""" # Get pointer value, so it will be working in Swift and Objective-C element_pointer = int(element_sbvalue.GetValue(), 16) # Get XCElementSnapshot object snapshot = take_snapshot(element_pointer) # Print details of snapshot element snapshot_object = XCElementSnapshot(snapshot, language=language) print(snapshot_object.detail_summary())
def accessibilityLabel(view): # using Apple private API to get real value of accessibility string for element. return fb.evaluateExpressionValue( "(id)[%s accessibilityAttributeValue:%i]" % (view, ACCESSIBILITY_LABEL_KEY), False, )
def suggested_hit_points(self): """ :return: XCUIElement suggested hit points :rtype: lldb.SBValue """ return fb.evaluateExpressionValue( "(NSArray *)[{} suggestedHitpoints]".format(self.element_value))
def run(self, arguments, options): element = arguments[0] language = fb.currentLanguage() if element == "__default__": element = "XCUIApplication()" if language == lldb.eLanguageTypeSwift else "(XCUIApplication *)[[XCUIApplication alloc] init]" # Evaluate object element_sbvalue = fb.evaluateExpressionValue("{}".format(element), language) """:type: lldb.SBValue""" # Get pointer value, so it will be working in Swift and Objective-C element_pointer = int(element_sbvalue.GetValue(), 16) # Get XCElementSnapshot object snapshot = take_snapshot(element_pointer) # Print tree for snapshot element snapshot_object = XCElementSnapshot(snapshot, language) elements = snapshot_object.find_missing_identifiers( status_bar=options.status_bar) if elements is not None: print elements.hierarchy_text(pointer=options.pointer, trait=options.trait, frame=options.frame) else: print "Couldn't found elements without identifier"
def run(self, arguments, options): object = fb.evaluateObjectExpression(arguments[0]) objectClass = fb.evaluateExpressionValue( '(id)[(id)(' + object + ') class]').GetObjectDescription() command = 'p *(({} *)((id){}))'.format(objectClass, object) lldb.debugger.HandleCommand(command)
def prettyPrintInvocation(frame, invocation): object = fb.evaluateExpression('(id)[(id)' + invocation + ' target]') description = fb.evaluateExpressionValue( '(id)' + invocation).GetObjectDescription() argDescriptions = description.splitlines(True)[4:] print('NSInvocation: ' + invocation) print('self: ' + fb.evaluateExpression('(id)' + object)) if len(argDescriptions) > 0: print('\n' + str(len(argDescriptions)) + ' Arguments:' if len(argDescriptions) > 1 else '\nArgument:') index = 2 for argDescription in argDescriptions: s = re.sub(r'argument [0-9]+: ', '', argDescription) address = findArgAdressAtIndexFromStackFrame(frame, index) encoding = s.split(' ')[0] description = ' '.join(s.split(' ')[1:]) readableString = argumentAsString(frame, address, encoding) if readableString: print(readableString) else: if encoding[0] == '{': encoding = encoding[1:] print((hex(address) + ', address of ' + encoding + ' ' + description).strip()) index += 1
def run(self, arguments, options): objectToPrint = arguments[0] pretty = 1 if options.plain is None else 0 jsonData = fb.evaluateObjectExpression('[NSJSONSerialization dataWithJSONObject:{} options:{} error:nil]'.format(objectToPrint, pretty)) jsonString = fb.evaluateExpressionValue('(NSString*)[[NSString alloc] initWithData:{} encoding:4]'.format(jsonData)).GetObjectDescription() print(jsonString)
def tableViewInHierarchy(): viewDescription = fb.evaluateExpressionValue( '(id)[(id)[UIWindow keyWindow] recursiveDescription]').GetObjectDescription() searchView = None # Try to find an instance of classPattern = re.compile(r'UITableView: (0x[0-9a-fA-F]+);') for match in re.finditer(classPattern, viewDescription): searchView = match.group(1) break # Try to find a direct subclass if not searchView: subclassPattern = re.compile(r'(0x[0-9a-fA-F]+); baseClass = UITableView;') for match in re.finditer(subclassPattern, viewDescription): searchView = match.group(1) break # SLOW: check every pointer in town if not searchView: pattern = re.compile(r'(0x[0-9a-fA-F]+)[;>]') for (view) in re.findall(pattern, viewDescription): if fb.evaluateBooleanExpression('[' + view + ' isKindOfClass:(id)[UITableView class]]'): searchView = view break return searchView
def run(self, arguments, options): maxDepth = int(options.depth) isMac = runtimeHelpers.isMacintoshArch() if arguments[0] == '__keyWindow_dynamic__': arguments[0] = '(id)[[UIApplication sharedApplication] keyWindow]' if isMac: arguments[0] = '(id)[[[[NSApplication sharedApplication] windows] objectAtIndex:0] contentView]' if options.upwards: view = arguments[0] description = viewHelpers.upwardsRecursiveDescription(view, maxDepth) if description: print(description) else: print('Failed to walk view hierarchy. Make sure you pass a view, not any other kind of object or expression.') else: printingMethod = 'recursiveDescription' if isMac: printingMethod = '_subtreeDescription' description = fb.evaluateExpressionValue('(id)[' + arguments[0] + ' ' + printingMethod + ']').GetObjectDescription() if description: description = description.replace(' |', ' |') if maxDepth > 0: separator = re.escape(" | ") prefixToRemove = separator * maxDepth + " " description += "\n" description = re.sub(r'%s.*\n' % (prefixToRemove), r'', description) else: description = '\n'.join([l if not l.startswith(' |') else l[4:] for l in description.splitlines()]) print(description)
def upwardsRecursiveDescription(view): if not fb.evaluateBooleanExpression('[(id)%s isKindOfClass:(Class)[UIView class]]' % view): return None currentView = view recursiveDescription = [] while currentView: viewDescription = fb.evaluateExpressionValue('(id)[%s debugDescription]' % (currentView)).GetObjectDescription() currentView = fb.evaluateExpression('(void*)[%s superview]' % (currentView)) try: if int(currentView, 0) == 0: currentView = None except: currentView = None if viewDescription: recursiveDescription.insert(0, viewDescription) if len(viewDescription) == 0: return None currentPrefix = "" builder = "" for viewDescription in recursiveDescription: builder += currentPrefix + viewDescription + "\n" currentPrefix += " | " return builder
def visible_frame(self): """ :return: XCUIElement visible frame :rtype: lldb.SBValue """ import_uikit() return fb.evaluateExpressionValue("(CGRect)[{} visibleFrame]".format(self.element_value))
def hit_point_for_scrolling(self): """ :return: XCUIElement hit point for scrolling :rtype: lldb.SBValue """ import_uikit() return fb.evaluateExpressionValue("(CGPoint)[{} hitPointForScrolling]".format(self.element_value))
def is_top_level_touch_bar_element(self): """ :return: XCUIElement is top level touch bar element :rtype: lldb.SBValue """ return fb.evaluateExpressionValue( "(BOOL)[{} isTopLevelTouchBarElement]".format(self.element_value))
def prettyPrintInvocation(frame, invocation): object = fb.evaluateExpression("(id)[(id)" + invocation + " target]") description = fb.evaluateExpressionValue( "(id)" + invocation).GetObjectDescription() argDescriptions = description.splitlines(True)[4:] print("NSInvocation: " + invocation) print("self: " + fb.evaluateExpression("(id)" + object)) if len(argDescriptions) > 0: print("\n" + str(len(argDescriptions)) + " Arguments:" if len(argDescriptions) > 1 else "\nArgument:") index = 2 for argDescription in argDescriptions: s = re.sub(r"argument [0-9]+: ", "", argDescription) address = findArgAdressAtIndexFromStackFrame(frame, index) encoding = s.split(" ")[0] description = " ".join(s.split(" ")[1:]) readableString = argumentAsString(frame, address, encoding) if readableString: print(readableString) else: if encoding[0] == "{": encoding = encoding[1:] print((hex(address) + ", address of " + encoding + " " + description).strip()) index += 1
def run(self, arguments, options): maxDepth = int(options.depth) isMac = runtimeHelpers.isMacintoshArch() if arguments[0] == "__keyWindow_dynamic__": arguments[0] = "(id)[[UIApplication sharedApplication] keyWindow]" if isMac: arguments[0] = "(id)[[[[NSApplication sharedApplication] windows] objectAtIndex:0] contentView]" if options.upwards: view = arguments[0] description = viewHelpers.upwardsRecursiveDescription(view, maxDepth) if description: print description else: print "Failed to walk view hierarchy. Make sure you pass a view, not any other kind of object or expression." else: printingMethod = "recursiveDescription" if isMac: printingMethod = "_subtreeDescription" description = fb.evaluateExpressionValue( "(id)[" + arguments[0] + " " + printingMethod + "]" ).GetObjectDescription() if maxDepth > 0: separator = re.escape(" | ") prefixToRemove = separator * maxDepth + " " description += "\n" description = re.sub(r"%s.*\n" % (prefixToRemove), r"", description) print description
def printInvocationForFrame(frame): print frame symbolName = frame.GetSymbol().GetName() if not re.match(r'[-+]\s*\[.*\]', symbolName): return self = findArgAtIndexFromStackFrame(frame, 0) cmd = findArgAtIndexFromStackFrame(frame, 1) commandForSignature = '[(id)' + self + ' methodSignatureForSelector:(char *)sel_getName((SEL)' + cmd + ')]' signatureValue = fb.evaluateExpressionValue('(id)' + commandForSignature) if signatureValue.GetError() is not None and str(signatureValue.GetError()) != 'success': print "My sincerest apologies. I couldn't find a method signature for the selector." return signature = signatureValue.GetValue() arg0 = stackStartAddressInSelectedFrame(frame) commandForInvocation = '[NSInvocation _invocationWithMethodSignature:(id)' + signature + ' frame:((void *)' + str(arg0) + ')]' invocation = fb.evaluateExpression('(id)' + commandForInvocation) if invocation: prettyPrintInvocation(frame, invocation) else: print frame
def tableViewInHierarchy(): viewDescription = fb.evaluateExpressionValue('(id)[(id)[[UIApplication sharedApplication] keyWindow] recursiveDescription]').GetObjectDescription() searchView = None # Try to find an instance of classPattern = re.compile(r'UITableView: (0x[0-9a-fA-F]+);') for match in re.finditer(classPattern, viewDescription): searchView = match.group(1) break # Try to find a direct subclass if not searchView: subclassPattern = re.compile(r'(0x[0-9a-fA-F]+); baseClass = UITableView;') for match in re.finditer(subclassPattern, viewDescription): searchView = match.group(1) break # SLOW: check every pointer in town if not searchView: pattern = re.compile(r'(0x[0-9a-fA-F]+)[;>]') for (view) in re.findall(pattern, viewDescription): if fb.evaluateBooleanExpression('[' + view + ' isKindOfClass:(id)[UITableView class]]'): searchView = view break return searchView
def _showLayer(layer): layer = '(' + layer + ')' size = '((CGRect)[(id)' + layer + ' bounds]).size' width = float(fb.evaluateExpression('(CGFloat)(' + size + '.width)')) height = float(fb.evaluateExpression('(CGFloat)(' + size + '.height)')) if width == 0.0 or height == 0.0: print('Nothing to see here - the size of this element is {} x {}.'. format(width, height)) return fb.evaluateEffect('UIGraphicsBeginImageContextWithOptions(' + size + ', NO, 0.0)') fb.evaluateEffect( '[(id)' + layer + ' renderInContext:(void *)UIGraphicsGetCurrentContext()]') result = fb.evaluateExpressionValue( '(UIImage *)UIGraphicsGetImageFromCurrentImageContext()') if result.GetError() is not None and str(result.GetError()) != 'success': print(result.GetError()) else: image = result.GetValue() _showImage(image) fb.evaluateEffect('UIGraphicsEndImageContext()')
def prettyPrintInvocation(frame, invocation): object = fb.evaluateExpression('(id)[(id)' + invocation + ' target]') description = fb.evaluateExpressionValue('(id)' + invocation).GetObjectDescription() argDescriptions = description.splitlines(True)[4:] print 'NSInvocation: ' + invocation print 'self: ' + fb.evaluateExpression('(id)' + object) if len(argDescriptions) > 0: print '\n' + str(len(argDescriptions)) + ' Arguments:' if len(argDescriptions) > 1 else '\nArgument:' index = 2 for argDescription in argDescriptions: s = re.sub(r'argument [0-9]+: ', '', argDescription) address = findArgAdressAtIndexFromStackFrame(frame, index) encoding = s.split(' ')[0] description = ' '.join(s.split(' ')[1:]) readableString = argumentAsString(frame, address, encoding) if readableString: print readableString else: if encoding[0] == '{': encoding = encoding[1:] print (hex(address) + ', address of ' + encoding + ' ' + description).strip() index += 1
def _showColor(color): color = '(' + color + ')' colorToUse = color isCF = _colorIsCGColorRef(color) if isCF: colorToUse = '[[UIColor alloc] initWithCGColor:(CGColorRef){}]'.format( color) else: isCI = objectHelpers.isKindOfClass(color, 'CIColor') if isCI: colorToUse = '[UIColor colorWithCIColor:(CIColor *){}]'.format( color) imageSize = 58 fb.evaluateEffect( 'UIGraphicsBeginImageContextWithOptions((CGSize)CGSizeMake({imageSize}, {imageSize}), NO, 0.0)' .format(imageSize=imageSize)) fb.evaluateEffect('[(id){} setFill]'.format(colorToUse)) fb.evaluateEffect( 'UIRectFill((CGRect)CGRectMake(0.0, 0.0, {imageSize}, {imageSize}))'. format(imageSize=imageSize)) result = fb.evaluateExpressionValue( '(UIImage *)UIGraphicsGetImageFromCurrentImageContext()') if result.GetError() is not None and str(result.GetError()) != 'success': print("got error {}".format(result)) print(result.GetError()) else: image = result.GetValue() _showImage(image) fb.evaluateEffect('UIGraphicsEndImageContext()')
def tableViewInHierarchy(): viewDescription = fb.evaluateExpressionValue( "(id)[(id)[[UIApplication sharedApplication] keyWindow] recursiveDescription]" ).GetObjectDescription() searchView = None # Try to find an instance of classPattern = re.compile(r"UITableView: (0x[0-9a-fA-F]+);") for match in re.finditer(classPattern, viewDescription): searchView = match.group(1) break # Try to find a direct subclass if not searchView: subclassPattern = re.compile(r"(0x[0-9a-fA-F]+); baseClass = UITableView;") for match in re.finditer(subclassPattern, viewDescription): searchView = match.group(1) break # SLOW: check every pointer in town if not searchView: pattern = re.compile(r"(0x[0-9a-fA-F]+)[;>]") for view in re.findall(pattern, viewDescription): if fb.evaluateBooleanExpression("[" + view + " isKindOfClass:(id)[UITableView class]]"): searchView = view break return searchView
def maskView(viewOrLayer, color, alpha): unmaskView(viewOrLayer) window = fb.evaluateExpression( '(UIWindow *)[[UIApplication sharedApplication] keyWindow]') origin = convertPoint(0, 0, viewOrLayer, window) size = fb.evaluateExpressionValue('(CGSize)((CGRect)[(id)%s frame]).size' % viewOrLayer) rectExpr = '(CGRect){{%s, %s}, {%s, %s}}' % ( origin.GetChildMemberWithName('x').GetValue(), origin.GetChildMemberWithName('y').GetValue(), size.GetChildMemberWithName('width').GetValue(), size.GetChildMemberWithName('height').GetValue()) mask = fb.evaluateExpression('(id)[[UIView alloc] initWithFrame:%s]' % rectExpr) lldb.debugger.HandleCommand('expr (void)[%s setTag:(NSInteger)%s]' % (mask, viewOrLayer)) lldb.debugger.HandleCommand( 'expr (void)[%s setBackgroundColor:[UIColor %sColor]]' % (mask, color)) lldb.debugger.HandleCommand('expr (void)[%s setAlpha:(CGFloat)%s]' % (mask, alpha)) lldb.debugger.HandleCommand('expr (void)[%s addSubview:%s]' % (window, mask)) flushCoreAnimationTransaction()
def run(self, arguments, options): objectToPrint = fb.evaluateInputExpression(arguments[0]) pretty = 1 if options.plain is None else 0 jsonData = fb.evaluateObjectExpression('[NSJSONSerialization dataWithJSONObject:(id){} options:{} error:nil]'.format(objectToPrint, pretty)) jsonString = fb.evaluateExpressionValue('(NSString*)[[NSString alloc] initWithData:(id){} encoding:4]'.format(jsonData)).GetObjectDescription() print jsonString
def run(self, arguments, options): maxDepth = int(options.depth) isMac = runtimeHelpers.isMacintoshArch() if arguments[0] == '__keyWindow_dynamic__': arguments[0] = '(id)[[UIApplication sharedApplication] keyWindow]' if isMac: arguments[ 0] = '(id)[[[[NSApplication sharedApplication] windows] objectAtIndex:0] contentView]' if options.upwards: view = arguments[0] description = viewHelpers.upwardsRecursiveDescription( view, maxDepth) if description: print description else: print 'Failed to walk view hierarchy. Make sure you pass a view, not any other kind of object or expression.' else: printingMethod = 'recursiveDescription' if isMac: printingMethod = '_subtreeDescription' description = fb.evaluateExpressionValue( '(id)[' + arguments[0] + ' ' + printingMethod + ']').GetObjectDescription() if maxDepth > 0: separator = re.escape(" | ") prefixToRemove = separator * maxDepth + " " description += "\n" description = re.sub(r'%s.*\n' % (prefixToRemove), r'', description) print description
def run(self, arguments, options): request = arguments[0] HTTPHeaderSring = '' HTTPMethod = fb.evaluateExpressionValue('(id)[{} HTTPMethod]'.format(request)).GetObjectDescription() URL = fb.evaluateExpressionValue('(id)[{} URL]'.format(request)).GetObjectDescription() timeout = fb.evaluateExpression('(NSTimeInterval)[{} timeoutInterval]'.format(request)) HTTPHeaders = fb.evaluateObjectExpression('(id)[{} allHTTPHeaderFields]'.format(request)) HTTPHeadersCount = fb.evaluateIntegerExpression('[{} count]'.format(HTTPHeaders)) allHTTPKeys = fb.evaluateObjectExpression('[{} allKeys]'.format(HTTPHeaders)) for index in range(0, HTTPHeadersCount): key = fb.evaluateObjectExpression('[{} objectAtIndex:{}]'.format(allHTTPKeys, index)) keyDescription = fb.evaluateExpressionValue('(id){}'.format(key)).GetObjectDescription() value = fb.evaluateExpressionValue('(id)[(id){} objectForKey:{}]'.format(HTTPHeaders, key)).GetObjectDescription() if len(HTTPHeaderSring) > 0: HTTPHeaderSring += ' ' HTTPHeaderSring += '-H "{}: {}"'.format(keyDescription, value) HTTPData = fb.evaluateObjectExpression('[{} HTTPBody]'.format(request)) dataFile = None dataAsString = None if fb.evaluateIntegerExpression('[{} length]'.format(HTTPData)) > 0: if options.embed: if fb.evaluateIntegerExpression('[{} respondsToSelector:@selector(base64EncodedStringWithOptions:)]'.format(HTTPData)): dataAsString = fb.evaluateExpressionValue('(id)[(id){} base64EncodedStringWithOptions:0]'.format(HTTPData)).GetObjectDescription() else : print('This version of OS doesn\'t supports base64 data encoding') return False elif not runtimeHelpers.isIOSDevice(): dataFile = self.generateTmpFilePath() if not fb.evaluateBooleanExpression('(BOOL)[{} writeToFile:@"{}" atomically:NO]'.format(HTTPData, dataFile)): print('Can\'t write data to file {}'.format(dataFile)) return False else: print('HTTPBody data for iOS Device is supported only with "--embed-data" flag') return False commandString = '' if dataAsString is not None and len(dataAsString) > 0: dataFile = self.generateTmpFilePath() commandString += 'echo "{}" | base64 -D -o "{}" && '.format(dataAsString, dataFile) commandString += 'curl -X {} --connect-timeout {}'.format(HTTPMethod, timeout) if len(HTTPHeaderSring) > 0: commandString += ' ' + HTTPHeaderSring if dataFile is not None: commandString += ' --data-binary @"{}"'.format(dataFile) commandString += ' "{}"'.format(URL) print(commandString)
def run(self, arguments, options): path = fb.evaluateExpressionValue('(NSString*)[[NSBundle mainBundle] bundlePath]') pathString = '{}'.format(path).split('"')[1] cmd = 'echo {} | tr -d "\n" | pbcopy'.format(pathString) os.system(cmd) print pathString if options.open: os.system('open '+ pathString)
def run(self, arguments, options): first = None haystack = fb.evaluateExpressionValue( "(id)[[[UIApplication sharedApplication] keyWindow] recursiveDescription]" ).GetObjectDescription() needle = arguments[0] allViews = re.findall(".* (0x[0-9a-fA-F]*);.*", haystack) for view in allViews: a11yLabel = fb.evaluateExpressionValue("(id)[(" + view + ") accessibilityLabel]").GetObjectDescription() if re.match(r".*" + needle + ".*", a11yLabel, re.IGNORECASE): print("{} {}".format(view, a11yLabel)) if first == None: first = view cmd = 'echo %s | tr -d "\n" | pbcopy' % first os.system(cmd)
def forceStartAccessibilityServer(): #We try to start accessibility server only if we don't have needed method active if not fb.evaluateBooleanExpression('[UIView instancesRespondToSelector:@selector(_accessibilityElementsInContainer:)]'): #Starting accessibility server is different for simulator and device if fb.evaluateExpressionValue('(id)[[UIDevice currentDevice] model]').GetObjectDescription().lower().find('simulator') >= 0: lldb.debugger.HandleCommand('expr (void)[[UIApplication sharedApplication] accessibilityActivate]') else: lldb.debugger.HandleCommand('expr (void)[[[UIApplication sharedApplication] _accessibilityBundlePrincipalClass] _accessibilityStartServer]')
def run(self, arguments, options): #Convert to NSObject first to allow for objc runtime to process it objectToPrint = fb.evaluateInputExpression('{obj} as NSObject'.format(obj=arguments[0])) pretty = 1 if options.plain is None else 0 jsonData = fb.evaluateObjectExpression('[NSJSONSerialization dataWithJSONObject:(NSObject*){} options:{} error:nil]'.format(objectToPrint, pretty)) jsonString = fb.evaluateExpressionValue('(NSString*)[[NSString alloc] initWithData:(NSObject*){} encoding:4]'.format(jsonData)).GetObjectDescription() print jsonString
def run(self, arguments, options): tableView = tableViewInHierarchy() if tableView: viewValue = fb.evaluateExpressionValue(tableView) print viewValue.GetObjectDescription() cmd = 'echo %s | tr -d "\n" | pbcopy' % tableView os.system(cmd) else: print "Sorry, chump. I couldn't find a table-view. :'("
def printMatchesInViewOutputStringAndCopyFirstToClipboard(needle, haystack, result): matches = re.findall('.*<.*' + needle + '.*: (0x[0-9a-fA-F]*);.*', haystack, re.IGNORECASE) for match in matches: className = fb.evaluateExpressionValue('(id)[(' + match + ') class]').GetObjectDescription() fb.printResult(('{} {}'.format(match, className)), result) if len(matches) > 0: cmd = 'echo %s | tr -d "\n" | pbcopy' % matches[0] os.system(cmd)
def run(self, arguments, options, result): tableView = tableViewInHierarchy() if tableView: viewValue = fb.evaluateExpressionValue(tableView) fb.printResult(viewValue.GetObjectDescription(), result) cmd = 'echo %s | tr -d "\n" | pbcopy' % tableView os.system(cmd) else: fb.printResult('Sorry, chump. I couldn\'t find a table-view. :\'(', result)