def accessibilityGrepHierarchy(self, view, needle): a11yLabel = accessibilityLabel(view) # if we don't have any accessibility string - we should have some children if int(a11yLabel.GetValue(), 16) == 0: # We call private method that gives back all visible accessibility children # for view iOS 10 and higher if fb.evaluateBooleanExpression( "[UIView respondsToSelector:@selector(_accessibilityElementsAndContainersDescendingFromViews:options:sorted:)]" # noqa B950 ): accessibilityElements = fb.evaluateObjectExpression( "[UIView _accessibilityElementsAndContainersDescendingFromViews:@[(id)%s] options:0 sorted:NO]" # noqa B950 % view) else: accessibilityElements = fb.evaluateObjectExpression( "[[[UIApplication sharedApplication] keyWindow] _accessibilityElementsInContainer:0 topLevel:%s includeKB:0]" # noqa B950 % view) accessibilityElementsCount = fb.evaluateIntegerExpression( "[%s count]" % accessibilityElements) for index in range(0, accessibilityElementsCount): subview = fb.evaluateObjectExpression( "[%s objectAtIndex:%i]" % (accessibilityElements, index)) self.accessibilityGrepHierarchy(subview, needle) elif re.match(r".*" + needle + ".*", a11yLabel.GetObjectDescription(), re.IGNORECASE): classDesc = objHelpers.className(view) print("({} {}) {}".format(classDesc, view, a11yLabel.GetObjectDescription())) # First element that is found is copied to clipboard if not self.foundElement: self.foundElement = True cmd = 'echo %s | tr -d "\n" | pbcopy' % view os.system(cmd)
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 run(self, arguments, options): commandForObject, ivarName = arguments objectAddress = int(fb.evaluateObjectExpression(commandForObject), 0) ivarOffsetCommand = '(ptrdiff_t)ivar_getOffset((void*)object_getInstanceVariable((id){}, "{}", 0))'.format( objectAddress, ivarName) ivarOffset = int(fb.evaluateExpression(ivarOffsetCommand), 0) # A multi-statement command allows for variables scoped to the command, # not permanent in the session like $variables. ivarSizeCommand = ( "unsigned int size = 0;" 'char *typeEncoding = (char *)ivar_getTypeEncoding((void*)class_getInstanceVariable((Class)object_getClass((id){}), "{}"));' "(char *)NSGetSizeAndAlignment(typeEncoding, &size, 0);" "size").format(objectAddress, ivarName) ivarSize = int(fb.evaluateExpression(ivarSizeCommand), 0) error = lldb.SBError() watchpoint = lldb.debugger.GetSelectedTarget().WatchAddress( objectAddress + ivarOffset, ivarSize, False, True, error) if error.Success(): print( "Remember to delete the watchpoint using: watchpoint delete {}" .format(watchpoint.GetID())) else: print("Could not create the watchpoint: {}".format( error.GetCString()))
def _recursiveViewControllerDescriptionWithPrefixAndChildPrefix( vc, string, prefix, childPrefix): isMac = runtimeHelpers.isMacintoshArch() s = "%s%s%s\n" % ( prefix, "" if prefix == "" else " ", _viewControllerDescription(vc), ) nextPrefix = childPrefix + " |" numChildViewControllers = fb.evaluateIntegerExpression( "(int)[(id)[%s childViewControllers] count]" % (vc)) for i in range(0, numChildViewControllers): viewController = fb.evaluateExpression( "(id)[(id)[%s childViewControllers] objectAtIndex:%d]" % (vc, i)) s += _recursiveViewControllerDescriptionWithPrefixAndChildPrefix( viewController, string, nextPrefix, nextPrefix) if not isMac: isModal = fb.evaluateBooleanExpression( "%s != nil && ((id)[(id)[(id)%s presentedViewController] presentingViewController]) == %s" # noqa B950 % (vc, vc, vc)) if isModal: modalVC = fb.evaluateObjectExpression( "(id)[(id)%s presentedViewController]" % (vc)) s += _recursiveViewControllerDescriptionWithPrefixAndChildPrefix( modalVC, string, childPrefix + " *M", nextPrefix) s += "\n// '*M' means the view controller is presented modally." return string + s
def run(self, arguments, options): object = fb.evaluateObjectExpression(arguments[0]) isHidden = fb.evaluateBooleanExpression("[" + object + " isHidden]") shouldHide = not isHidden for _ in range(0, 2): viewHelpers.setViewHidden(object, shouldHide) viewHelpers.setViewHidden(object, isHidden)
def firstSubviewOfView(view): subviews = subviewsOfView(view) numViews = fb.evaluateIntegerExpression("[(id)" + subviews + " count]") if numViews == 0: return None else: return fb.evaluateObjectExpression("[" + subviews + " objectAtIndex:0]")
def nextResponder(object): command = "[((id){}) nextResponder]".format(object) nextResponder = fb.evaluateObjectExpression(command) try: if int(nextResponder, 0): return nextResponder else: return None except Exception: return None
def run(self, arguments, options): command = arguments[0] if len(command.split(".")) == 1: lldb.debugger.HandleCommand("po " + command) else: objectToMessage, keypath = command.split(".", 1) object = fb.evaluateObjectExpression(objectToMessage) print( fb.describeObject('[{} valueForKeyPath:@"{}"]'.format( object, keypath)))
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): # 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 nthSiblingOfView(view, n): subviews = subviewsOfView(superviewOfView(view)) numViews = fb.evaluateIntegerExpression("[(id)" + subviews + " count]") idx = fb.evaluateIntegerExpression("[(id)" + subviews + " indexOfObject:" + view + "]") newIdx = idx + n while newIdx < 0: newIdx += numViews newIdx = newIdx % numViews return fb.evaluateObjectExpression("[(id)" + subviews + " objectAtIndex:" + str(newIdx) + "]")
def accessibilityElements(view): if fb.evaluateBooleanExpression( "[UIView instancesRespondToSelector:@selector(accessibilityElements)]" ): a11yElements = fb.evaluateExpression( "(id)[%s accessibilityElements]" % view, False) if int(a11yElements, 16) != 0: return a11yElements if fb.evaluateBooleanExpression( "[%s respondsToSelector:@selector(_accessibleSubviews)]" % view): return fb.evaluateExpression("(id)[%s _accessibleSubviews]" % (view), False) else: return fb.evaluateObjectExpression( "[[[UIApplication sharedApplication] keyWindow] _accessibilityElementsInContainer:0 topLevel:%s includeKB:0]" # noqa B950 % view)
def run(self, arguments, options): object = fb.evaluateObjectExpression(arguments[0]) if options.appleWay: if fb.evaluateBooleanExpression( "[{} respondsToSelector:@selector(_ivarDescription)]".format(object) ): command = "po [{} _ivarDescription]".format(object) else: print("Sorry, but it seems Apple dumped the _ivarDescription method") return else: objectClass = fb.evaluateExpressionValue( "(id)[(id)(" + object + ") class]" ).GetObjectDescription() command = "p *(({} *)((id){}))".format(objectClass, object) lldb.debugger.HandleCommand(command)
def printAccessibilityIdentifiersHierarchy(view, indent=0): a11yIdentifier = accessibilityIdentifier(view) classDesc = objHelpers.className(view) indentString = " | " * indent # if we don't have any accessibility identifier - we should have some children if int(a11yIdentifier.GetValue(), 16) == 0: print(indentString + ("{} {}".format(classDesc, view))) # We call private method that gives back all visible accessibility children # for view a11yElements = accessibilityElements(view) accessibilityElementsCount = int( fb.evaluateExpression("(int)[%s count]" % a11yElements)) for index in range(0, accessibilityElementsCount): subview = fb.evaluateObjectExpression("[%s objectAtIndex:%i]" % (a11yElements, index)) printAccessibilityIdentifiersHierarchy(subview, indent + 1) else: print(indentString + ("({} {}) {}".format( classDesc, view, a11yIdentifier.GetObjectDescription())))
def subviewAtIndex(views, index): return fb.evaluateObjectExpression("[%s objectAtIndex:%i]" % (views, index))
def subviewsOfView(view): return fb.evaluateObjectExpression("[%s subviews]" % view)
def rootView(): return fb.evaluateObjectExpression( "[[UIApplication sharedApplication] keyWindow]")
def setTextInView(view, text): fb.evaluateObjectExpression('[%s setText:@"%s"]' % (view, text)) viewHelpers.flushCoreAnimationTransaction()
def accessibilityIdentifier(view): return fb.evaluateObjectExpression("[%s accessibilityIdentifier]" % view)
def run(self, args, options): viewOrLayer = fb.evaluateObjectExpression(args[0]) viewHelpers.unmaskView(viewOrLayer)
def run(self, args, options): viewOrLayer = fb.evaluateObjectExpression(args[0]) viewHelpers.maskView(viewOrLayer, options.color, options.alpha)
def run(self, arguments, options): request = fb.evaluateInputExpression(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( # noqa B950 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' # noqa B950 ) 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): forceStartAccessibilityServer() rootView = fb.evaluateObjectExpression( "[[UIApplication sharedApplication] keyWindow]") self.foundElement = False self.accessibilityGrepHierarchy(rootView, arguments[0])
def subviewsOfView(view): return fb.evaluateObjectExpression("[" + view + " subviews]")
def viewControllerRecursiveDescription(vc): return _recursiveViewControllerDescriptionWithPrefixAndChildPrefix( fb.evaluateObjectExpression(vc), "", "", "")
def _copyFromURL(url, preferredFilename, noOpen): data = fb.evaluateObjectExpression( '(id)[NSData dataWithContentsOfURL:(id){}]'.format(url)) defaultFilename = fb.describeObject( '(id)[[{} pathComponents] lastObject]'.format(url)) _copyFromData(data, defaultFilename, preferredFilename, noOpen)
def run(self, arguments, options): expression = arguments[0] methodPattern = re.compile( r""" (?P<scope>[-+])? \[ (?P<target>.*?) (?P<category>\(.+\))? \s+ (?P<selector>.*) \] """, re.VERBOSE, ) match = methodPattern.match(expression) if not match: print("Failed to parse expression. Do you even Objective-C?!") return expressionForSelf = objc.functionPreambleExpressionForSelf() if not expressionForSelf: arch = objc.currentArch() print( "Your architecture, {}, is truly fantastic. However, I don't currently support it." .format(arch)) return methodTypeCharacter = match.group("scope") classNameOrExpression = match.group("target") category = match.group("category") selector = match.group("selector") methodIsClassMethod = methodTypeCharacter == "+" if not methodIsClassMethod: # The default is instance method, and methodTypeCharacter # may not actually be '-'. methodTypeCharacter = "-" targetIsClass = False targetObject = fb.evaluateObjectExpression( "({})".format(classNameOrExpression), False) if not targetObject: # If the expression didn't yield anything then it's likely a class. # Assume it is. We will check again that the class does actually # exist anyway. targetIsClass = True targetObject = fb.evaluateObjectExpression( "[{} class]".format(classNameOrExpression), False) targetClass = fb.evaluateObjectExpression( "[{} class]".format(targetObject), False) if not targetClass or int(targetClass, 0) == 0: print( 'Couldn\'t find a class from the expression "{}". Did you typo?' .format(classNameOrExpression)) return if methodIsClassMethod: targetClass = objc.object_getClass(targetClass) found = False nextClass = targetClass while not found and int(nextClass, 0) > 0: if classItselfImplementsSelector(nextClass, selector): found = True else: nextClass = objc.class_getSuperclass(nextClass) if not found: print( "There doesn't seem to be an implementation of {} in the class hierarchy. Made a boo boo with the selector name?" .format(selector)) return breakpointClassName = objc.class_getName(nextClass) formattedCategory = category if category else "" breakpointFullName = "{}[{}{} {}]".format(methodTypeCharacter, breakpointClassName, formattedCategory, selector) if targetIsClass: breakpointCondition = "(void*)object_getClass({}) == {}".format( expressionForSelf, targetClass) else: breakpointCondition = "(void*){} == {}".format( expressionForSelf, targetObject) print("Setting a breakpoint at {} with condition {}".format( breakpointFullName, breakpointCondition)) if category: lldb.debugger.HandleCommand( 'breakpoint set --skip-prologue false --fullname "{}" --condition "{}"' .format(breakpointFullName, breakpointCondition)) else: breakpointPattern = r"{}\[{}(\(.+\))? {}\]".format( methodTypeCharacter, breakpointClassName, selector) lldb.debugger.HandleCommand( 'breakpoint set --skip-prologue false --func-regex "{}" --condition "{}"' .format(breakpointPattern, breakpointCondition))
def superviewOfView(view): superview = fb.evaluateObjectExpression("[" + view + " superview]") if int(superview, 16) == 0: return None return superview