声明: 本文只限学习, 请勿用于其他途径, 如造成任何问题与本人无关.
经过不断的逆向学习, 已经由小白进阶到了入门的阶段. 所以在业余时间准备做点什么.
获取目标可执行文件头文件
-
获取目标ipa到mac.
-
砸壳 (如果已经砸壳则不需要此步骤)
-
dump头文件
由于我之前文章都有做过, 比较简单就不一一写出来了.
寻找目标控制器
首先我们从UI表现入手, 找到我们的控制器.
由于解锁操作是在一个新界面, 所以我们要在扫码后查看这个控制器.
怎么查看控制器呢?
这里我通过FLEXLoader查看, 首先找一个可扫描的二维码(这里可以是任意的二维码), 然后打开app登录并扫描(这里这个登录的账号一定是缴费过的, 不过没缴费也不要怕, 后面我会讲解决办法), 然后点击开锁进入到扫描界面, 然后选择select选项, 对准二维码开始扫描, 当进入到加载界面的时候马上点击此界面. 如图:
UnlockStateViewController
通过命名我们可以猜测这个控制器就是目标的控制器.
那么怎么验证呢 ?
由于这个界面在请求到数据后会马上pop到首页, 我们很难在这么短的时间做验证, 所以我们可以利用Tweak, hook这个类的viewDidLoad方法并打印验证.
创建我们的Tweak, 这里只给出关键部分截图:
Makefile
Tweak
把手机连接到Xcode, 然后cmd shift 2 到真机/模拟器设备管理界面. 点击下方的小三角可以看到项目的运行打印信息.
我们安装Tweak到手机, 并扫描进入到目标控制器.看是否有打印, 如果出现我们的打印, 那么就没什么问题.如果没有那么我们需要重新找.
通过图片发现来自我们的WQCar.dylib动态库的打印WQCar出现了, 这也就验证了我们的操作. 我们的目标控制器就是 UnlockStateViewController
寻找解锁时的请求方法
打开 UnlockStateViewController.h, 寻找我们需要的信息.
通过观察, 这个类的信息并不是特别多, 很容易就找到了相关的解锁方法.
首先确认一下扫码后这三个方法是否会执行. 我们通过lldb下断点验证一下. 为了不让我们每次下断点的时候都加上ASLR, 我们可以通过ida更改基地址, 直接在原来的地址基础上加上ASLR. (lldb设置断点的操作之前文章有讲过)
修改完成之后, 我们分别断点到以下三个方法:
- (void)openLock:(unsigned long long)arg1 bikeNo:(id)arg2 cityCode:(id)arg3 cityName:(id)arg4 areaCode:(id)arg5 areaName:(id)arg6 province:(id)arg7;
- (void)openLock;
- (void)unlockSuccess:(id)arg1;
复制代码
然后扫描二维码看看断点执行情况.
由于中途出现一个问题, 所以一直没有更新.
问题是断点一直不触发, 后来发现问题是 debugserver时出现的问题, 因为我目前网络环境不好, 当我通过lldb调试时, 网络突然断了, 但是程序还在debugserver状态, 也就是卡死状态.
我是怎么解决的呢?
通过ps -e 查看当前手机的当前进程我发现, debugserver的进程还在, 因为就是他才让程序卡死的, 所以我想把这个进程kill掉应该就没事了, 但是当我执行完命令后这个进程并没有kill掉. 所以这个方法不行.
后来我想到因为退出调试状态的唯一办法就是通过lldb退出调试状态, 既然现在的调试状态断开了, 那么我们等网络好一些再重新通过lldb连接到手机. 等了一会, 连接上之后, 执行 Ctrl+D 退出调试状态, 可以看到App退了出来, 再来看看手机进程, 发现debugserver 进程不见了.
这个问题困扰我好久, 因为网上没有类似的问题, 所以只能自己解决, 通过不断地思考和解决问题, 我们的能力会很快的提升, 希望读者遇到类似问题也要尝试先自己解决. 然后继续...
三天后:
因为我之前购买过会员, 所以随便找一个二维码扫描下, 看看断点触发情况:
可以看到先触发 - (void)openLock;
后触发了 - (void)openLock:(unsigned long long)arg1 bikeNo:(id)arg2 cityCode:(id)arg3 cityName:(id)arg4 areaCode:(id)arg5 areaName:(id)arg6 province:(id)arg7;
通过名字可以猜测出来 - (void)unlockSuccess:(id)arg1;
方法应该是在解锁成功时候调用.
而我通过ida 发现第一个方法调用的第二个方法.
所以现在我们把目标放在 第二个方法上. 我们通过ida看看这个方法做了什么.
这个方法实现很简单: 我们找到关键代码
可以看到 通过[BLEManager shareBLEManager] 获取单例, 然后给这个类的block属性赋值. 具体是哪个属性我们通过ida看不到.
这里可以通过Tweak的方式打印 BLEManager 类的所有方法.通过打印结果去看看调用情况, 这里留给读者自己完成.
然后找到 ADR x8, sub_1002AD8D4
, 这个其实就是赋值的block. 我们双击它定位到这个block.
找到关键代码:
可以发现 - (void)openLock:(unsigned long long)arg1 bikeNo:(id)arg2 cityCode:(id)arg3 cityName:(id)arg4 areaCode:(id)arg5 areaName:(id)arg6 province:(id)arg7;
中的参数被传递到这个方法. 做事情的方法应该就是它了.
我们在往上看看发现HttpClient这个类.
我们到这个类中看看有没有这个方法. 可以发现这个类就是集中管理所有请求数据方法的.
经过查找发现就是这个方法.然后我们通过ida看看这个方法做了什么.
通过观察发现大部分都是为了下一次请求做的参数准备操作.我们看看最下面的 _objc_msgSend
. 分析一下他的参数:
首先看一下调用者X0是谁:
可以发现调用者是XMCenter这个类.在看看X1是谁.
X1是 + (id)sendRequest:(CDUnknownBlockType)arg1 onSuccess:(CDUnknownBlockType)arg2 onFailure:(CDUnknownBlockType)arg3;
这是个类方法. 我们打开XMCenter.h看看
而且我在这个类中还发现了同样参数名字的实例方法, 我猜测这应该是为了简化外面调用做的一层包装.
我们看看是不是这样.
可以看到 XMCenter
通过+ (id)defaultCenter;
类方法获取实例然后调用的实例方法.
那么我们看看这个实例方法做了什么事情.
发现这个方法又调用了当前类的这个方法
我们看看这个方法. - (id)sendRequest:(CDUnknownBlockType)arg1 onProgress:(CDUnknownBlockType)arg2 onSuccess:(CDUnknownBlockType)arg3 onFailure:(CDUnknownBlockType)arg4 onFinished:(CDUnknownBlockType)arg5;
发现又调用了当前类的- (void)xm_processRequest:(id)arg1 onProgress:(CDUnknownBlockType)arg2 onSuccess:(CDUnknownBlockType)arg3 onFailure:(CDUnknownBlockType)arg4 onFinished:(CDUnknownBlockType)arg5;
这个方法.
而这个方法就是我们要找的解锁底层请求方法.
怎么验证呢?
打个断点试试就知道了.
到这里整个分析过程就结束了, 不过由于本人没有接触过硬件交互的App开发, 不熟悉这里面的业务逻辑. 所以没有进一步的深入分析, 由于会员逻辑是在服务器处理的, 所以我们客户端想做的事情就很难了, 不过本文的目的是巩固之前学习的知识, 实践静态动态分析和逆向常用工具的使用.
到这里这篇文章就结束了.