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

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.py
The "sbr" python command has been installed and is ready for use.
(lldb)
输出安装成功,只需就收一个地址就可工作
subr 地址

(lldb) br delete
About to delete all breakpoints, do you want to do that?: [Y/n] y
All breakpoints removed. (1 breakpoint)
(lldb) subr 0x00000001000093dd
Breakpoint 2: where = Calculator`___lldb_unnamed_function161$$Calculator, address = 0x000000010cb033dd
(lldb)
对于经常使用的脚本,可以在lldb的初始化文件里添加命令加载脚本,启动自定义的命令
修改~/.lldbinit文件,在文件里加入一行

command script import ~/sbr.py
'''
import lldb
import commands
import optparse
import shlex
import 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 None

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
#得到最后断点的标号
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
def 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 breakpoint
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 
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.'
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值