zoukankan      html  css  js  c++  java
  • [原]调试没有符号的 iOS 应用

    说明:

      这里的调试是指使用 lldb 远程调试 iOS 应用

      设置断点是指在 ObjC 方法上设置断点

    使用场景:

      1、调试被 strip 了的 iOS 应用

      2、调试被 strip 了的 iOS 系统 dylib

    在调试时没有符号的 iOS 应用时,设置断点非常不方便:

      1、App:在没有开启 ASLR 时,需要首先找到方法的地址,然后针对地址设置断点

      2、Dylib:在没有开启 ASLR 时,需要找到dylib的基地址,然后计算偏移

    如果开启了 ASLR,设置断点会更麻烦。

    一直想解决这个问题,曾经想过的方法:

    首先,ObjC 语言是一个相对动态的语言,所以使用class-dump这样的工具,可以 dump 出类信息,函数地址。

    另外,DWARF 格式是有公开标准的,

    因此,可以通过将 class-dump 的输出信息转换成 DWARF,在调试时动态加载符号。

    这个方法我不是第一想到,这个帖子中有详细说明:http://stackoverflow.com/questions/17554070/import-class-dump-info-into-gdb

    但是照这个方法进行操作后,发现对 iOS 应用没效果,而且过程繁琐。

    后来想,ObjC是通过在C语言之上封装了薄薄的一层(消息特性)而形成的,

    所有 ObjC 的方法调用最终会转换为 C 方法调用,

    因此,可以通过在对应的 C 函数上设置断点来解决断点设置问题,

    而如何得到 C 函数的地址,就依赖于 ObjC 的运行时方法了,主要涉及:

      1、object_getClass

      2、NSSelectorFromString

      3、class_respondsToSelector

      4、class_getMethodImplementation

    在解决了在什么位置设置断点的问题后,

    接下来需要解决如果在 lldb 中方便的设置断点。

    lldb 集成了 Python 脚本引擎,参考:http://lldb.llvm.org/python-reference.html

    因此我们可以通过Python脚本扩展 lldb 的调试命令,主要用到如下几个函数:

      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

    脚本配置方法:

      方法一:在调试控制台执行:command script import bt_objc.py的文件路径

      方法二:将如上命令加入到 ~/.lldbinit,如果文件不存在则可以自己动手创建

    脚本内容:

      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"))" % class_name).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 0
    100 
    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         return
    111 
    112     arg = args[0]
    113     class_name = get_class_name(arg)
    114     method_name = get_method_name(arg)
    115 
    116     address = 0
    117     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"

    如上脚本也可以从这个链接下载:https://raw.github.com/Proteas/lldb-scripts/master/bt_objc.py

    脚本配置完毕后,可以通过如下命令设置断点:

    bt_objc "-[UIView initWithFrame:]"

  • 相关阅读:
    寻找大富翁
    C++ STL sort()函数用法
    众数
    平方因子
    Hdu 1089 A+B for Input-Output Practice (I)
    Hdu 1090 A+B for Input-Output Practice (II)
    Hdu 1083 Courses
    Hdu 1069 Monkey and Banana
    Hdu 1062 Text Reverse
    Hdu 1068 Girls and Boys
  • 原文地址:https://www.cnblogs.com/Proteas/p/3583264.html
Copyright © 2011-2022 走看看