编写一个watchdog.sh脚本_编写 LLDB 调试器脚本

LLDB 调试器提供对 Python 脚本的支持,可以执行一些自动化的操作,提供工作效率。本文从 HelloWorld 开始学习如何编写 LLDB 脚本。

编写第一个 LLDB 脚本

下面我们来实际操作编写 LLDB 调试器脚本。在计算机上建立一个目录用于存脚本文件,比如我们在 /Users/exchen/lldb 这个目录下操作,新建一个 HelloWorld.py 的文件,输入下面的代码:

def test(debugger, command, result, internal_dict):  """  This is my first lldb script  """  print "hello, world"def __lldb_init_module(debugger, internal_dict):  debugger.HandleCommand("command script add -f HelloWorld.test mycmd")

test 函数是我们准备要导出的功能,里面的 4 个参数是固定的,含义如下:

  • debugger 是当前调试器对象,类型是 lldb.SDBDebugger。

  • command 是命令的参数,类型是字符串。

  • result 是执行命令后返回的参数,类型是 lldb.SBCommandReturnObject

  • internal_dict 是当前脚本的所有变量和函数,类型是字典。

lldb_init_module 函数是在加载脚本时会执行的,command script add -f 是用于将 Python 代码导出成自定义的命令,其中 HelloWorld 是脚本的名称,这个要和脚本的文件名保持一致,test 是函数的名称,mycmd 是导出的命令名称。启动 LLDB 加载这一段脚本,输入 mycmd 命令就可以执行 test 函数里的代码。

执行 command script import 将脚本引入到调试器,输入 mycmd 命令可以看到打印出 hello, world,证明 test 函数的代码得到执行。输入 help mycmd 可以查看到相应的提示信息。

(lldb)command script import /Users/exchen/lldb/HelloWorld.py(lldb)mycmdhello, world(lldb)help mycmdSyntax:  This is my frist lldb script

了解 LLDB 配置文件

LLDB打开时会自动加载~/.lldbinit配置文件,通过这个配置文件可以设置加载的脚本路径等。上面我们编写了第一个脚本,在启动 LLDB 需要手动执行 command script import 才能引入脚本,如果将 command script import 添加到 ~/.lldbinit 每次启动 LLDB 时就会自动会引入脚本。默认这个配置文件是不存在的,我们手动创建一个,然后添加下面的命令,这样每次 LLDB 启动都会自动引入 HelloWorld.py。

command script import /Users/exchen/lldb/HelloWorld.py

除了引入自定义脚本,lldbinit配置文件还可以设置命令的别名、命令提示符文字等等,比如我们添加下面的信息:

setting set prompt "(exchen lldb)"command alias connect process connect connect://127.0.0.1:12345

此时再打开 LLDB 会发现命令行的提示文字显示的是 "(exchen lldb)",输入connect 命令别名就可以连接程序进行调试,省去了每次烦琐地输入一大串命令。

$ lldb(exchen lldb)connectProcess 5702 stopped* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP    frame #0: 0x00000002334200f4 libsystem_kernel.dylib`mach_msg_trap + 8libsystem_kernel.dylib`mach_msg_trap:->  0x2334200f4 8>: re

编写实际功能脚本

在上面我们已经编写了一个 HelloWorld 脚本,通过这个脚本我们了解到 LLDB 脚本的大概框架,但是这个脚本并没有任何实际功能,接下面我们要编写一个有实际功能的脚本,这个脚本的功能是导出三个命令,具体代码如下:

#!/usr/bin/env python# -*- coding: utf-8 -*-import lldbimport reimport osimport shlexdef goto_main(debugger, command, result, internal_dict):    """    goto main    """    interpreter = lldb.debugger.GetCommandInterpreter()    return_object = lldb.SBCommandReturnObject()     # 用来保存结果    interpreter.HandleCommand('dis', return_object)  # 执行dis命令    output = return_object.GetOutput(); #获取反汇编后的结果    br_index = output.rfind('br     x16') #查找最后的 bx x16    br_index = br_index - 20 #位置减去20    addr_index = output.index('0x', br_index) #查找0x开头的字符串    br_addr = output[br_index:br_index+11] #找到之后偏移11位    debugger.HandleCommand('b ' + br_addr) #添加断点    debugger.HandleCommand('continue') #运行    debugger.HandleCommand('si') #单步步入    def get_aslr():       interpreter = lldb.debugger.GetCommandInterpreter()    return_object = lldb.SBCommandReturnObject()    interpreter.HandleCommand('image list -o -f', return_object) #执行image list -o -f命令    output = return_object.GetOutput(); #获取命令的返回值    match = re.match(r'.+(0x[0-9a-fA-F]+)', output) #正则匹配(0x开头)    if match:        return match.group(1)    else:        return Nonedef aslr(debugger, command, result, internal_dict):    """    get ASLR offset    """    aslr = get_aslr()    print >>result, "ASLR offset is:", aslrdef breakpoint_address(debugger, command, result, internal_dict):    """    breakpoint aslr address    """    fileoff = shlex.split(command)[0] #获取输入的参数    if not fileoff:        print >>result, 'Please input the address!'        return    aslr = get_aslr()    if aslr:        #如果找到了ASLR偏移,就设置断点        debugger.HandleCommand('br set -a "%s+%s"' % (aslr, fileoff))    else:        print >>result, 'ASLR not found!'def __lldb_init_module(debugger, internal_dict):    debugger.HandleCommand('command script add -f myscript.aslr aslr')    debugger.HandleCommand('command script add -f myscript.goto_main gm')    debugger.HandleCommand('command script add -f myscript.breakpoint_address ba')

从上面的代码 lldb_init_module 可以看出导出了三个命令,第一个命令是 aslr,用于获取 ASLR 的地址,原理是调用 image list -f -o 命令,从返回的结果中匹配出括号中有 0x 开头的就是第一个模块的 ASLR 地址。

第二个命令是 gm,用于跳转到 main 函数的入口点,原理是 lldb 连接启动的进程时,第一条指令是在 dyld 里的 _dyld_start 函数,该函数最后一条指令是 br x16,会跳转到 main 函数,使用 dis 命令查看 _dyld_start 的反汇编代码,从结果中查找字符串 br x16 得到这条指令所在的地址,然后调用 b 命令添加断点,再调用 continue 命令让程序继续运行,断点在 br x16 处会触发,然后再执行 si 单步步入即可达到 main 函数,操作过程如下:

(exchen lldb)command script import ~/lldb/myscript.py(exchen lldb)connectProcess 5693 stopped* thread #1, stop reason = signal SIGSTOP    frame #0: 0x0000000100a3d000 dyld`_dyld_startdyld`_dyld_start:->  0x100a3d000 :  mov    x28, sp    0x100a3d004 :  and    sp, x28, #0xfffffffffffffff0    0x100a3d008 :  mov    x0, #0x0    0x100a3d00c : mov    x1, #0x0    0x100a3d010 : stp    x1, x0, [sp, #-0x10]!    0x100a3d014 : mov    x29, sp    0x100a3d018 : sub    sp, sp, #0x10             ; =0x10     0x100a3d01c : ldr    x0, [x28]Target 0: (AppStore) stopped.(exchen lldb)aslrASLR offset is: 0x0000000100360000(exchen lldb)gmBreakpoint 1: where = dyld`_dyld_start + 132, address = 0x0000000100a3d084Process 5693 resumingProcess 5693 stopped* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1    frame #0: 0x0000000100a3d084 dyld`_dyld_start + 132dyld`_dyld_start:->  0x100a3d084 : br     x16dyld`dyldbootstrap::start:    0x100a3d088 :   sub    sp, sp, #0x80             ; =0x80     0x100a3d08c :   stp    x28, x27, [sp, #0x20]    0x100a3d090 :   stp    x26, x25, [sp, #0x30]    0x100a3d094 :  stp    x24, x23, [sp, #0x40]    0x100a3d098 :  stp    x22, x21, [sp, #0x50]    0x100a3d09c :  stp    x20, x19, [sp, #0x60]    0x100a3d0a0 :  stp    x29, x30, [sp, #0x70]Target 0: (AppStore) stopped.Process 5693 stopped* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into    frame #0: 0x0000000100373ce0 AppStore`_mh_execute_header + 81120AppStore`_mh_execute_header:->  0x100373ce0 : stp    x22, x21, [sp, #-0x30]!    0x100373ce4 : stp    x20, x19, [sp, #0x10]    0x100373ce8 : stp    x29, x30, [sp, #0x20]    0x100373cec : add    x29, sp, #0x20            ; =0x20     0x100373cf0 : mov    x19, x1    0x100373cf4 : mov    x20, x0    0x100373cf8 : mov    x0, #0x0    0x100373cfc : bl     0x1004ca278               ; AppStore.__TEXT.__text + 1469760Target 0: (AppStore) stopped.

第三个命令是 ba,功能在调试器里给 IDA 里看到的地址添加断点,省去了每次从 IDA 里看到地址,需要手动加上 aslr 地址才能和调试器的地址对应上。

关于更多脚本编写的说明可以参考 LLDB 官网的文档 http://lldb.llvm.org/use/python-reference.html

如有疑问,请点击

推荐文章:

iOS安全论坛软件源

体验 iPhone 安装 Android 系统

【Frida 实战】远程过程调用(RPC)

想了解最新的iOS安全资讯、技术干货请关注 iOS安全论坛(ioshacker.net)微信公众号

2b8b254811672b6403d2a43b19ea8a15.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值