大家有多久没有对自己的 iPhone 越狱了,越狱意味着打开了一扇 iOS 的 root 大门,但同时也对稳定和安全 say goodbye 了。那么非越狱的 iOS 手机对于我们的自动化测试还能做些啥?今天和大家一起探讨关于 iOS 非越狱的动态插桩原理及其在自动化测试领域的应用。

一、前言

iOS 的动态插桩(iOS hook)技术在 iOS 越狱界已经是耳熟能详的话题,但是有一个问题——越狱后的手机不稳定,不稳定对于自动化测试来说就是致命的伤害,所以本文主要分析 iOS 在非越狱手机上的动态插桩技术及其自动化方面的应用。

二、QT4i 通用测试桩的介绍

QT4i是我们的 QTA 自动化测试提供的 ios 框架,下面我们先以 QT4i 的例子了解下动态插桩的实现原理。QT4i 框架提供了基于动态插桩原理实现的一个通用测试桩——QT4iSTub,对于需要访问被测 App 的进程内接口的测试场景,提供了一种新的测试思路,同时也提升和丰富了 App 可测性。下图是 QT4iSTub 的实现原理,通过 Python 层的 API 接口可以直接调用 APP 内部实现的 ObjectIve-c 方法。

三、iOS 的动态插桩原理分析

动态插桩是在没有目标 app 源码的前提下,通过一些技术手段实现对目标 app 的 ipa 安装包的修改,再将修改后的 app 安装到手机设备上,从而达到改变目标 app 的表现行为的目的。从这里的定义可以看出,iOS 动态插桩的步骤大致分为三个:编写 hook 方法的具体内容(改变目标 app 的行为)、注入目标 app(保证目标 app 启动时能加载 hook 的内容)、重签名目标 app(保证修改后的 app 能在非越狱的手机上能安装)。下面结合这三个步骤分析 iOS 动态插桩的原理。

1. 编写 hook 方法,改变目标 app 的行为

工欲善其事,必先利其器,开发 iOS 的动态桩,可以选择theos或者MonkeyDev作为开发工具,下面的例子以 theos 为例。

%hook AppDelegate

 // Hooking an instance method with an argument.
 - (void)applicationDidEnterBackground:(UIApplication *)application {
     NSLog(@"App entered background!!!");
     %orig; // Call through to the original function with its original arguments.
 }

 %end

2. 注入目标 app,保证目标启动时会加载 hook 的动态库

前面介绍 tweak 的时候已经说明,要在目标 app 运行时加载我们的 dylib,必须保证 app 中存储有指向我们的 dylib 的引用。所以,注入目标 app 的动作就是修改目标 app 的二进制文件。

我们先看下 iOS 中可执行文件的格式 Mach-O,如下图所示,包含三个部分:

了解了 Mach-O 文件格式后,我们注入 app 的目标就很清晰了,也即在目标 app 的可执行文件的 Load Command 部分添加一个加载命令 LC_LOAD_DYLIB 指向我们的 dylib,使得目标 app 启动时可以加载我们的 dylib。这里推荐一个命令行工具insert_dylib方便我们完成修改动作。修改完后,查看目标 app 的 Mach-O 结构如下所示:

3. 重签名目标 app

对于非越狱的 iOS 设备来说,系统的签名校验机制是保证 app 安全的重要防线。然后,事物都有它的两面性,一方面为我们提供安全保障,另一方面却阻碍了我们的动态插桩(直接安装修改后的二进制文件会导致安装失败)。所以,这里的重签名显得尤为重要,一旦失败将前功尽弃。重要的事情说三遍:必须要用花钱买的个人开发者证书或者企业证书进行重签名!必须要用花钱买的个人开发者证书或者企业证书进行重签名!必须要用花钱买的个人开发者证书或者企业证书进行重签名!

下面简单说下重签名的步骤:
1) 解压目标 app 的安装包,将 Tweak 工程生成的 dylib 文件放入 Payload/app 名的目录下;
2) 重签名步骤 1) 中的 dylib;
3)删除原有签名文件,对整个安装包使用有效的证书重签名。

四、动态插桩在 iOS 自动化测试中的应用

目前主流的 iOS UI 自动化测试都是基于 apple 官方的 XCTest 框架实现的,受限于 XCTest 和被测 app 之间的通信是跨进程的方式,很多基于被测 app 内部信息的测试场景就无法覆盖了。而动态插桩的测试方案很好的弥补了 XCTest 的不足,因为动态插桩保证了测试进程和被测 app 是同一进程(也即进程内),可以方便采集被测 app 的内部信息,依据测试需要修改被测 app 的状态等,下面给出一些动态插桩在自动化测试中的典型应用。

1、app 的沙盒访问:读取沙盒信息,清理登录态
对比利用 itunes 私有协议访问沙盒,动态插桩方案更可靠稳定,访问效率更高;

2、访问系统相册:上传图片、比对图片
由于系统相册中的图片 iOS 系统进行了加密,直接无法访问和文件关联,但是动态插桩可以轻松实现;

3、动态切换 APP 内的启动参数,无需重新编译安装包,通过即可完成 APP 内的配置参数的切换;

4、获取自动化测试资源
例如:自动化用例需要一些比较大的视频文件,相比于在 mac 上下载后,通过 usb 传输到手机,让手机上 app 主动下载是效率更好的方式,插桩便可以实现;

5、动态修改当前窗口某控件的属性,用于国际化的语言排版展示等测试场景。

五、感兴趣的同学可以加入 QQ 群和公众号交流


如果你想要了解更多资讯,欢迎关注我们的微信公众号😀 我们会定时向大家推送团队同学分享的经验文章哦。


↙↙↙阅读原文可查看相关链接,并与作者交流