逆向破解4 - 动态调试
了解一下xcode的调试app的原理:
我们xcode的调试原理是:首先mac电脑上会装有lldb的这个调试器,他怎么来调试的呢,首先我们打上断点会出现lldb调试阶段,他首先会链接手机上上的debugserver,当然这个debugserver是在xcode的包文件里,当我们运行在手机上的时候他会将这个debugserver安装到手机上,首先lldb会链接debugserver,这个debugserver在链接我们的
app,app将信息反馈给debugserver,debugserver在反馈给lldb。lldb在展示信息。
大致流程如图:
image.png
其中debugserver在哪?
他的位置在这:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/De viceSupport/9.1/DeveloperDiskImage.dmg/usr/bin/debugserver
区分几个关键词:
xcode的编译发展历程:GCC -> LLVM
xcode的调试器的发展历程:GNC -> LLDB
怎么动态调试app?
我们知道xcode是可以调试app的,但是他有一个局限性那就是他必须是有xcode运行起来才能打断点进行调试。但是装到自己手机的app是不能够用xcode进行运行的,我们的解决办法是:用终端的lldb,连接到我们的手机,然后进行调试。
- 给我们手机debugserver付给他更高的权限
我们在手机的/device/developer/usr/bin 目录下找到debugserver,这个debugserver是xcode给我们安装到的,如果你是新的手机 没有用xcode调试过按道理来说是没有这个的。那么你需要时xcode调试一次。将这个debugserver拿到桌面,查看他的权限。
执行这个命令:
ldid -e debugserver > debugserver.entitlements
如果你还没有安装ldid那么请用homebrew进行安装ldid,另外注意一下我的路径
image.png
执行完命令后在桌面上生成了:
image.png
我们用xcode将他打开,可以看到权限不多,现在我们要给他添加权限,并且重新付给debugserver权限。
我们要给他添加如下的权限:
get-task-allow 和task_for_pid-allow 都是bool类型
我的添加好了如下:
image.png
现在我们将这个权限从新赋值给debugserver
执行如下命令:
ldid -Sdebugserver.entitlements debugserver
为了确保我们的权限赋值成功了,需要验证一下,我们重新生成debugserver的权限看下是不是赋值好了。
执行如下的命令:
ldid -e debugserver > abc.entitlements
可以看到我们的权限表为:
image.png
当然我们也可以用codesign进行签名:
image.png
这说明我们配置好了,将这个debugserver拖拽到手机的 /usr/bin 目录下,切记不是/device/developer/usr/bin 目录,因为这个目录是只读的。
注意点:可能你拖拽的时候的你的ifunbox可能会闪退,我就出现了这个问题,怎么解决呢?那么就用终端命令吧
scp -P 10010 ~/Desktop/debugserver root@localhost:/usr/bin
如果你只是有一个越狱的手机那么执行这个命令可能是不正确的,因为我现在命令的意思是连接10010那个端口,你需要做的是配置手机链接,其实这个还是挺复杂的,你如果完全弄明白需要看我的越狱第一篇文章,越狱环境的搭建。我的执行完命令如下:
image.png
可以看到提示这个错误:No space left on device
通过ifunbox来查看如下:
image.png
而我们的debugserver实际上是有14M的,这说明不对,我猜测的原因有以下几个:
1.我的手机不是完美越狱的
2.在usr/bin 不能放大文件
我尝试着放到bin目录下还有usr/sbin目录下都是不行的,经过一番在网上查阅资料最后发现也有人遇到我的问题他放到root目录下了,但是这样的话会有一个问题就是不能再其他的目录下出现提示,要想在任意的文件夹出现提示 需要配置手机的环境变量,这里我就不介绍了 我也没有配置,但是你要想配置的话我之前的文章有介绍过配置mac的,手机的一样的道理。
但是现在这个文件的权限是不够的,比如:
image.png
注意我的路径这次是在手机的路径输入的。
下面执行这个命令:
chmod +x debugserver
注意我的路径位置:
image.png
下面我们将我们的端口作为映射,因为之前映射是22:10010,那个是我们之前连接ssh的时候,代码为:
image.png
然后执行一下命令
./debugserver
image.png
现在我们需要在usb.sh中增加一个一个端口映射,那就是10011:10011,这个你用别的也行只是不要用系统占用的比如:8080 1024 什么的。
下面我改usb.sh为:
python ~/Desktop/Common/usbmuxd-1.0.8/tcprelay.py -t 22:10010 10011:10011
- 让debugserver附加到某个app的进程
执行如下的命令:
debugserver *:端口号 -a 进程的id或者进程的名字
比如我将debugserver附件到微信上
./debugserver *:10011 -a WeChat
lldb进行链接debugserver 在微信中打断点
经过上面的一套的配置,现在需要lldb进行链接debugserver了,首先在电脑的终端输入lldb那么就进入了lldb环境了
比如我的是:
image.png
下面用lldb链接debugserver,执行这个命令
process connect connect://localhost:10011
注意:为什么是localhost因为我们刚才已经映射了端口了
这时候会出现如下的画面:
image.png
这说明一个断点打在可微信上了。
这时候会卡住不动,你只需要执行如下的命令:
c
continue 的意思 微信就可以继续点击了
比如我的是这样的:
image.png
至此链接搭建完成了。
lldb的语法介绍
breakpoint的介绍
从字面上的意思是打断点,比如我们在工程文件中写一个函数,我不手动打断点,直接调用命令来进行打断点看下。
breakpoint set -n test
image.png
可以看到他自动进入了那个方法。
解释:
breakpoint :断点
set:设置
-n:name 名字的意思
test :函数名称
- help的使用
当我们忘记一个命令的时候 可以借助help来帮助我们
比如我查breakpoint的:
help breakpoint
image.png
上图显示的是他的子命令,然后在继续执行这个命令:
help breakpoint set
image.png
然后在输入:
breakpoint set -n test
比如我们给一个函数打断点还可以这样使用
breakpoint set -n "-[ViewController touchesBegan:withEvent:]"
解释比如:我们如果输入:touchesBegan:withEvent: 那样的会找到很多的方法,有可能系统的方法也找到了。如下图:
image.png
- breakpoint set -r 正则表达式
比如我这样输入:
breakpoint set -r test1233
image.png
- breakpoint set -s 动态库 -n 函数名字
比如我的
breakpoint set -s libate.dylib -n freePartitionTables2D
我就是给libate.dylib动态库的这个freePartitionTables2D函数设置了一个断点
image.png
-
breakpoint list 列出所有的断点
比如我的是:
image.png -
启用、禁用、删除断点
image.png
比如我的
breakpoint disable 2
image.png
发现点击控制器断点不停住。
其他两个不在说明了。
- 给断点预先设置需要执行的命令
breakpoint command add 断点编号
image.png
image.png
- 查看某处的断点命令
breakpoint command list 断点的编号
image.png
- 删除每个断点设置的命令
breakpoint command delete 断点的编号
image.png
内存断点
大致如下,不在举例子
image.png
image lookup的使用
- 查找某个类型的信息
image lookup -t 类型
image.png
- 根据内存地址查找某个模块的位置
image lookup -a 内存的地址
image.png
- 查找某个符号或者函数的名字
image lookup - n 符号或者函数的名字
image.png
image list 的使用
- 打印模块的全路径和偏移地址
image list -o -f
image.png
- expression 的使用
他的主要作用是比如:有时候在打断点的时候,我们这个断点过了 但是我们又不想从新在执行在执行程序,那这个时候就需要这个了
比如我在当前的断点处执行:
expression self.view.backgroundColor = [UIColor redColor]
点击单步走 可以看到结果为:
image.png
简单介绍:
- thread 的使用
打印堆栈信息
thread backtrace 或者也可以为:bt
image.png
让函数直接返回某个值,不会执行断点后面的代码
thread return []
比如我的:
thread return 0
image.png
点击下一步直接退出后面的所有的断点,也没有打印了。
查看当前的栈帧变量
frame variable
image.png
下面是我们最常用的几个:
image.png
下面是我的执行结果
c
image.png
可以看到他是从第一个断点跳转到第二个断点。
s
image.png
可以看到他是单步走,遇到函数直接进去,其实他对应xcode这个地方
image.png
n
image.png
可以看到他遇到函数直接当一句话直接过
finish
image.png
image.png
可以看到在当前函数执行finish直接退出当前函数
si s 、n ni的区别:
其中si ni 是指令级别的。
image.png
而n、s是源码界别的
image.png
也就说si ni 是一般来说我们来看汇编指令的时候运用的,比如我首先设置xcode如下
image.png
然后我们直接就看nslog的输入,首先我输入:
s
image.png
可以看到直接从20跳到24行,现在我开始运行si
si
image.png
可以看到他是直接一步一步的执行。n和ni同理。
ALSR的简单说明
我们通过hopper或者machoview 查看的是虚拟内存,也就是不是真实函数的内存,以前没有ALSR的情况下,如果黑客知道你起始的内存地址,那么他就可以知道一连串的其他的地址,这样的话就会存在危险。如果我们通过一定的手段来创建一个偏移地址,这个偏移地址加上我们现在看到的起始地址才是真正的函数的地址那么就能确保安全的这个问题。现在ALSR个人认为就是:随机的偏移地址。
- 至此动态调试应该会了。