lldb python 脚本扩展之超级断点增强版

来源:互联网 发布:python可以开发游戏吗 编辑:程序博客网 时间:2024/05/17 01:40

lldb python 脚本扩展之超级断点增强版

引言

不知道各位在逆向ios用到lldb的时候是啥感觉,反正我是服了。没界面到算了,各种跳转和断点用起来不知道有多麻烦。所以我尽量用静态分析。不过越往后,越发现,光静态实在是不行。

  1. ios不知道有没有动态解密一说,就是把mach-o加密的部分在加载的时候再解密。android和windows上有一种方式叫做dump内存,可以把加载后解密的部分保存下来,但我在ios上用lldb memory read dump的时候发现限制了1024字节,后来又想把一堆1024字节拼起来,有发现不是所有的地址都能读。于是乎,我怀疑,ios内存读写存在限制。不知是否?有明白的希望留言告知。
  2. 网上有个python lldb的超级断点的脚本,本工具经该工具改编而来,原作者不详,大家可以查下。
  3. ios和window汇编上有个说法上的不同,模块的地址ios上叫ASLR偏移,而windows上叫基地址。我们反编译工具里的地址,ios叫做基地址,windows上叫偏移offset.好像正好相反,文中按照ios本来的叫法说明。

lldb 断点之困

lldb下断点之前都有一个偏移查找的过程,“image list -o -f”。其实其他平台也类是,超级断点这个工具的核心就是把模块偏移寻找的过程自动化,让我们只需要输入基地址就可以在目标地址下断点的脚本。当然这一切的前提是,lldb预留了python的接口,否则就悲催了。
简单介绍下代码:

def get_ASLR():    # 获取‘image list -o’命令的返回结果    interpreter = lldb.debugger.GetCommandInterpreter()    returnObject = lldb.SBCommandReturnObject()    interpreter.HandleCommand('image list -o', returnObject)    output = returnObject.GetOutput();    # 正则匹配出第一个0x开头的16进制地址    match = re.search(r'(1\]\s)(0x[0-9a-fA-F]+)', output)    if match:        return match.group(2)    else:        return None

代码很简单,通过接口执行lldb命令得到返回的数据,正则匹配出用户模块偏移量。

def sbr(debugger, command, result, internal_dict):    #用户是否输入了地址参数    if not command:        print >>result, 'Please input the address!'        return    ASLR = get_ASLR()    if ASLR:        #如果找到了ASLR偏移,就设置断点        debugger.HandleCommand('br set -a "%s+%s"' % (ASLR, command))    else:        print >>result, 'ASLR not found!'# And the initialization code to add your commands 

同样利用接口把我们输入的基地址和偏移传入lldb执行命令。

用户态代码的锁定

这里到了我胡编乱造的时候,我在调试ios的发现,object-c反汇编后的代码有一个很大的特点,就是喜欢在汇编代码块里乱跳。可能是更object-c和arm的运行机制有关,导致我们的代码在用户模块里的过程很难捕捉到,一句话,单步走功能就是个坑。
为了解决这个问题,我在以上脚本的基础上,利用断点功能实现了两大扩展。
1.单步执行在本模块,坚决不跳到其他模块的单步走
2.多条指令一键式执行,只要输入执行指令数,一条命令立刻执行到位。同样执行过程锁定在同一个模块。
(你要想去其他模块,本身的命令就够了)

正真意义上的单步执行

首先,我们要获得当前指令的地址,很简单,“register read/x pc”
同样我们正则匹配出地址字符串返回。

def get_pc():    # 获取‘image list -o’命令的返回结果    interpreter = lldb.debugger.GetCommandInterpreter()    returnObject = lldb.SBCommandReturnObject()    interpreter.HandleCommand('register read/x pc', returnObject)    output = returnObject.GetOutput();    # 正则匹配出第一个0x开头的16进制地址    match = re.match(r'.+(0x[0-9a-fA-F]+)', output)    if match:        return match.group(1)    else:        return None

然后在下一条指令的本模块下一条地址的地方下断点,并执行到断点

 ADDRESS = get_pc()    #增加断点    if not command:        if ADDRESS:            debugger.HandleCommand('br set -a "%s+4"' % (ADDRESS))            print 'br set success:"%s+4"'%ADDRESS        #执行到断点            debugger.HandleCommand('c')            NUM = getbrlistn()

执行完毕删除断点

    NUM = getbrlistn()        #删除断点            if NUM:                debugger.HandleCommand('br dele %s' % (NUM))                print "br dele success:%s"%NUM            else:                print >>result, 'br list number not found!'        else:

这里还有个获取上一步断点并删除的操作。同样正则。

#得到最后断点的标号def getbrlistn():    interpreter = lldb.debugger.GetCommandInterpreter()    returnObject = lldb.SBCommandReturnObject()    interpreter.HandleCommand('br list', returnObject)    output = returnObject.GetOutput();    # 正则匹配出所有的断点代号    match = re.findall(r'(^\d{1,4})(:\saddress)', output,re.M)    if match:        num=len(match)        strlast=match[num-1]        brnum=strlast[0]        #print brnum        return brnum    else:        return None# Super breakpoint

单步基础上的多步执行

基本和单步执行的代码一样,不同之处在于我们需要传入执行步数,command.并转换到响应的地址下断点。

offby=int(command,16)*4        if ADDRESS:            debugger.HandleCommand('br set -a "%s+%x"' % (ADDRESS,offby))            print 'br set success:"%s+%x"'%(ADDRESS,offby)            #执行到断点            debugger.HandleCommand('c')

然后就是执行到断点和删除断点。

获得静态编译的地址

同样的,我们执行到某个位置后想在ida里静态查看下相应的代码,也给他自动化一下。

def idaposi(debugger, command, result, internal_dict):    ASLR=get_ASLR()    PCADD=get_pc()    print "ASLR:",ASLR    print "trueADDR:",PCADD    if (ASLR!=None)&(PCADD!=None):        #print ASLR        #print PCADD        addr=int(PCADD,16)-int(ASLR,16)        print "idaposition_address:ox%x"%addr    else:        print "get info error"  

这下齐全了。各位可以自己敲一下,把代码改成这样就能用了。
完整代码在下面。

#!/usr/bin/python#coding:utf-8'''执行以下脚本导入超级断点工具(lldb) command script import ~/workspace/3-lldb/subr.pyThe "sbr" python command has been installed and is ready for use.(lldb)输出安装成功,只需就收一个地址就可工作subr 地址(lldb) br deleteAbout to delete all breakpoints, do you want to do that?: [Y/n] yAll breakpoints removed. (1 breakpoint)(lldb) subr 0x00000001000093ddBreakpoint 2: where = Calculator`___lldb_unnamed_function161$$Calculator, address = 0x000000010cb033dd(lldb)对于经常使用的脚本,可以在lldb的初始化文件里添加命令加载脚本,启动自定义的命令修改~/.lldbinit文件,在文件里加入一行command script import ~/sbr.py'''import lldbimport commandsimport optparseimport shleximport re# 获取ASLR的偏移地址def get_ASLR():    # 获取‘image list -o’命令的返回结果    interpreter = lldb.debugger.GetCommandInterpreter()    returnObject = lldb.SBCommandReturnObject()    interpreter.HandleCommand('image list -o', returnObject)    output = returnObject.GetOutput();    # 正则匹配出第一个0x开头的16进制地址    match = re.search(r'(1\]\s)(0x[0-9a-fA-F]+)', output)    if match:        return match.group(2)    else:        return Nonedef get_pc():    # 获取‘image list -o’命令的返回结果    interpreter = lldb.debugger.GetCommandInterpreter()    returnObject = lldb.SBCommandReturnObject()    interpreter.HandleCommand('register read/x pc', returnObject)    output = returnObject.GetOutput();    # 正则匹配出第一个0x开头的16进制地址    match = re.match(r'.+(0x[0-9a-fA-F]+)', output)    if match:        return match.group(1)    else:        return None#得到最后断点的标号def getbrlistn():    interpreter = lldb.debugger.GetCommandInterpreter()    returnObject = lldb.SBCommandReturnObject()    interpreter.HandleCommand('br list', returnObject)    output = returnObject.GetOutput();    # 正则匹配出所有的断点代号    match = re.findall(r'(^\d{1,4})(:\saddress)', output,re.M)    if match:        num=len(match)        strlast=match[num-1]        brnum=strlast[0]        #print brnum        return brnum    else:        return None# Super breakpointdef sni(debugger, command, result, internal_dict):    ADDRESS = get_pc()    #增加断点    if not command:        if ADDRESS:            debugger.HandleCommand('br set -a "%s+4"' % (ADDRESS))            print 'br set success:"%s+4"'%ADDRESS        #执行到断点            debugger.HandleCommand('c')            NUM = getbrlistn()        #删除断点            if NUM:                debugger.HandleCommand('br dele %s' % (NUM))                print "br dele success:%s"%NUM            else:                print >>result, 'br list number not found!'        else:            print >>result, 'ADDRESS not found!'    else:        offby=int(command,16)*4        if ADDRESS:            debugger.HandleCommand('br set -a "%s+%x"' % (ADDRESS,offby))            print 'br set success:"%s+%x"'%(ADDRESS,offby)            #执行到断点            debugger.HandleCommand('c')            NUM = getbrlistn()            #删除断点            if NUM:                debugger.HandleCommand('br dele %s' % (NUM))                print "br dele success:%s"%NUM            else:                print >>result, 'br list number not found!'        else:            print >>result, 'ADDRESS not found!'def idaposi(debugger, command, result, internal_dict):    ASLR=get_ASLR()    PCADD=get_pc()    print "ASLR:",ASLR    print "trueADDR:",PCADD    if (ASLR!=None)&(PCADD!=None):        #print ASLR        #print PCADD        addr=int(PCADD,16)-int(ASLR,16)        print "idaposition_address:ox%x"%addr    else:        print "get info error"  def sni2(debugger, command, result, internal_dict):    NUM = getbrlistn()    #删除断点    if NUM:        #print "%d"%NUM        debugger.HandleCommand('br dele %s' % (NUM))        print NUM    else:        print >>result, 'br list number not found!'# And the initialization code to add your commands # Super breakpointdef sbr(debugger, command, result, internal_dict):    #用户是否输入了地址参数    if not command:        print >>result, 'Please input the address!'        return    ASLR = get_ASLR()    if ASLR:        #如果找到了ASLR偏移,就设置断点        debugger.HandleCommand('br set -a "%s+%s"' % (ASLR, command))    else:        print >>result, 'ASLR not found!'# And the initialization code to add your commands def __lldb_init_module(debugger, internal_dict):    # 'command script add sbr' : 给lldb增加一个'sbr'命令    # '-f sbr.sbr' : ¸该命令调用了sbr文件的sbr    debugger.HandleCommand('command script add subr -f subr.sbr')    debugger.HandleCommand('command script add sni -f subr.sni')    debugger.HandleCommand('command script add idap -f subr.idaposi')    print 'The "sbr" python command has been installed and is ready for use.'
原创粉丝点击