【QTA】探讨iOS非越狱的动态插桩原理及自动化测试的应用

2019-04-03  阿呆 

一、前言

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为例。

  • 使用theos创建iOS tweak工程,这里解释一下何为tweak。Tweak实质上是iOS平台的动态库,以dylib这种形式存在,类似Windows 下的 .dll,Linux 下的 .so,Mac 下的 .dylib/.tbd。与静态库相反,动态库在编译时并不会被拷贝到目标程序中,目标程序中只会存储指向动态库的引用,等到程序运行时,动态库才会被真正加载进来。

  • 打开工程中的Tweak.xm文件,编写hook方法的具体内容,这里theos提供一套Logos命令(以%开头)简化我们实现hook方法的过程。在这套Logos命令背后,theos定义了一系列的宏和函数,底层利用objective-c的runtime特性来替换系统或者目标app的函数(Method Swizzling),从而实现对目标app的hook。下面的代码展示了一个简单功能,在目标app退到后台时打印一条日志记录。
    1. %hook AppDelegate
    2.  
    3. // Hooking an instance method with an argument.
    4. - (void)applicationDidEnterBackground:(UIApplication *)application {
    5. NSLog(@"App entered background!!!");
    6. %orig; // Call through to the original function with its original arguments.
    7. }
    8.  
    9. %end
  • 编译Tweak工程,生成hook后的动态库(dylib)。如果Tweak工程依赖第三方库,需要将第三方库放入$THEOS/lib目录。


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

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


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

  • Mach-O头部(mach header):描述了Mach-O的cpu架构、文件类型以及加载命令等信息。

  • 加载命令(load command):描述了文件中数据的具体组织结构,不同的数据类型使用不同的加载命令表示。

  • Data: Data中的每个段(segment)的数据都保存在这里,段的概念与ELF文件中段的概念类似。每个段都有一个或多个Section,它们存放了具体的数据与代码。

了解了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、动态修改当前窗口某控件的属性,用于国际化的语言排版展示等测试场景。

五、欢迎感兴趣的同学扫描加入我们的QTA用户交流群




283°/2835 人阅读/0 条评论 发表评论

登录 后发表评论