def removeDebugHUD(debugger, command, exe_ctx, result, internal_dict) -> None: """ Syntax: removehud Examples: (lldb) removehud This command is implemented in HMDebugHUD.py """ global gClassName if not HM.existClass(gClassName): HM.DPrint(f"{gClassName} does not exist.") return command_script = f''' UIView *keyWindow = [UIApplication sharedApplication].keyWindow; Class HUDClass = (Class)objc_lookUpClass("{gClassName}"); UIView *objView = nil; for (UIView *subView in keyWindow.subviews) {{ if ([subView isKindOfClass:HUDClass]) {{ objView = subView; break; }} }} [objView removeFromSuperview]; objView; ''' val = HM.evaluateExpressionValue(command_script) if HM.judgeSBValueHasValue(val): HM.DPrint("remove done!") else: HM.DPrint(f"{gClassName} does not exist.")
def registerProtocol(): global gProtocolName if HM.existClass(gProtocolName): return # Register class HM.DPrint(f"Register {gProtocolName}...") classValue = HM.allocateClass(gProtocolName, "NSURLProtocol") HM.registerClass(classValue.GetValue()) # Add methods HM.DPrint(f"Add methods to {gProtocolName}...") canInitWithRequestIMPValue = makeCanInitWithRequestIMP() if not HM.judgeSBValueHasValue(canInitWithRequestIMPValue): return HM.addClassMethod(gProtocolName, "canInitWithRequest:", canInitWithRequestIMPValue.GetValue(), "B@:@") HM.DPrint(f"Register {gProtocolName} done!") # register NSURLProtocol registerClassExp = f"[NSURLProtocol registerClass:(Class){classValue.GetValue()}]" HM.evaluateExpressionValue(registerClassExp)
def register() -> None: if HM.existClass(gClassName): return # Register class HMProgressHUD.show(f"Register {gClassName}...") HM.DPrint(f"Register {gClassName}...") classValue = HM.allocateClass(gClassName, HMDebugBaseViewController.gClassName) HM.addIvar(classValue.GetValue(), "_leftTextArray", "NSMutableArray *") HM.addIvar(classValue.GetValue(), "_rightTextArray", "NSMutableArray *") HM.registerClass(classValue.GetValue()) # Add methods HM.DPrint(f"Add methods to {gClassName}...") viewDidLoadIMPValue = makeViewDidLoadIMP() if not HM.judgeSBValueHasValue(viewDidLoadIMPValue): HMProgressHUD.hide() return HM.addInstanceMethod(gClassName, "viewDidLoad", viewDidLoadIMPValue.GetValue(), "v@:") # Methods related to tableView. HM.DPrint(f"Add methods to {gClassName}......") if not addTableViewMethods(): HMProgressHUD.hide() return HM.DPrint(f"Register {gClassName} done!") HMProgressHUD.hide()
def properties(debugger, command, exe_ctx, result, internal_dict): """ Syntax: properties <className/classInstance> Examples: (lldb) properties UIViewController (lldb) properties [NSObject new] (lldb) expression -l objc -O -- [NSObject new] <NSObject: 0x60000372f760> (lldb) properties 0x60000372f760 This command is implemented in HMClassInfoCommands.py """ if len(command) == 0: HM.DPrint( "Requires a argument, Please enter \"help properties\" for help.") return value = HM.evaluateExpressionValue( expression= f'(NSString *)[{command} performSelector:NSSelectorFromString(@"_propertyDescription")]', printErrors=False) if HM.successOfSBError(value.GetError()): HM.DPrint(value.GetObjectDescription()) return clsPrefixesValue = HM.getClassPrefixes()[1] command_script = f''' Class inputClass = objc_lookUpClass("{command}"); if (inputClass == nil) {{ // Find prefixed class for (NSString *prefix in (NSMutableArray *){clsPrefixesValue.GetValue()}) {{ NSString *clsName = [prefix stringByAppendingString:@".{command}"]; inputClass = objc_lookUpClass((char *)[clsName UTF8String]); if (inputClass) {{ break; }} }} }} NSMutableString *result = [[NSMutableString alloc] init]; if (inputClass == nil) {{ [result appendString:@"Unable to resolve {command} or find {command} class, maybe {command} is not a subclass of NSObject\\n"]; }} else {{ if ((BOOL)[(Class)inputClass respondsToSelector:(SEL)NSSelectorFromString(@"_propertyDescription")]) {{ [result appendString:(NSString *)[inputClass performSelector:NSSelectorFromString(@"_propertyDescription")]]; }} else {{ [result appendString:@"{command} is not a subclass of NSObject"]; }} }} result; ''' result = HM.evaluateExpressionValue(command_script).GetObjectDescription() HM.DPrint(result)
def redirect(debugger, command, exe_ctx, result, internal_dict): """ Syntax: redirect [--append] <stdout/stderr/both> <path> Options: --append/-a; Use "a+" mode instead of "w+" mode in freopen function Examples: (lldb) redirect both /dev/ttys000 (Simulator) (lldb) redirect stdout /path/to/file (lldb) redirect -a stderr /path/to/file This command is implemented in HMRedirectStdout.py """ command_args = shlex.split(command) parser = generate_option_parser() try: # options: optparse.Values # args: list (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return argsLen = len(args) if argsLen != 2: HM.DPrint( "Error input, plase enter 'help redirect' for more infomation") return stream = args[0] path = args[1] mode = "w+" if options.append: mode = "a+" if stream == "stdout" or stream == "stderr": redirectValue = HM.evaluateExpressionValue( f"freopen(\"{path}\", \"{mode}\", {stream})") logRedirectResult(redirectValue, stream) elif stream == "both": stdoutValue = HM.evaluateExpressionValue( f"freopen(\"{path}\", \"{mode}\", stdout)") logRedirectResult(stdoutValue, "stdout") stderrValue = HM.evaluateExpressionValue( f"freopen(\"{path}\", \"{mode}\", stderr)") logRedirectResult(stderrValue, "stderr") else: HM.DPrint( f"Error input(\"{stream}\"), plase enter \"help redirect\" for more infomation" )
def register() -> None: if HM.existClass(gClassName): return # Register class HM.DPrint(f"Register {gClassName}...") classValue = HM.allocateClass(gClassName, "UIView") HM.addIvar(classValue.GetValue(), "_contentView", "UIView *") HM.addIvar(classValue.GetValue(), "_indicator", "UIActivityIndicatorView *") HM.addIvar(classValue.GetValue(), "_textLab", "UILabel *") HM.addIvar(classValue.GetValue(), "_hideDelayTimer", "NSTimer *") HM.registerClass(classValue.GetValue()) # Add Class methods HM.DPrint(f"Add methods to {gClassName}...") sharedInstanceIMPValue = makeSharedInstanceIMP() if not HM.judgeSBValueHasValue(sharedInstanceIMPValue): return HM.addClassMethod(gClassName, "sharedInstance", sharedInstanceIMPValue.GetValue(), "@@:") showHUDIMPValue = makeShowHUDIMP() if not HM.judgeSBValueHasValue(showHUDIMPValue): return HM.addClassMethod(gClassName, "showHUD", showHUDIMPValue.GetValue(), "@@:") showOnlyTextIMPValue = makeShowOnlyTextHiddenAfterDelayIMP() if not HM.judgeSBValueHasValue(showOnlyTextIMPValue): return HM.addClassMethod(gClassName, "showOnlyText:hiddenAfterDelay:", showOnlyTextIMPValue.GetValue(), "@@:@i") hideHUDIMPValue = makeHideHUDIMP() if not HM.judgeSBValueHasValue(hideHUDIMPValue): return HM.addClassMethod(gClassName, "hideHUD", hideHUDIMPValue.GetValue(), "@@:") setTextIMPValue = makeSetTextIMP() if not HM.judgeSBValueHasValue(setTextIMPValue): return HM.addClassMethod(gClassName, "setText:", setTextIMPValue.GetValue(), "v@:@") # Add Instance methods HM.DPrint(f"Add methods to {gClassName}......") initWithFrameIMPValue = makeInitWithFrameIMP() if not HM.judgeSBValueHasValue(initWithFrameIMPValue): return HM.addInstanceMethod(gClassName, "initWithFrame:", initWithFrameIMPValue.GetValue(), "@@:{CGRect={CGPoint=dd}{CGSize=dd}}") layoutSubviewsIMPValue = makeLayoutSubviewsIMP() if not HM.judgeSBValueHasValue(layoutSubviewsIMPValue): return HM.addInstanceMethod(gClassName, "layoutSubviews", layoutSubviewsIMPValue.GetValue(), "v@:") HM.DPrint(f"Register {gClassName} done!")
def delay(debugger, command, exe_ctx, result, internal_dict): """ Syntax: delay [--continue] <second> <lldb command> Options: --continue/-c; "process continue" after executing specified lldb command Notice: If <lldb command> has options, you should enclose it in quotes. Examples: (lldb) delay 3 showhud (lldb) delay -c 2 phomedirectory (lldb) delay 0.5 push PersonalViewController (lldb) delay 2 "deletefile -f path/to/fileOrDirectory" This command is implemented in HMDelay.py """ command_args = shlex.split(command) parser = generate_option_parser() try: # options: optparse.Values # args: list (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return if len(args) < 2: HM.DPrint("Requires two arguments(second and lldb command), Please enter \"help delay\" for help.") return secondString = args[0] if not isNumber(secondString): HM.DPrint(f"\"{secondString}\" cannot be converted to seconds, Please enter \"help delay\" for help.") return debugger.SetAsync(True) debugger.HandleCommand("process continue") seconds = float(secondString) specifiedCommand: str = "" for i, item in enumerate(args): if i == 0: continue specifiedCommand += item + " " specifiedCommand = specifiedCommand.rstrip() HM.DPrint(f"Execute lldb command after {seconds} seconds: {specifiedCommand}") t = Timer(seconds, lambda: runDelayed(specifiedCommand, options.isContinue)) t.start()
def register() -> None: if HM.existClass(gClassName): return # Register class HMProgressHUD.show(f"Register {gClassName}...") HM.DPrint(f"Register {gClassName}...") classValue = HM.allocateClass(gClassName, "UIWindow") HM.registerClass(classValue.GetValue()) HM.DPrint(f"Register {gClassName} done!")
def findClass(debugger, command, exe_ctx, result, internal_dict): """ Syntax: fclass <className> Examples: (lldb) fclass (lldb) fclass UITabBarController (lldb) fclass controller Notice: Case insensitive. This command is implemented in HMClassInfoCommands.py """ HM.DPrint("Waiting...") if len(command) == 0: addObjectScript = '[classNames appendFormat:@"%@ (%p)\\n", name, classList[i]]; findCount += 1;' else: command = command.lower() addObjectScript = f''' if ([[name lowercaseString] containsString:@"{command}"]) {{ [classNames appendFormat:@"%@ (%p)\\n", name, classList[i]]; findCount += 1; }} ''' command_script = f''' unsigned int findCount = 0; unsigned int classCount; Class *classList = objc_copyClassList(&classCount); NSMutableString *classNames = [[NSMutableString alloc] init]; for (int i = 0; i < classCount; i++) {{ NSString *name = [[NSString alloc] initWithUTF8String:class_getName(classList[i])]; {addObjectScript} }} free(classList); if (findCount == 0) {{ [classNames insertString:@"No class found.\\n" atIndex:0]; }} else {{ [classNames insertString:[[NSString alloc] initWithFormat:@"Count: %u \\n", findCount] atIndex:0]; }} classNames; ''' classNames = HM.evaluateExpressionValue( command_script).GetObjectDescription() HM.DPrint(classNames)
def showFPS(debugger, command, exe_ctx, result, internal_dict): """ Syntax: showfps Examples: (lldb) showfps Notice: showfps is deprecated. Use showhud instead. This command is implemented in HMFPSLabel.py """ HM.DPrint("showfps is deprecated. Use showhud instead.") FPSClassName = "HMFPSLabel" if HM.existClass(FPSClassName): HM.DPrint("HMFPSLabel is already on display") return # Register class FPSLabelClassValue = HM.allocateClass(FPSClassName, "UILabel") HM.addIvar(FPSLabelClassValue.GetValue(), "_link", "CADisplayLink *") HM.addIvar(FPSLabelClassValue.GetValue(), "_count", "int") HM.addIvar(FPSLabelClassValue.GetValue(), "_lastTime", "double") HM.registerClass(FPSLabelClassValue.GetValue()) addToKeyWindowIMPValue = makeAddToKeyWindowIMP() if not HM.judgeSBValueHasValue(addToKeyWindowIMPValue): return HM.addClassMethod(FPSClassName, "addToKeyWindow", addToKeyWindowIMPValue.GetValue(), "@@:") tickIMPValue = makeTickIMP() if not HM.judgeSBValueHasValue(tickIMPValue): return HM.addInstanceMethod(FPSClassName, "tick:", tickIMPValue.GetValue(), "v@:@") # Show fps command addToKeyWindowCommand = ''' Class fps = NSClassFromString(@"HMFPSLabel"); (UILabel *)[fps performSelector:@selector(addToKeyWindow)]; ''' HM.evaluateExpressionValue(addToKeyWindowCommand) HM.DPrint("showfps Done!") HM.processContinue()
def register() -> None: if HM.existClass(gClassName): return HMDebugBaseViewController.register() # Register class HMProgressHUD.show(f"Register {gClassName}...") HM.DPrint(f"Register {gClassName}...") classValue = HM.allocateClass(gClassName, HMDebugBaseViewController.gClassName) HM.registerClass(classValue.GetValue()) # Add methods HM.DPrint(f"Add methods to {gClassName}...") presentIMPValue = makePresentIMP() if not HM.judgeSBValueHasValue(presentIMPValue): HMProgressHUD.hide() return HM.addClassMethod(gClassName, "present", presentIMPValue.GetValue(), "@@:") viewDidLoadIMPValue = makeViewDidLoadIMP() if not HM.judgeSBValueHasValue(viewDidLoadIMPValue): HMProgressHUD.hide() return HM.addInstanceMethod(gClassName, "viewDidLoad", viewDidLoadIMPValue.GetValue(), "v@:") dismissSelfIMPValue = makeDismissSelfIMP() if not HM.judgeSBValueHasValue(dismissSelfIMPValue): HMProgressHUD.hide() return HM.addInstanceMethod(gClassName, "dismissSelf", dismissSelfIMPValue.GetValue(), "v@:") # Methods related to tableView. HM.DPrint(f"Add methods to {gClassName}......") if not addTableViewMethods(): HMProgressHUD.hide() return # Methods related to features. HM.DPrint(f"Add methods to {gClassName}.........") if not addFeatureMethods(): HMProgressHUD.hide() return HM.DPrint(f"Register {gClassName} done!") HMProgressHUD.hide()
def deleteAllFileInDirectory(directoryPath: str) -> None: command_script = f''' NSString *directoryPath = @"{directoryPath}"; NSMutableString *result = [[NSMutableString alloc] init]; NSFileManager *fileMgr = [NSFileManager defaultManager]; if ([fileMgr fileExistsAtPath:directoryPath]) {{ NSArray *subFileArray = [fileMgr contentsOfDirectoryAtPath:directoryPath error:nil]; for (NSString *subFileName in subFileArray) {{ NSString *subFilePath = [directoryPath stringByAppendingPathComponent:subFileName]; if ([fileMgr removeItemAtPath:subFilePath error:nil]) {{ [result appendFormat:@"removed file: %@\\n", subFilePath]; }} else {{ [result appendFormat:@"failed to remove file: %@\\n", subFilePath]; }} }} }} else {{ [result appendFormat:@"failed to remove non-existing file: %@\\n", directoryPath]; }} if ([result length] == 0) {{ [result appendString:@"There are no files in this directory.\\n"]; }} result; ''' result = HM.evaluateExpressionValue(command_script).GetObjectDescription() HM.DPrint(result)
def pHomeDirectory(debugger, command, exe_ctx, result, internal_dict): """ Syntax: phomedirectory [--open] Options: --open/-o; open in Finder Examples: (lldb) phomedirectory (lldb) phomedirectory -o This command is implemented in HMFileCommands.py """ command_args = shlex.split(command) parser = generate_option_parser("phomedirectory") try: # options: optparse.Values # args: list (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return homeDirectoryValue = HM.evaluateExpressionValue( '(NSString *)NSHomeDirectory()') path = homeDirectoryValue.GetObjectDescription() HM.DPrint(path) if options.open: os.system('open ' + path)
def pBundlePath(debugger, command, exe_ctx, result, internal_dict): """ Syntax: pbundlepath [--open] Options: --open/-o; open in Finder Examples: (lldb) pbundlepath (lldb) pbundlepath -o This command is implemented in HMFileCommands.py """ command_args = shlex.split(command) parser = generate_option_parser("pbundlepath") try: # options: optparse.Values # args: list (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return bundlePathValue = HM.evaluateExpressionValue( '(NSString*)[[NSBundle mainBundle] bundlePath]') path = bundlePathValue.GetObjectDescription() HM.DPrint(path) if options.open: # Cannot open the bundle, so we open the directory where the bundle is located. directoryValue = HM.evaluateExpressionValue( f'(NSString *)[(NSString *){bundlePathValue.GetValue()} stringByDeletingLastPathComponent]' ) os.system('open ' + directoryValue.GetObjectDescription())
def findSuperClass(debugger, command, exe_ctx, result, internal_dict): """ Syntax: fsuperclass <className> Examples: (lldb) fsuperclass UIButton This command is implemented in HMClassInfoCommands.py """ if len(command) == 0: HM.DPrint( "Requires a argument, Please enter \"help fsuperclass\" for help.") return clsPrefixesValue = HM.getClassPrefixes()[1] command_script = f''' Class inputClass = objc_lookUpClass("{command}"); if (inputClass == nil) {{ // Find prefixed class for (NSString *prefix in (NSMutableArray *){clsPrefixesValue.GetValue()}) {{ NSString *clsName = [prefix stringByAppendingString:@".{command}"]; inputClass = objc_lookUpClass((char *)[clsName UTF8String]); if (inputClass) {{ break; }} }} }} NSMutableString *result = [[NSMutableString alloc] init]; if (inputClass == nil) {{ [result appendString:@"Can't find {command} class\\n"]; }} else {{ [result appendString:[[NSString alloc] initWithUTF8String:class_getName(inputClass)]]; for (Class superClass = class_getSuperclass(inputClass); superClass != nil; superClass = class_getSuperclass(superClass)) {{ NSString *name = [[NSString alloc] initWithUTF8String:class_getName(superClass)]; [result appendFormat:@" : %@", name]; }} }} result; ''' classNames = HM.evaluateExpressionValue( command_script).GetObjectDescription() HM.DPrint(classNames)
def environment(debugger, command, exe_ctx, result, internal_dict): """ Syntax: environment Examples: (lldb) environment This command is implemented in HMEnvironment.py """ # Python version # LLDB version # Target triple # Git commit hash # Optimized # Xcode version # Xcode build version # Model identifier # System version HM.DPrint('[Python version] ' + sys.version.replace('\n', '\n\t\t')) HM.DPrint('[LLDB version] ' + debugger.GetVersionString().replace('\n', '\n\t\t')) HM.DPrint('[Target triple] ' + debugger.GetSelectedTarget().GetTriple()) HM.DPrint('[Git commit hash] ' + getGitCommitHash()) HM.DPrint('[Optimized] ' + getOptimizedStr()) XcodeVersionValue = HM.evaluateExpressionValue( '(NSString *)([NSBundle mainBundle].infoDictionary[@"DTXcode"] ?: @"-")' ) HM.DPrint('[Xcode version] ' + XcodeVersionValue.GetObjectDescription()) XcodeBuildVersionValue = HM.evaluateExpressionValue( '(NSString *)([NSBundle mainBundle].infoDictionary[@"DTXcodeBuild"] ?: @"-")' ) HM.DPrint('[Xcode build version] ' + XcodeBuildVersionValue.GetObjectDescription()) HM.DPrint('[Model identifier] ' + getModelIdentifier()) SystemVersionValue = HM.evaluateExpressionValue( '(NSString *)[[NSString alloc] initWithFormat:@"%@ %@", [[UIDevice currentDevice] systemName], [[UIDevice currentDevice] systemVersion]]' ) HM.DPrint('[System version] ' + SystemVersionValue.GetObjectDescription())
def plifecycle(debugger, command, exe_ctx, result, internal_dict): """ Syntax: plifecycle [-i/--ignore_system_classes] Options: --ignore_system_classes/-i; Ignore the system generated UIViewController classes Notice: You should use plifecycle in symbolic breakpoint(UIViewController's life cycle) for easier control. This command can ignore the system generated UIViewController classes, Otherwise you may use the following command directly. Method A: expression -l objc -O -- [[$arg1 description] stringByAppendingString:@" dealloc/viewDidAppear:/..."] Method B: expression -l objc -O -- @import UIKit; [[NSString alloc] initWithFormat:@"%@ %s", (id)$arg1, (char *)$arg2] Method C: Add symbolic breakpoint of "UIApplicationMain" with command "expression -l objc -O -- @import UIKit", then add symbolic breakpoint(UIViewController's life cycle) with command "expression -l objc -O -- [[NSString alloc] initWithFormat:@"%@ %s", (id)$arg1, (char *)$arg2]" This command is implemented in HMLifeCycle.py """ command_args = shlex.split(command) parser = generate_option_parser() try: # options: optparse.Values # args: list (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return selfDescription = HM.evaluateExpressionValue( "(id)$arg1").GetObjectDescription() ignore = False if options.ignore_system_classes: ignoreClasses = [ "UIAlertController", "_UIAlertControllerTextFieldViewController", "UIInputWindowController", "UICompatibilityInputViewController", "UIKeyboardCandidateGridCollectionViewController", "UISystemKeyboardDockController", "_UIRemoteInputViewController", "UIApplicationRotationFollowingController", "UISystemInputAssistantViewController", "UIPredictionViewController", "UICandidateViewController", "_SFAppPasswordSavingViewController", "SFPasswordSavingRemoteViewController" ] for className in ignoreClasses: if className in selfDescription: ignore = True break if not ignore: selectorDescription = HM.evaluateExpressionValue( "(char *)$arg2").GetSummary().strip('"') HM.DPrint(selfDescription + ' ' + selectorDescription + '\n')
def register() -> None: if HM.existClass(gClassName): return # Register class HMProgressHUD.show(f"Register {gClassName}...") HM.DPrint(f"Register {gClassName}...") classValue = HM.allocateClass(gClassName, "UIViewController") HM.registerClass(classValue.GetValue()) # Add methods HM.DPrint(f"Add methods to {gClassName}...") viewDidLoadIMPValue = makeViewDidLoadIMP() if not HM.judgeSBValueHasValue(viewDidLoadIMPValue): return HM.addInstanceMethod(gClassName, "viewDidLoad", viewDidLoadIMPValue.GetValue(), "v@:") HM.DPrint(f"Register {gClassName} done!")
def deleteFileOrDirectory(filePath: str) -> None: command_script = f''' NSString *filePath = @"{filePath}"; NSMutableString *result = [[NSMutableString alloc] init]; NSFileManager *fileMgr = [NSFileManager defaultManager]; if ([fileMgr fileExistsAtPath:filePath]) {{ if (![filePath containsString:(NSString *)NSHomeDirectory()]) {{ [result appendString:@"The file path must be in the sandbox\\n"]; }} else if ([fileMgr removeItemAtPath:filePath error:nil]) {{ [result appendFormat:@"removed file: %@\\n", filePath]; }} else {{ [result appendFormat:@"failed to remove file: %@\\n", filePath]; }} }} else {{ [result appendFormat:@"failed to remove non-existing file: %@\\n", filePath]; }} result; ''' result = HM.evaluateExpressionValue(command_script).GetObjectDescription() HM.DPrint(result)
def addFeatureMethods() -> bool: global gClassName selectedAPPInfoIMPValue = makeSelectedAPPInfoIMP() if not HM.judgeSBValueHasValue(selectedAPPInfoIMPValue): return False HM.addInstanceMethod(gClassName, "selectedAPPInfo", selectedAPPInfoIMPValue.GetValue(), "v@:") selectedSandboxIMPValue = makeSelectedSandboxIMP() if not HM.judgeSBValueHasValue(selectedSandboxIMPValue): return False HM.addInstanceMethod(gClassName, "selectedSandbox", selectedSandboxIMPValue.GetValue(), "v@:") selectedInspectViewIMPValue = makeSelectedInspectViewIMP() if not HM.judgeSBValueHasValue(selectedInspectViewIMPValue): return False HM.addInstanceMethod(gClassName, "selectedInspectView", selectedInspectViewIMPValue.GetValue(), "v@:") HM.DPrint("Add breakpoints to hook method...") HM.addOneShotBreakPointInIMP(selectedAPPInfoIMPValue, "HMDebugMainViewController.selectedAPPInfoBreakPointHandler", "HMDebugMainViewController_selectedAPPInfo_Breakpoint") HM.addOneShotBreakPointInIMP(selectedSandboxIMPValue, "HMDebugMainViewController.selectedSandboxBreakPointHandler", "HMDebugMainViewController_selectedSandbox_Breakpoint") HM.addOneShotBreakPointInIMP(selectedInspectViewIMPValue, "HMDebugMainViewController.selectedInspectViewBreakPointHandler", "HMDebugMainViewController_selectedInspectView_Breakpoint") return True
def methods(debugger, command, exe_ctx, result, internal_dict): """ Syntax: methods [--short] <className/classInstance> Examples: (lldb) methods UIViewController (lldb) methods -s UIViewController (lldb) methods [UIView new] (lldb) expression -l objc -O -- [NSObject new] <NSObject: 0x60000375f9a0> (lldb) methods 0x60000375f9a0 Options: --short/-s; Use [inputClass _shortMethodDescription] instead of [inputClass _methodDescription] This command is implemented in HMClassInfoCommands.py """ command_args = shlex.split(command) parser = generate_methods_option_parser() try: # options: optparse.Values # args: list (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return inputStr: str = "" for string in args: inputStr += string + " " inputStr = inputStr.rstrip() if len(inputStr) == 0: HM.DPrint( "Requires a argument, Please enter \"help methods\" for help.") return if options.short: selName = "_shortMethodDescription" else: selName = "_methodDescription" value = HM.evaluateExpressionValue( expression= f'(NSString *)[{inputStr} performSelector:NSSelectorFromString(@"{selName}")]', printErrors=False) if HM.successOfSBError(value.GetError()): HM.DPrint(value.GetObjectDescription()) return clsPrefixesValue = HM.getClassPrefixes()[1] command_script = f''' Class inputClass = objc_lookUpClass("{inputStr}"); if (inputClass == nil) {{ // Find prefixed class for (NSString *prefix in (NSMutableArray *){clsPrefixesValue.GetValue()}) {{ NSString *clsName = [prefix stringByAppendingString:@".{inputStr}"]; inputClass = objc_lookUpClass((char *)[clsName UTF8String]); if (inputClass) {{ break; }} }} }} NSMutableString *result = [[NSMutableString alloc] init]; if (inputClass == nil) {{ [result appendString:@"Unable to resolve {inputStr} or find {inputStr} class, maybe {inputStr} is not a subclass of NSObject\\n"]; }} else {{ if ((BOOL)[(Class)inputClass respondsToSelector:(SEL)NSSelectorFromString(@"{selName}")]) {{ [result appendString:(NSString *)[inputClass performSelector:NSSelectorFromString(@"{selName}")]]; }} else {{ [result appendString:@"{inputStr} is not a subclass of NSObject"]; }} }} result; ''' result = HM.evaluateExpressionValue(command_script).GetObjectDescription() HM.DPrint(result)
def register() -> None: if HM.existClass(gClassName): return HMProgressHUD.register() HMDebugBaseViewController.register() # Register class HMProgressHUD.show(f"Register {gClassName}...") HM.DPrint(f"Register {gClassName}...") classValue = HM.allocateClass(gClassName, HMDebugBaseViewController.gClassName) HM.addIvar(classValue.GetValue(), "_tableView", "UITableView *") HM.addIvar(classValue.GetValue(), "_currentPath", "NSString *") HM.addIvar(classValue.GetValue(), "_childPaths", "NSMutableArray *") HM.registerClass(classValue.GetValue()) # Add methods HM.DPrint(f"Add methods to {gClassName}...") viewDidLoadIMPValue = makeViewDidLoadIMP() if not HM.judgeSBValueHasValue(viewDidLoadIMPValue): HMProgressHUD.hide() return HM.addInstanceMethod(gClassName, "viewDidLoad", viewDidLoadIMPValue.GetValue(), "v@:") loadPathIMPValue = makeLoadPathIMP() if not HM.judgeSBValueHasValue(loadPathIMPValue): HMProgressHUD.hide() return HM.addInstanceMethod(gClassName, "loadPath:", loadPathIMPValue.GetValue(), "v@:@") clickBackItemIMPValue = makeClickBackItemIMP() if not HM.judgeSBValueHasValue(clickBackItemIMPValue): HMProgressHUD.hide() return HM.addInstanceMethod(gClassName, "clickBackItem", clickBackItemIMPValue.GetValue(), "v@:") clickPopItemIMPValue = makeClickPopItemIMP() if not HM.judgeSBValueHasValue(clickPopItemIMPValue): HMProgressHUD.hide() return HM.addInstanceMethod(gClassName, "clickPopItem", clickPopItemIMPValue.GetValue(), "v@:") deleteFileOrDirIMPValue = makeDeleteFileOrDirIMP() if not HM.judgeSBValueHasValue(deleteFileOrDirIMPValue): HMProgressHUD.hide() return HM.addInstanceMethod(gClassName, "deleteFileOrDir:", deleteFileOrDirIMPValue.GetValue(), "v@:@") # Methods related to tableView. HM.DPrint(f"Add methods to {gClassName}......") if not addTableViewMethods(): HMProgressHUD.hide() return HM.DPrint(f"Register {gClassName} done!") HMProgressHUD.hide()
def showDebugHUD(debugger, command, exe_ctx, result, internal_dict): """ Syntax: showhud Examples: (lldb) showhud Summary: Show debug HUD. 1.Memory footprint. 2.CPU utilization. 3.FPS in main thread. The UI style is based on https://github.com/meitu/MTHawkeye This command is implemented in HMDebugHUD.py """ global gClassName if isDisplayingHUD(): HM.DPrint(f"{gClassName} is already on display") HM.processContinue() return elif HM.existClass(gClassName): showHUDFunc() HM.processContinue() return # Register class HMProgressHUD.show(f"Register {gClassName}...") HM.DPrint(f"Register {gClassName}...") classValue = HM.allocateClass(gClassName, "UIView") HM.addIvar(classValue.GetValue(), "_link", "CADisplayLink *") HM.addIvar(classValue.GetValue(), "_count", "int") # count in 1 second HM.addIvar(classValue.GetValue(), "_lastTime", "double") HM.addIvar(classValue.GetValue(), "_memoryLab", "UILabel *") HM.addIvar(classValue.GetValue(), "_cpuUtilizationLab", "UILabel *") HM.addIvar(classValue.GetValue(), "_fpsLab", "UILabel *") HM.registerClass(classValue.GetValue()) # Add methods HM.DPrint(f"Add methods to {gClassName}...") addToKeyWindowIMPValue = makeAddToKeyWindowIMP() if not HM.judgeSBValueHasValue(addToKeyWindowIMPValue): HMProgressHUD.hide() return HM.addClassMethod(gClassName, "addToKeyWindow", addToKeyWindowIMPValue.GetValue(), "@@:") tapSelfIMPValue = makeTapSelfIMP() if not HM.judgeSBValueHasValue(tapSelfIMPValue): HMProgressHUD.hide() return HM.addInstanceMethod(gClassName, "tapSelf", tapSelfIMPValue.GetValue(), "v@:") # Add methods(update) if not addUpdateMethods(): HMProgressHUD.hide() return # Add methods(move) HM.DPrint(f"Add methods to {gClassName}......") if not addMoveMethods(): HMProgressHUD.hide() return # Add breakpoint in tapSelf HM.DPrint("Add breakpoint to hook method...") HM.addOneShotBreakPointInIMP(tapSelfIMPValue, "HMDebugHUD.tapSelfBreakPointHandler", "HMDebugHUD_TapSelf_Breakpoint") HM.DPrint(f"Register {gClassName} done!") # Show HUD command showHUDFunc() HMProgressHUD.hide() HM.processContinue()
def findSubclass(debugger, command, exe_ctx, result, internal_dict): """ Syntax: fsubclass [--nonrecursively] <className> Options: --nonrecursively/-n; Find subclass non-recursively Examples: (lldb) fsubclass UIViewController (lldb) fsubclass -n UIViewController This command is implemented in HMClassInfoCommands.py """ command_args = shlex.split(command) parser = generate_findSubclass_option_parser() try: # options: optparse.Values # args: list (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return if len(args) != 1: HM.DPrint( "Requires a argument, Please enter \"help fsubclass\" for help.") return HM.DPrint("Waiting...") if options.nonrecursively: compareScript = ''' if (class_getSuperclass(cls) == inputClass){ NSString *name = [[NSString alloc] initWithUTF8String:class_getName(cls)]; [result appendFormat:@"%@\\n", name]; findCount += 1; } ''' else: compareScript = ''' for (Class superClass = class_getSuperclass(cls); superClass != nil; superClass = class_getSuperclass(superClass)) { if (superClass == inputClass) { NSString *name = [[NSString alloc] initWithUTF8String:class_getName(cls)]; [result appendFormat:@"%@\\n", name]; findCount += 1; break; } } ''' clsPrefixesValue = HM.getClassPrefixes()[1] command_script = f''' Class inputClass = objc_lookUpClass("{args[0]}"); if (inputClass == nil) {{ // Find prefixed class for (NSString *prefix in (NSMutableArray *){clsPrefixesValue.GetValue()}) {{ NSString *clsName = [prefix stringByAppendingString:@".{args[0]}"]; inputClass = objc_lookUpClass((char *)[clsName UTF8String]); if (inputClass) {{ break; }} }} }} NSMutableString *result = [[NSMutableString alloc] init]; if (inputClass == nil) {{ [result appendString:@"Can't find {args[0]} class\\n"]; }} else {{ unsigned int classCount; unsigned int findCount = 0; Class *classList = objc_copyClassList(&classCount); for (int i = 0; i < classCount; i++) {{ Class cls = classList[i]; {compareScript} }} if (findCount == 0) {{ [result insertString:@"No subclass found.\\n" atIndex:0]; }} else {{ [result insertString:[[NSString alloc] initWithFormat:@"Subclass count: %u \\n", findCount] atIndex:0]; }} free(classList); }} result; ''' classNames = HM.evaluateExpressionValue( command_script).GetObjectDescription() HM.DPrint(classNames)
def logRedirectResult(val: lldb.SBValue, stream: str) -> None: if val.GetValueAsSigned() == 0: HM.DPrint(f"redirect {stream} failed") else: HM.DPrint(f"redirect {stream} successful")
def push(debugger, command, exe_ctx, result, internal_dict): """ Syntax: push <className> push [--instance] <instance> Options: --instance/-i; Push the UIViewController instance. Examples: (lldb) push PersonalViewController (lldb) push -i [[PersonalViewController alloc] init] (lldb) expression -l objc -O -- [PersonalViewController new] <PersonalViewController: 0x7fed30c5a070> (lldb) push -i 0x7fed30c5a070 Notice: "push MyViewController" needs to execute "[[MyViewController alloc] init]" first. If the initializer of the class requires parameters, or the class needs to pass parameters after initialization, this command may cause errors. This command is implemented in HMPushViewController.py """ # HM.DPrint(type(command)) # <class 'str'> # HM.DPrint(type(exe_ctx)) # <class 'lldb.SBExecutionContext'> # HM.DPrint(type(result)) # <class 'lldb.SBCommandReturnObject'> # HM.DPrint(type(internal_dict)) # <class 'dict'> command_args = shlex.split(command) parser = generate_option_parser() try: # options: optparse.Values # args: list (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return if len(args) == 0: HM.DPrint("Error input, plase enter 'help push' for more infomation") return HM.DPrint("Waiting...") navigationVC = getNavigationVC() if navigationVC is None: HM.DPrint("Cannot find a UINavigationController") return state = False if options.instance: instanceExpr: str = "" for string in args: instanceExpr += string + " " instanceExpr = instanceExpr.rstrip() VCObject = HM.evaluateExpressionValue(instanceExpr).GetValue() else: makeVCExpression = f"(UIViewController *)[[NSClassFromString(@\"{args[0]}\") alloc] init]" VCObject = HM.evaluateExpressionValue(makeVCExpression).GetValue() # address if verifyObjIsKindOfClass(VCObject, "UIViewController"): pushExpression = f"(void)[{navigationVC} pushViewController:(id){VCObject} animated:YES]" debugger.HandleCommand('expression -l objc -O -- ' + pushExpression) state = True elif not options.instance: classPrefixes = HM.getClassPrefixes()[0] for prefix in classPrefixes: # for Swift file className = f"{prefix}.{args[0]}" if not HM.existClass(className): continue makeVCExpression = f"(UIViewController *)[[NSClassFromString(@\"{className}\") alloc] init]" VCObject = HM.evaluateExpressionValue(makeVCExpression).GetValue() # address if verifyObjIsKindOfClass(VCObject, "UIViewController"): pushExpression = f"(void)[{navigationVC} pushViewController:(id){VCObject} animated:YES]" debugger.HandleCommand('expression -l objc -O -- ' + pushExpression) state = True break HM.DPrint("push succeed" if state else "push failed") if state: HM.processContinue()
def findMethod(debugger, command, exe_ctx, result, internal_dict): """ Syntax: fmethod <methodName> (Case insensitive.) fmethod [--class] <className> Options: --class/-c; Find all method in the class Examples: (lldb) fmethod viewdid (lldb) fmethod viewDidLayoutSubviews (lldb) fmethod -c UITableViewController This command is implemented in HMClassInfoCommands.py """ command_args = shlex.split(command) parser = generate_findMethod_option_parser() try: # options: optparse.Values # args: list (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return if not options.cls: if len(args) != 1: HM.DPrint("Error input, Please enter \"help fmethod\" for help.") return elif len(args[0]) <= 5: HM.DPrint("Argument length must be greater than 5.") return HM.DPrint("Waiting...") if options.cls: clsPrefixesValue = HM.getClassPrefixes()[1] command_script = f''' NSMutableString *result = [[NSMutableString alloc] init]; Class inputClass = objc_lookUpClass("{options.cls}"); if (inputClass == nil) {{ // Find prefixed class for (NSString *prefix in (NSMutableArray *){clsPrefixesValue.GetValue()}) {{ NSString *clsName = [prefix stringByAppendingString:@".{options.cls}"]; inputClass = objc_lookUpClass((char *)[clsName UTF8String]); if (inputClass) {{ break; }} }} }} if (inputClass == nil) {{ [result appendString:@"Can't find {options.cls} class\\n"]; }} else {{ unsigned int instanceMethodCount; Method *instanceMethodList = class_copyMethodList(inputClass, &instanceMethodCount); for (int j = 0; j < instanceMethodCount; ++j) {{ Method method = instanceMethodList[j]; SEL sel = method_getName(method); NSString *selName = [[NSString alloc] initWithUTF8String:sel_getName(sel)]; [result appendFormat:@"(-) %@\\n\\tType encoding:%s\\n", selName, method_getTypeEncoding(method)]; }} free(instanceMethodList); unsigned int classMethodCount = 0; Class metaCls = object_getClass(inputClass); if (class_isMetaClass(metaCls)) {{ Method *classMethodList = class_copyMethodList(metaCls, &classMethodCount); for (int j = 0; j < classMethodCount; ++j) {{ Method method = classMethodList[j]; SEL sel = method_getName(method); NSString *selName = [[NSString alloc] initWithUTF8String:sel_getName(sel)]; [result appendFormat:@"(+) %@\\n\\tType encoding:%s\\n", selName, method_getTypeEncoding(method)]; }} free(classMethodList); }} if (instanceMethodCount + classMethodCount == 0) {{ [result insertString:@"No method found.\\n" atIndex:0]; }} else {{ [result insertString:[[NSString alloc] initWithFormat:@"Instance methods count: %u. Class method count: %u.\\n", instanceMethodCount, classMethodCount] atIndex:0]; NSString *clsName = [[NSString alloc] initWithUTF8String:class_getName(inputClass)]; [result insertString:[[NSString alloc] initWithFormat:@"Class: %@ (%p)\\n", clsName, inputClass] atIndex:0]; }} }} result; ''' else: inputMethodName = args[0].lower() command_script = f''' NSString *inputMethodName = [[NSString alloc] initWithUTF8String:"{inputMethodName}"]; NSMutableString *result = [[NSMutableString alloc] init]; unsigned int classCount; unsigned int findCount = 0; Class *classList = objc_copyClassList(&classCount); for (int i = 0; i < classCount; ++i) {{ Class cls = classList[i]; // Instance Methods unsigned int instanceMethodCount; Method *instanceMethodList = class_copyMethodList(cls, &instanceMethodCount); for (int j = 0; j < instanceMethodCount; ++j) {{ Method method = instanceMethodList[j]; SEL sel = method_getName(method); NSString *selName = [[NSString alloc] initWithUTF8String:sel_getName(sel)]; if ([[selName lowercaseString] containsString:inputMethodName]) {{ NSString *clsName = [[NSString alloc] initWithUTF8String:class_getName(cls)]; [result appendFormat:@"(-) %@\\n\\tType encoding:%s\\n\\tClass:%@\\n", selName, method_getTypeEncoding(method), clsName]; findCount += 1; }} }} free(instanceMethodList); // Class Methods Class metaCls = object_getClass(cls); if (!class_isMetaClass(metaCls)) {{ continue; }} unsigned int classMethodCount; Method *classMethodList = class_copyMethodList(metaCls, &classMethodCount); for (int j = 0; j < classMethodCount; ++j) {{ Method method = classMethodList[j]; SEL sel = method_getName(method); NSString *selName = [[NSString alloc] initWithUTF8String:sel_getName(sel)]; if ([[selName lowercaseString] containsString:inputMethodName]) {{ NSString *clsName = [[NSString alloc] initWithUTF8String:class_getName(cls)]; [result appendFormat:@"(+) %@\\n\\tType encoding:%s\\n\\tClass:%@\\n", selName, method_getTypeEncoding(method), clsName]; findCount += 1; }} }} free(classMethodList); }} if (findCount == 0) {{ [result insertString:@"No method found.\\n" atIndex:0]; }} else {{ [result insertString:[[NSString alloc] initWithFormat:@"Methods count: %u \\n", findCount] atIndex:0]; }} free(classList); result; ''' result = HM.evaluateExpressionValue(command_script).GetObjectDescription() HM.DPrint(result)
def deleteFile(debugger, command, exe_ctx, result, internal_dict): """ Syntax: deletefile [--option] deletefile [--file] <path> Options: --all/-a; Delete all file in the sandbox --documents/-d; Delete the "~/Documents" directory --library/-l; Delete the "~/Library" directory --tmp/-t; Delete the "~/tmp" directory --caches/-c; Delete the "~/Library/Caches" directory --preferences/-p; Delete the "~Library/Preferences" directory --file/-f; Delete the specified file or directory Examples: (lldb) deletefile -a (lldb) deletefile -c -p (lldb) deletefile -f path/to/fileOrDirectory This command is implemented in HMFileCommands.py """ command_args = shlex.split(command) parser = generate_DeleteFile_optionParser() try: # options: optparse.Values # args: list (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return hasOption = False if options.all: # Reserve the directory under the Home directory hasOption = True subFileArrayValue = HM.evaluateExpressionValue( "[[NSFileManager defaultManager] contentsOfDirectoryAtPath:(NSString *)NSHomeDirectory() error:nil]" ) for i in range(subFileArrayValue.GetNumChildren()): subFileValue = subFileArrayValue.GetChildAtIndex(i) HM.DPrint("=============" + subFileValue.GetObjectDescription() + "=============") subFilePathValue = HM.evaluateExpressionValue( f"[(NSString *)NSHomeDirectory() stringByAppendingPathComponent:(NSString *){subFileValue.GetValue()}]" ) deleteAllFileInDirectory(subFilePathValue.GetObjectDescription()) if options.documents: hasOption = True documentsDirectoryValue = HM.evaluateExpressionValue( "(NSString *)[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]" ) deleteAllFileInDirectory( documentsDirectoryValue.GetObjectDescription()) if options.library: hasOption = True libraryDirectoryValue = HM.evaluateExpressionValue( "(NSString *)[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject]" ) deleteAllFileInDirectory(libraryDirectoryValue.GetObjectDescription()) if options.tmp: hasOption = True tmpDirectoryValue = HM.evaluateExpressionValue( "(NSString *)NSTemporaryDirectory()") deleteAllFileInDirectory(tmpDirectoryValue.GetObjectDescription()) if options.caches: hasOption = True cachesDirectoryValue = HM.evaluateExpressionValue( "(NSString *)[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]" ) deleteAllFileInDirectory(cachesDirectoryValue.GetObjectDescription()) if options.preferences: hasOption = True libraryDirectoryValue = HM.evaluateExpressionValue( "(NSString *)[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject]" ) preferencesDirectoryValue = HM.evaluateExpressionValue( f"(NSString *)[(NSString *){libraryDirectoryValue.GetValue()} stringByAppendingPathComponent:@\"Preferences\"]" ) deleteAllFileInDirectory( preferencesDirectoryValue.GetObjectDescription()) if options.file: hasOption = True deleteFileOrDirectory(options.file) if not hasOption: HM.DPrint( "Requires at least one target file/directory, Please enter \"help deletefile\" for help." )
def register() -> None: if HM.existClass(gClassName): return HMProgressHUD.register() HMDebugWindow.register() HMDebugBaseViewController.register() # Register class HMProgressHUD.show(f"Register {gClassName}...") HM.DPrint(f"Register {gClassName}...") classValue = HM.allocateClass(gClassName, HMDebugBaseViewController.gClassName) HM.addIvar(classValue.GetValue(), "_previousKeyWindow", "UIWindow *") HM.addIvar(classValue.GetValue(), "_highlightView", "UIView *") HM.addIvar(classValue.GetValue(), "_targetView", "UIView *") HM.addIvar(classValue.GetValue(), "_exitBtn", "UIButton *") HM.addIvar(classValue.GetValue(), "_infoView", "UIView *") HM.addIvar(classValue.GetValue(), "_actionView", "UIButton *") HM.registerClass(classValue.GetValue()) HM.DPrint(f"Add methods to {gClassName}...") startIMPValue = makeStartIMP() if not HM.judgeSBValueHasValue(startIMPValue): HMProgressHUD.hide() return HM.addClassMethod(gClassName, "start", startIMPValue.GetValue(), "@@:") viewDidLoadIMPValue = makeViewDidLoadIMP() if not HM.judgeSBValueHasValue(viewDidLoadIMPValue): HMProgressHUD.hide() return HM.addInstanceMethod(gClassName, "viewDidLoad", viewDidLoadIMPValue.GetValue(), "v@:") viewDidLayoutSubviewsIMPValue = makeViewDidLayoutSubviewsIMP() if not HM.judgeSBValueHasValue(viewDidLayoutSubviewsIMPValue): HMProgressHUD.hide() return HM.addInstanceMethod(gClassName, "viewDidLayoutSubviews", viewDidLayoutSubviewsIMPValue.GetValue(), "v@:") # event HM.DPrint(f"Add methods to {gClassName}......") clickExitBtnIMPValue = makeClickExitBtnIMP() if not HM.judgeSBValueHasValue(clickExitBtnIMPValue): HMProgressHUD.hide() return HM.addInstanceMethod(gClassName, "clickExitBtn", clickExitBtnIMPValue.GetValue(), "v@:") clickCloseBtnIMPValue = makeClickCloseBtnIMP() if not HM.judgeSBValueHasValue(clickCloseBtnIMPValue): HMProgressHUD.hide() return HM.addInstanceMethod(gClassName, "clickCloseBtn", clickCloseBtnIMPValue.GetValue(), "v@:") handleTapRecognizerIMPValue = makeHandleTapRecognizerIMP() if not HM.judgeSBValueHasValue(handleTapRecognizerIMPValue): HMProgressHUD.hide() return HM.addInstanceMethod(gClassName, "handleTapRecognizer:", handleTapRecognizerIMPValue.GetValue(), "v@:@") findSubviewAtPointInViewIMPValue = makeFindSubviewAtPointInViewIMP() if not HM.judgeSBValueHasValue(findSubviewAtPointInViewIMPValue): HMProgressHUD.hide() return HM.addInstanceMethod(gClassName, "findSubviewAtPoint:inView:", findSubviewAtPointInViewIMPValue.GetValue(), "@@:{CGPoint=dd}@") refreshTargetViewIMPValue = makeRefreshTargetViewIMP() if not HM.judgeSBValueHasValue(refreshTargetViewIMPValue): HMProgressHUD.hide() return HM.addInstanceMethod(gClassName, "refreshTargetView:", refreshTargetViewIMPValue.GetValue(), "v@:@") getInfoArrayFromTargetViewIMPValue = makeGetInfoArrayFromTargetViewIMP() if not HM.judgeSBValueHasValue(getInfoArrayFromTargetViewIMPValue): HMProgressHUD.hide() return HM.addInstanceMethod(gClassName, "getInfoArrayFromTargetView:", getInfoArrayFromTargetViewIMPValue.GetValue(), "@@:@") # function action HM.DPrint(f"Add methods to {gClassName}.........") if not addFunctionMethods(): HMProgressHUD.hide() return HM.DPrint(f"Register {gClassName} done!") HMProgressHUD.hide()