iOS逆向工程 iOS工具篇

CydiaSubstrate

绝大部分tweak正常工作的基础, 它由MobileHooker MobileLoader Safe mode 组成.

MobileHooker

替换系统函数. 也就是所谓的hook, 它主要包含以下两个函数:

(1) 其中MSHookMessageEx作用于OC函数, 通过调用method_setImplementation函数将[class selector] 的实现改为replacement, 达到hook的目的. 如 向一个NSString对象发送hasSuffix消息, 而做出hasPrefix的操作, 相当于把函数实现换了.

(2) MSHookFunction : 作用于C和C++函数, 通过编写汇编指令, 在进程执行到function时转而执行replacement, 同时保持function的指令及其返回地址, 使得用户可以选择性地执行function, 并保证进程能够在执行完replacement后继续正常运行. 而且MSHookFunction对function的指令总长度是有要求的, function所有指令加起来长度不能太短.(8字节)

MSHookFunction三个参数作用分别是: 替换的原函数 替换函数 被MobileHooker保存的原函数.这个体系写法如下:

MobileLoader

作用是加载第三方dylib. iOS 启动时, 会由launchd将MobileLoader载入内存, 然后MobileLoader会根据dylib的同名plist文件指定的作用范围, 有选择地在不同进程里通过dlopen函数打开目录/Library/MobileSubstrate/DynamicLibraries/ 下的所有dylib.

Safe mode

由于tweak本质是dylib, 寄生在别的进程里, 一旦出错可能会导致进程崩溃, 而如果是SpringBoard等系统进程, 则会造成iOS 系统瘫痪, 所以CydiaSubstrate引入Safe mode, 他会捕获SIGTRAP SIGABRT SIGILL SIGBUS SIGSEGV SIGSYS这六种信号, 然后进入安全模式. 安全模式里所有基于CydiaSubstrate的第三方均会被禁用. 而且这个插件要自己去安装, 越狱之后是不会自动安装的, 所以本人之前的手机就白苹果了, 如果有这个插件可以避免一些问题的出现, 不过白苹果也是可以解决的, 通过iTunes刷机就好了, 还是可以恢复正常的, 但系统就是最新的了.

Cycript

是由saurik推出的一款脚本语言, 可以看作是Objective-JavaScript .

首先ssh到手机的进程, 然后 cycript, 出现cy# 的提示符就说明已成功启动cycript.

按下control + D , 先退出 Cycript. 如果要测试NSStrign类的 length函数功能, 则可注入任意连接了Foundation库的进程.

通过进程注入方式调用Cycript测试函数的步骤很简单, 以SpringBoard为例, 首先找到进程名或PID如下:

SpringBoard的进程PID是4634, 接下来输入 cycript-p 4634 或者 cycript-p SpringBoard, 把Cycript注入到SpringBoard, 这时Cycript已经运行到SpringBoard进程里了.

如果知道对象的内存地址, 还可以通过# 操作符来获取这个对象.

如果知道对象的内存地址, 还可以通过# 操作符来获取这个对象.

通过choose命令, 可以获取类对象的地址.

LLDB和debugserver

LLDB

全称 Low Level Debugger 苹果出品, 内置Xcode中的动态调试工具, 运行在Mac中. LLDB功能可以概括以下四点:

(1) 在指定的条件下启动程序

(2) 在指定的条件下停止程序

(3) 在程序停止的时候对程序进行改动, 观察程序的执行过程有什么变化.

(4) 在程序停止的时候检查程序内部发生的事.


debugserver

运行在iOS 上, 它作为服务端, 实际执行LLDB(客户端)传过来的命令, 再把执行结果反馈给LLDB显示给用户. 所谓的远程调试. 默认iOS 上没有安装debugserver, 只有设备连接过Xcode, 并运行调试过才会把debugserver安装到iOS 的 /Developer/usr/bin/ 目录. 因为缺少task_for_pid权限,所以只能调试自己的App.

配置debugserver

1.给debugserver减肥

由于本人用的是iPhone5s所以选择arm64的架构.

首先将未处理过的debugserver拷贝到电脑中(~/debugserver)这里可以直接用pp助手.

然后帮助debugserver减肥.

lipo -thin armv7s ~/debugserver -output ~/debugserver
复制代码

2.给debugserver添加task_for_pid权限

下载xml格式文件配置信息 iosre.com/ent.xml 到debugserver同级目录.

然后执行如下命令:

codesign -s - --entitlements ent.xml -f debugserver
复制代码

执行前确保xml文件和debugserver在同一个文件夹内, 而且执行当前命令时要在当前文件的文件夹中.

如图:

出现这样的提示就是成功了.


3.然后将处理过的debugserver拷贝到手机中(/usr/bin/)

这里没有覆盖之前的文件, 一是因为原版文件是不可写的, 无法覆盖, 二是因为/usr/bin/下的命令无须输入全路径就可以执行, 即在任何路径下运行debugserver都可以启动处理过的debugserver.

最后还要给debugserver赋予执行权限命令如下:

chmod +x /usr/bin/debugserver
复制代码

如果在电脑终端执行必须要ssh到当前手机才可以.


4.用debugserver启动或附加进程

  • 启动进程

      debugserver -x backboard *:1234 /Applications/MobileSMS.app/MobileSMS
    复制代码

    debugserver会启动MobileSMS, 并开启1234端口, 等待来自任何IP的LLDB接入.

    如果中途出现错误, 那么就有可能是task_for_pid权限没加上.

  • 附加进程

     debugserver *:1234 -a "MobileSMS"
    复制代码

    其实附加进程和启动进程区别就是需要手动打开指定的App.

  • 错误

    如果出现如图:

    说明iOS上的/Developer/目录下缺少必要的调试数据.因为没有在Xcode的Window->Devices菜单中添加此设备, 重新添加即可.


LLDB使用说明

在终端中输入lldb即可启动lldb. 如图:

然后执行:

process connect connect://iOSIP:1234
复制代码

记得把iOSIP换成自己手机的IP.

成功后如图:

在这之前debugserver启动过进程或者附加了进程才可以.

所以电脑终端最好开倆个窗口好操作些, 用手机终端则麻烦很多.

这个时候我们就可以开始调试了, 下面看一下常用的LLDB命令.

1. image list

用于列举当前进程中的所有模块, 因为ASLR的关系, 每次进程启动时(就是当你打开一个应用时), 同一进程的所有模块在虚拟内存中的起始地址都会产生随机偏移. 个人理解其实就是App启动时在手机内存中是有一个起始的内存地址的, 而ASLR其实就是让App每次打开时的起始地址随机.

那么怎么获取模块的起始地址呢? 待LLDB链接debugserver后, 先输入如下口令:

image list -o -f
复制代码

  • 模块基地址

上图的输出中 第一列 [x] 是模块的序号. 第二列是ASLR产生随机偏移大小. 第三列是模块的全路径, 括号里是偏移之后的起始地址. 模块的起始地址术语叫模块基地址.

偏移后模块基地址 = 偏移前模块基地址 + ASLR偏移
复制代码

如上图MobileSubstrate.dylib的偏移前模块基地址 = 0x0000000104910000 - 0x0000000104910000 = 0

那这个0哪里来的呢? 我们把MobileSubstrate.dylib放到IDA中.

把View-A拉到最上面看到的第一行, 其中0就是我们计算后的偏移前基地址.

  • 符号基地址

    偏移后符号的基地址 = 模块ASLR偏移 + 符号基地址

    如果是偏移前只要减去ASLR偏移即可.

    符号偏移前基地址可以在IDA中根据符号来获取

    只要知道偏移前基地址从IDA看, ASLR偏移从LLDB看就可以了.

2. breakpoint

和Xcode中的断点一样, 只不过这里不是图形工具而已. 一般逆向工程中用到的:

b function 

在函数的起始位置设置断点

br s –a address

br s –a 'ASLROffset+address'

在地址处设置断点
复制代码

以我自己新建Xocde工程项目设置函数[ViewController buttonAction:]断点为例:

(1) 用IDA查看这个项目偏移前的基地址, 把项目二进制文件放入IDA中, 定位到buttonAction:方法可以看到:

第一条指令   SUB    SP, SP, #0x30 偏移前的基地址是0x100006728.  
复制代码

(2) 通过LLDB查看ASLR偏移 0xa8000

(3) 设置并触发断点:

指令的偏移后基地址 = 0x100006728 + 0xa8000 = 0x1000ae728

在LLDB中设置断点

br s -a 0x1000ae728
复制代码

其中Breakpoint后面的1是断点的序号, 以后会用到.

然后我们点击屏幕上的按钮触发断点.

打印的是一些方法的信息, 当进程停下来后我们可以用c命令继续运行.

还可以通过br dis 和 br en 和 br del 来禁用 启用 删除断点.

如果是禁用所有断点则执行

br dis
复制代码

禁用某个断点在后面加上断点的序号

br dis 1
复制代码

同理启用和删除断点也是一样.

br en  
br del
复制代码

另外一个很有用的命令就是在断点触发前执行预先设置的指令, 它的用法如下:

br com add 1
复制代码

然后出现如图:

其中po i 是要执行的指令, 而DONE是退出设置指令. 数字4是断点的序号.

这里设置了一条指令, 然后我们点击按钮触发方法如图:

这个时候i的值是1.

这个命令一般用于自动观察某个断点触发时的上下文变化, 后面会用到.

3. print

LLDB主要功能之一是在程序停止的时候检查程序内部发生的事, 而这个功能是通过print命令完成的, 他可以打印某处的值. 以我本人最近开发的程序 -[HomePageController tableView:didSelectRowAtIndexPath:] 方法为例演示一系列用法.

po   $r0    输出r0对应的值
p    $r0    输出r0值的类型以及命令结果
p/x  $r0    输出r0的十六进制值
x/10 $r0    输出指针r0指向的连续10个字的数据


nexti(ni)   执行下一条机器指令  不会进入函数体
stepi(si)   执行下一条机器指令  会进入函数体
** 进不进入函数体的意思就是你再方法中调用其他方法, 而当断点到这个方法的时候, 如果是上面的指令会跳转的这个调用的方法里面, 而下面的指令不会进入. ** 


register write  r0  1   给寄存器r0赋值为1


复制代码

转载于:https://juejin.im/post/5b289af2518825748871b481

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值