LLDB && debugServer的简单使用
1.1 LLDB
<h4>LLDB全称为low level debugger,是xcode自带的动态调试工具,可用于c,c++,objective-c,全盘支持iOS,OSX以及iOS模拟器。
LLDB有以下四个功能:
1.在特定的情况下暂停程序
2.在特定的情况下启动程序
3.在程序停止的情况下检查程序内部
4.在程序停止的情况下改动程序,观察执行过程。
LLDB是运行在OSX程序中的,可以通过特定的语句配合debugServer对iOS中的程序进行监视。
1.2 debugServer
<h4>debugserver是运行在iOS程序中的,顾名思义,作为server端,它接受与来自服务端,也就是LLDB传送过来的命令,继而执行的操作。再把执行结果返回给LLDB,默认情况下, iOS并没有安装debugserver,只有在设备连接了一次xcode之后,并在window->device中添加此设备之后,debugservercai才会别安装到iOS中,默认位于Developer/usr/bin目录下。
但是默认的debugserver只用用户调试我们自己的app,如果想要调试iOS里面所有的app,还需要通过给debugserver赋予task_for_pid的权限。以下是配置task_for_pid的教程
1.debugserver瘦身,首先根据你的iOS设备确定你的手机对应的ARM,4s是armv7,5,5c是armv7s,5s以上的机型都是arm64,我的手机是5s,所以对应的arm是arm64,所以只从debugserver保留arm64的架构即可。具体步骤:
首先将未经处理的debugserver从iOS拷贝到OSX中,可以用scp命令,也可以用iFunBox。
然后利用命令
lipo -thin arm64(这里对应你的iOS设备的结构) (未经处理的debugServer的路径) -output (新的debugserver的保存路径)
2.给debugServer添加task_for_pid权限,将以下xml保存为ent.xml。
<plist version="1.0">
<dict>
<key>com.apple.springboard.debugapplications</key>
<true/>
<key>get-task-allow</key>
<true/>
<key>task_for_pid-allow</key>
<true/>
<key>run-unsigned-code</key>
<true/>
</dict>
</plist>
运行以下命令:
ldid -S(ent.xml的全路径) (debugServer的全路径)
以上命令实质上是通过ldid这个签名工具,为debugserver添加task_for_pid权限,上面的命令也可以用xcode自带的codesign实现。
codesign -s -f - --entitlements ent.plist -f debugserver
3.将添加权限后的debugserver放回去iOS复制到/usr/bin/debugserver,这样的好处是可以全局执行debugserver命令。另一个原因是因为在developer里面的是不可以写的,无法覆盖。
4.给新的debugserver添加可执行的权限
chmod +x /usr/bin/debugserver
1.3 debugserver使用
<h4>debugserver有两个命令。
1.启动进程,debugserver会启动进程,并且开启port端口,等待来自ip的LLDB接入。ip如果为*则代表任意ip
debugserver backend ip:port /path/to/excutable
2.附加进程,附加进程是在进程已打开的情况下,可以执行以下命令。
debugserver ip:port -a "processname"
1.4 LLDB && debugserver配合使用
<h5>
假设我们要调试在iOS中要按下home键的处理。通过IDA我们知道按下home键的时候调用了 -[SpringBoard _menuButtonDown:] 这个方法,现在我们的需求是在menuButtonDown的方法下面打一个断点。
(1)首先利用debugserver启动进程或者附加进程。系统的SpringBoard默认是启动的,所以不需要启动进程,使用附加进程命令即可。SSH到iOS中,然后附加到SpringBoard进程(以下命令开启了1234接口并且等待来自任意ip的LLDB接入)
Last login: Sun Apr 8 11:04:23 on ttys016
MacBookPro:~ lemon$ ssh root@192.168.2.154
lemons-iPhone5S:~ root# debugserver *:1234 -a "SpringBoard"
debugserver-@(#)PROGRAM:debugserver PROJECT:debugserver-340.3.124
for arm64.
Attaching to process SpringBoard...
Listening to port 1234 for a connection from *...
(2)在OSX的terminal中启动LLDB,然后连接远程1234端口
MacBookPro:~ lemon$ lldb
(lldb) process connect connect://192.168.2.154:1234
Process 2148 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame #0: 0x000000018208cfd8 libsystem_kernel.dylib`mach_msg_trap + 8
libsystem_kernel.dylib`mach_msg_trap:
-> 0x18208cfd8 <+8>: ret
libsystem_kernel.dylib`mach_msg_overwrite_trap:
0x18208cfdc <+0>: mov x16, #-0x20
0x18208cfe0 <+4>: svc #0x80
0x18208cfe4 <+8>: ret
Target 0: (SpringBoard) stopped.
(lldb)
(3)通过以上两步我们已经可以在电脑的终端利用LLDB调试APP了。现在在终端输入 image list -o -f 可以看到当前所有iOS中启动的所有的进程模块。
[(lldb) image list -o -f
[ 0] 0x0000000000040000 /Users/lemon/Library/Developer/Xcode/iOS DeviceSupport/9.3.1 (13E238)/Symbols/usr/lib/dyld
[ 1] 0x00000000000dc000 /System/Library/CoreServices/SpringBoard.app/SpringBoard(0x00000001000dc000)
[ 2] 0x0000000100988000 /Library/MobileSubstrate/MobileSubstrate.dylib(0x0000000100988000)
[ 3] 0x0000000001acc000 /Users/lemon/Library/Developer/Xcode/iOS DeviceSupport/9.3.1 (13E238)/Symbols/System/Library/PrivateFrameworks/StoreServices.framework/StoreServices
(4)通过3中的信息我们发现了Spring的偏移是0x00000000000dc000,接下来我们需要用ida查看menuButtonDown这个函数在模块中的地址是多少。
-[SpringBoard _menuButtonDown:]:
0000000100016fe8 stp x28, x27, [sp, #0xffffffa0]! ; Objective C Implementation defined at 0x1006a4550 (instance)
0000000100016fec stp x26, x25, [sp, #0x10]
0000000100016ff0 stp x24, x23, [sp, #0x20]
0000000100016ff4 stp x22, x21, [sp, #0x30]
0000000100016ff8 stp x20, x19, [sp, #0x40]
0000000100016ffc stp x29, x30, [sp, #0x50]
0000000100017000 add x29, sp, #0x50
0000000100017004 sub sp, sp, #0x60
通过上面的信息可以看到,该函数的第一条指令的地址是0000000100016fe8,所以该函数偏移后的地址是:原地址+偏移 =》0x00000000000dc000 + 0000000100016fe8 = 0x1000F2FE8
(5)在偏移后的地址出打断点
(lldb) br s -a 0x1000F2FE8
Breakpoint 1: where = SpringBoard`_mh_execute_header + 71804, address = 0x00000001000f2fe8
(lldb)
(6)在打完断点之后,可以按下home键,这个时候程序就会停在断点处,可以用1.5里面的命令进行调试等等
1.5 LLDB命令解析
<h5>
(1) b NSog // 在函数的起始位置设置断点
(2) br s -a address //在地址处设置断点
(3) br s -a 'address + offsetAddress' //在地址处设置断点
(4) br dis //禁用所有的断点
(5) br dis 6 //禁用序号为6的断点
(6) br del //删除所有断点
(7) br del 6 //删除序号为6的断点
(8) br en //启用所有断点
(9) br en 6 //启用序号为6的断点
(10)br com add 1 //当序号为1的断点执行的时候,可以执行预先设置的指令。当执行了这条指令之后,llDB会要求你输入一些指令,并且以DONE结束,当程序停在序号为1的断点时,就会执行这些预先设定的指令。
(11)p $R1 //打印R1的值
(12)p/x $sp //打印SP的地址,以16进制输出
(13)x/10 $sp //打印sp的地址,以10进制输出
(14)nexti 和 stepi //nexti不进入函数体,stepi进入函数体,nexti可以简写成ni,setpi可以简写成si。
(15)register write r0 1 //用于给指定的寄存器r0赋值为1从而达到对程序进行改动的目的。