LLDB(Low Level Debugger),如果说IDA是倚天剑,那么LLDB就是屠龙刀两者在逆向工程中的地位不相上下,难分伯仲。内置于xCode中的调试工具,通吃c,c++,object-c,全盘支持osx、ios,及ios模拟器;
功能可以概括为4点:
1、在指定的条件下启动程序
2、在指定的条件下停止程序
3、在程序停止的时候检查程序内部发生的事;
4、在程序停止的时候对程序进行改动,观察程序的执行过程有什么变化。
LLDB是运行在OSX中运行的,要想调试iOS,还需要另一个工具debugserver的配合。
debugserver运行在iOS上,作为服务端,实际执行LLDB(作为客户端)传过来的命令,再把执行结果反给LLDB,显示给用户,即所谓的“远程调试”。在默认情况下,iOS上并没有安装debugserver,只有在iOS设备连接过一次xCode,并在window-Devices菜单中添加此设备后,debugserver才会被Xcode安装到iOS的“/Developer/usr/bin/”目录下。
ios逆向工具--LLDB+debugServerX
但是,因为缺少task_for_pid权限,通过Xcode安装的debugserver只能调试我们自己的app,要想能够调试其他人的app,还需要进行配置;
先用scp命令或pp助手之类的工具将手机中的debugserver拷到mac上来;然后用lipo命令,结其瘦身:(ps:要确认手机对应的指令集iphone5,5c为armv7s,iphone5s,6..为arm64),比如我的是iphone5s,故只需要保留arm64即可:
ios逆向工具--LLDB+debugServer
可以看到lipo命令瘦身之后的debgugserver由原来的13M多变成4、5M的大小。
ios逆向工具--LLDB+debugServer
用file命令,也可查看到,未瘦身之前的debugserver包含armv7、armv7s和arm64,而lipo之后的只有arm64。
接下来给debugserver添加task_for_pid权限,下载http://iosre.com/ent.xml至与瘦身后的degserver同一目录下;然后ldid -Sent.xml debugserver执行添加权限:(注意:-S与ent.xml中间无空格)
ios逆向工具--LLDB+debugServer
然后将处理后的debugserver拷回到ios的/usr/bin/目录下,然后ssh到ios,给debugserver添加使用权限:
ios逆向工具--LLDB+debugServer
P.S. 为什么不把debugserver拷回原来的/Developer/usr/bin/目录,而是/usr/bin/目录下呢?因为一是原版的debugserver是不可写的,无法替换覆盖;二是放在/usr/bin/目录下的命令,无须输入全路径即可执行,即在ios任意目录下都可以执行debugserver命令。
debugserver最常用的2种场景,就是启动和附加进程,命令分别是:
debugserver -x backboard IP:port /paht/to/excutable
此种方式,debugserver会启动excutable,并开启port端口,等待来自IP的LLDB接入;
debgugserver IP:port -a 'processName'
此种方式,debugserver会附加processName进程,并开启port端口,等待来自IP的LLDB接入。
eg:
在ios上用debugserver打开MobileSMS并启动调试(*:1234表示允许任意IP通过1234端口进行lldb连接调试)
ios逆向工具--LLDB+debugServer
在mac上启动lldb连接,连接成功后即可行远程调试:
ios逆向工具--LLDB+debugServer
ps:连接后,进程将处理暂停状态,输入c即可进入运行调试状态。ctrl+D中止调试。
在ios上用debugserver附加到已经打开的MobileSMS上,进行调试:
ios逆向工具--LLDB+debugServer
mac上lldb连接同上。
至此即可使用lldb进行愉快的动态调试了!
附lldb常用命令:
imagelist -o -f //查看ASLR偏移
b function //函数的起始位置设置断点,如b NSlog
br s -a 0xAddress //在计算出的偏移后的地址上设置断点
br s -a 'Addr1+Addr2' //直接用ASLR+偏移前地址进行断点
br s del //删除所有断点
br s del index //删除指定断点
br dis //禁用所有断点
br dis index //禁用指定断点
br en //启用断点
br en index //启用指定断点
br com add index //在指定断点上,预先设置指令,在触发后得到执行
eg:
(lldb) br com add 1
Enter your debugger command(s). Type 'DONE' to end.
>po [$r0 class]
>p (char *)$r1
>c
>DONE
//br com add命令一般用于自动观察某个断点被触发时其上下文的变化,找到进一步分析的线索
p (char *)$r1
//通过强制转换的方式打印了C语言基本数据类型对象;
//oc里所有的对象都是用指针表示的,p打印出来的是对象的指针,而不是对象的本身,要打印对象本身则可以用po命令(print object)
po $r1
p/x $sp //16进制方式打印sp的值(一个指针)
x/10 $sp //打印这个指针指向的连续10个字(word)的数据
p/x $lr //在方法开始处断点,输入此命令可以查找调用此方法的上一个方法,即该方法的调用者;还可以直接在方法的结束处断开,然后ni执行,会回到上一级调用者的方法里;
c 继续执行至下一个断点
ni 单步执行
si 进入函数体
register write //用于指定的寄存器赋值,从而对程序进行改动,观察程序的执行过程有啥变化;
eg:register write r0 1 //将r0的值改成1,使下面的判断执行到另一分支
1)lldb调试的二进制文件必须从iOS中提取,IDA分析的二进制文件必须与LLDB调试的二进制文件为同一个。二进制文件可以通过dyld_decache工具从iOS上获取。
2)LLDB中,如果要重复执行上一个调试指令,直接回车即可;上下方向键可以查看之前执行过的调试指令。
lldb中下内存读写断点断点:
(lldb) help watchpoint set
The following subcommands are supported:
哈哈试出来了:
(lldb) wa s e -w r -- 0x27d61114
Watchpoint created: Watchpoint 5: addr = 0x27d61114 size = 4 state = enabled type = r
new value: 391767744
(lldb) wa s e -w w -- 0x27d61114
Watchpoint created: Watchpoint 6: addr = 0x27d61114 size = 4 state = enabled type = w
new value: 391767744
现有一int型地址0x12345678,内容初始值为0。我想用LLDB watchpoint捕获这个地址内容变更(条件内存写断点),请问前辈们如何写这个命令。
re:
watchpoint set expressiom(最后一个m改成n,操蛋的博客输入expressio(n)时判断为非法字符) -w write -s 4 -- 0x12345678
watchpoint set expressiom -w write -s 8 -- 0x12345678