Note:
Debugging here refers to using lldb to remotely debug iOS applications
Setting a breakpoint refers to setting a breakpoint on the ObjC method.
Use Cases:
1. debug the iOS app that has been strip
2. debug dylib of the iOS system that has been strip
When debugging iOS apps without symbols, it is inconvenient to set breakpoints:
1. App: When ASLR is not enabled, you must first find the method address and set a breakpoint for the address.
2. Dylib: When ASLR is not enabled, find the base address of dylib and calculate the offset.
If ASLR is enabled, it is more troublesome to set breakpoints.
I have been trying to solve this problem. I have thought about the following methods:
First, the ObjC language is a relatively dynamic language, so you can dump the class information and function address by using a tool such as class-dump.
In addition, the DWARF format is open standard,
Therefore, you can convert the output information of class-dump to DWARF to dynamically load symbols during debugging.
This method I am not the first thought of, this post has a detailed description: http://stackoverflow.com/questions/17554070/import-class-dump-info-into-gdb
However, after performing operations based on this method, it is found that the iOS application is ineffective and the process is cumbersome.
Later I thought that ObjC was formed by encapsulating a thin layer (Message characteristics) on the C language,
All ObjC method calls are eventually converted to C method calls,
Therefore, you can set breakpoints on the corresponding C function to solve the breakpoint setting problem,
How to obtain the address of the C function depends on the runtime method of ObjC, mainly involving:
1. object_getClass
2. NSSelectorFromString
3. class_respondsToSelector
4. class_getMethodImplementation
After resolving the problem of where to set the breakpoint,
Next, we need to solve the problem of easily setting breakpoints in lldb.
Lldb integrates the Python script engine, see: http://lldb.llvm.org/python-reference.html
Therefore, we can use the Python script to extend the lldb debugging command, mainly using the following functions:
1. lldb. debugger
2. lldb. debugger. GetSelectedTarget ()
3. lldb. debugger. GetSelectedTarget (). GetProcess ()
4. lldb. debugger. GetSelectedTarget (). GetProcess (). GetSelectedThread ()
5. lldb. debugger. GetSelectedTarget (). GetProcess (). GetSelectedThread (). GetSelectedFrame ()
6. lldb. frame. EvaluateExpression
7. lldb. debugger. HandleCommand
Script configuration method:
Method 1: Run command script import bt_objc.py In the debugging console.
Method 2: add the above command ~ /. Lldbinit. If the file does not exist, you can create it by yourself.
Script content:
1 #!/usr/bin/python 2 3 ''' 4 Author: 5 Proteas 6 Date: 7 2014-03-05 8 Purpose: 9 set breakpoint without symbols, for examle: stripped macho 10 Usage: 11 add the following line to ~/.lldbinit 12 command script import ~/.lldb/bt_objc.py 13 ''' 14 15 import lldb 16 import commands 17 import shlex 18 import optparse 19 import re 20 21 def __lldb_init_module (debugger, dict): 22 debugger.HandleCommand('command script add -f bt_objc.bt_objc bt_objc') 23 print 'The "bt_objc" command has been installed' 24 25 def create_command_arguments(command): 26 return shlex.split(command) 27 28 def is_command_valid(args): 29 "" 30 if len(args) == 0: 31 return False 32 33 arg = args[0] 34 if len(arg) == 0: 35 return False 36 37 ret = re.match('^[+-]\[.+ .+\]$', arg) # TODO: more strict 38 if not ret: 39 return False 40 41 return True 42 43 def get_class_name(arg): 44 match = re.search('(?<=\[)[^\[].*[^ ](?= +)', arg) # TODO: more strict 45 if match: 46 return match.group(0) 47 else: 48 return None 49 50 def get_method_name(arg): 51 match = re.search('(?<= )[^ ].*[^\]](?=\]+)', arg) # TODO: more strict 52 if match: 53 return match.group(0) 54 else: 55 return None 56 57 def is_class_method(arg): 58 if len(arg) == 0: 59 return False 60 61 if arg[0] == '+': 62 return True 63 else: 64 return False 65 66 def get_selected_frame(): 67 debugger = lldb.debugger 68 target = debugger.GetSelectedTarget() 69 process = target.GetProcess() 70 thread = process.GetSelectedThread() 71 frame = thread.GetSelectedFrame() 72 73 return frame 74 75 def get_class_method_address(class_name, method_name): 76 frame = get_selected_frame(); 77 class_addr = frame.EvaluateExpression("(Class)object_getClass((Class)NSClassFromString(@\"%s\"))" % "FigPluginView").GetValueAsUnsigned() 78 if class_addr == 0: 79 return 0 80 81 sel_addr = frame.EvaluateExpression("(SEL)NSSelectorFromString(@\"%s\")" % method_name).GetValueAsUnsigned() 82 has_method = frame.EvaluateExpression("(BOOL)class_respondsToSelector(%d, %d)" % (class_addr, sel_addr)).GetValueAsUnsigned() 83 if not has_method: 84 return 0 85 86 method_addr = frame.EvaluateExpression('(void *)class_getMethodImplementation(%d, %d)' % (class_addr, sel_addr)) 87 88 return method_addr.GetValueAsUnsigned() 89 90 def get_instance_method_address(class_name, method_name): 91 frame = get_selected_frame(); 92 class_addr = frame.EvaluateExpression("(Class)NSClassFromString(@\"%s\")" % class_name).GetValueAsUnsigned() 93 if class_addr == 0: 94 return 0 95 96 sel_addr = frame.EvaluateExpression("(SEL)NSSelectorFromString(@\"%s\")" % method_name).GetValueAsUnsigned() 97 has_method = frame.EvaluateExpression("(BOOL)class_respondsToSelector(%d, %d)" % (class_addr, sel_addr)).GetValueAsUnsigned() 98 if not has_method: 99 return 0100 101 method_addr = frame.EvaluateExpression('(void *)class_getMethodImplementation(%d, %d)' % (class_addr, sel_addr))102 103 return method_addr.GetValueAsUnsigned()104 105 def bt_objc(debugger, command, result, dict):106 args = create_command_arguments(command)107 108 if not is_command_valid(args):109 print 'please specify the param, for example: "-[UIView initWithFrame:]"'110 return111 112 arg = args[0]113 class_name = get_class_name(arg)114 method_name = get_method_name(arg)115 116 address = 0117 if is_class_method(arg):118 address = get_class_method_address(class_name, method_name)119 else:120 address = get_instance_method_address(class_name, method_name)121 122 if address:123 lldb.debugger.HandleCommand ('breakpoint set --address %x' % address)124 else:125 print "fail, please check the arguments"
The above script can also be downloaded from this link: https://raw.github.com/Proteas/lldb-scripts/master/bt_objc.py
After the script is configured, run the following command to set the breakpoint:
Bt_objc "-[UIView initWithFrame:]"