在使用class-dump、hopper/ida等工具静态分析程序之后,很可能要定位出关键函数在哪里(进而反汇编或注入这段代码),而动态调试程序是定位关键代码的有效辅助办法。
(一)目标
在mac机上远程调试手机上的进程。
(二)问题
(1)为什么不用本地调试的方式?
可以在手机上安装gdb,再使用gdb调试目标进程。但因为本地用gdb来调试可能会遇到很多gdb本身的问题(有一些可以解决,而有一些并不好解决;后续可以介绍这方面的内容),而lldb被苹果支持,并用于替代gdb。所以,可考虑使用更好的工具,即lldb+debugserver来远程调试目标进程。
(2)需要什么工具?
mac上需要lldb,有安装xcode便有lldb。
手机上需要安装debugserver。
(3)手机如何安装debugserver?
debugserver在哪里?
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/xx.xx(目标手机是什么系统就选择什么版本的目录),双击下面的文件:DeveloperDiskImage.dmg,在打开的finder中,usr/bin目录中存放debugserver。
怎么定制手机上相应的debugserver?
(a)
双击打开DeveloperDiskImage.dmg后,
cd /Volumes/DeveloperDiskImage/Library/PrivateFrameworks,
scp -r ARMDisassembler.framework root@192.168.2.22:/System/Library/PrivateFrameworks/,
把ARMDisassembler.framework拷贝到手机。之所以这样做,是据别人的经验,可以让lldb看汇编代码的效果更好,不妨照做。
(b)
把usr/bin中的debugserver拷贝到另一个目录,为后续的签名作准备。
(c)
创建一个entitlement.xml文件,内容如下:
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<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>
(d)
使用ldid来签名:
ldid -Sentitlement.xml debugserver
拷贝到手机:
scp debugserver root@192.168.2.22:/usr/bin/
在手机上测试是否可用:
debugserver
(4)重签名工具ldid怎么安装?
如果有安装iOSOpenDev,那在目录/opt/iOSOpenDev/bin/下面就有ldid。
如果没有,那可以拉代码下来安装:
git clone git://git.saurik.com/ldid.git
cd ldid
git submodule update --init
./make.sh
which ldid #查看是否安装成功
(5)怎么使用debugserver与lldb?
在手机上启动服务器debugserver:
debugserver *:54321 -a "Spotify"
*表示侦听任意ip的连接,54321为端口,-a连接目标进程的名称(可用ps取得)。
然后,在mac上启动lldb并连接服务器:
process connect connect://192.168.2.22:54321。
如果在手机端有看到:Waiting for debugger instructions for process 0,那说明已经连接上。
之后可以在lldb端发送命令,比如查看当前调试进程的模块信息:
image list -o -f
注意,第一次image list -o -f,可能要等上几分钟。当有返回后,再输入c,就可以让程序继续运行。
(6)如果觉得debugserver运行很慢,怎么解决?
使用usbmuxd来启用数据线来调试。
* 如何安装与使用usbmuxd?
* 下载:http://7xibfi.com1.z0.glb.clouddn.com/uploads/default/original/2X/a/aa9cecf05b47d08a59324edeaaeea3f17e0608ee.zip
* 具体可以参考:http://www.jianshu.com/p/9333a706641a。
我直接用lldb跟debugserver,就第一次image list等几分钟,整体还可以接受。
(7)怎么调试?
* debugserver *:54321 -a "Spotify" 手机上启动debugserver并附加到目标进程
* lldb mac上启动lldb
* process connect connect://192.168.2.22:54321 lldb连接debugserver
* image list -o -f 查看目标进程的所有模块的信息,比如:
[ 0] 0x000d8000 /var/containers/Bundle/Application/A80AF35B-DAD3-4D7E-B467-6C4230E32556/Spotify.app/Spotify(0x00000000000dc000)
第二个值为程序在内存中的基地址,记录这个值。
* br s -a (0x000d8000+目标代码的偏移地址)
* c 让程序继续执行
* (操作程序)让程序触发断点。
* 使用lldb命令进行调试。
(三)示例
这里简单演示下怎么调试Spotify这个应用。
从class-dump得到的信息,尝试调试类SPTNowPlayingBarModel的pause函数,看看在播放bar条上暂停播放时会不会断在这个函数上。
class-dump得到的pause的地址是0x00477ff1,在hopper上跳转到这个地址(G),也可以看到-[SPTNowPlayingBarModel pause]的信息。
* 运行目标应用
* debugserver *:54321 -a "Spotify" 手机端启动服务器,之后都在客户端lldb中操作。
* lldb
* process connect connect://192.168.2.22:54321
* image list -o -f 或 image list -o -f | grep Spotify
这一步可能要等一会,在ipod5的6.0系统会很快,而在iphone5s的10.1.1系统则很慢。
* 从上面的命令找到基地址为:0x000e6000 (每次运行不一样)
* 查看一下即将下断点的代码是怎么样的:dis -a '0x000e6000+0x00477ff1' 结果跟用hopper看到一样(0x0047ff1处)。
* br s -a '0x000e6000+0x00477ff1'
* c
* 让程序触发到断点,使用lldb的命令查看数据或设置值,或单步等。
(四)lldb的常用命令
打印信息:
expr或e、po、p或print
display 表达式
undisplay 序号
断点:
br l或breakpoint list #所有断点
breakpoint set -a 函数地址,可简写:br s -a xxx #下断点
br s --函数关键字(可模糊)
breakpoint set --shlib foo.dylib --name foo
br del 1 #删除断点
br del 2 3 4 #删除几个断点
内存断点
watchpoint set expression 地址
watchpoint set variable 变量名称
条件断点
watchpoint modify -c 表达式
单步:
s/si #单步进入函数
n/ni #单步
f #跳出函数
线程:
thread backtrace,或bt,或bt all #列出所有线程
模块信息:
image list [-o -f]
image lookup -a expression
image lookup -a $pc
image lookup -r -n xxx
image lookup -r -n playPause #用来查看一个函数(关键字)的地址
image lookup -r -s
显示地址处的代码:
disassemble/dis -a 地址
dis -a $pc
dis -s 0x0002c000 -c 9 #后面的参数-c用来限制显示的代码数。
内存操作:
memory read [起始地址 结束地址]/寄存器 -outfile 输出路径
寄存器操作:
register read/格式
register write 寄存器名称 数值
https://mp.weixin.qq.com/s/rZRqZxefbuVipMlz4DsZXA