iOS非越狱逆向分析实战

本次实战的根本目的,是为了分析竞品,优化自身产品的用户体验。在我们之前版本中,增加了GIF制作的功能,用户可以拍摄视频、照片或者从相册选择视频、照片等来合成GIF图,并可以给GIF添加贴纸、文字,以及修改GIF速度的功能。最初,我们通过ffmpeg进行GIF的合成,由于中间有一系列的操作,为了保证图片质量,我们不得不做一些耗时的操作,导致合成速度慢,用户体验非常差。与此同时,我们发现了市面上一款类似的app,其合成效率很高,用户体验很好,于是,我们就走上了逆向之路。

软件逆向工程,指的是通过分析一个程序或系统的功能、结构或行为,将它的技术实现或细节推导出来的过程。当我们对一个软件的功能很感兴趣,却又拿不到它的源代码时,往往可以通过逆向工程来对它进行分析。

逆向工程主要有两个作用:

  • 分析目标程序,拿到关键信息,可以归类于安全相关的逆向工程

    • 评定软件的安全级别
    • 逆向恶意软件
    • 检查软件后门
  • 借鉴他人的程序功能来开发自己的软件,可以归类于开发相关的逆向工程

    • 逆向系统API
    • 借鉴别的软件

最开始,iOS平台的逆向工程都是基于越狱环境的。目前非越狱环境下的软件逆向,也非常成熟了。有很多大神研究过,这方面的资料和文章也比较多。有的大神为了方便还做了一套框架,专门做非越狱逆向,现在用起来十分方便。本文不会话太多篇幅来讲概念的东西,主要以实战为主,那就开始吧。

获取砸壳后的app

从AppStore下载的App都是经过加密的,可执行文件被加上了一层“壳”,所以为了解密,我们就必须进行“砸壳”。

最早的砸壳工具是stefanesser写的dumpdecrypted,通过手动注入然后启动应用程序在内存进行dump解密后的内存实现砸壳,这种砸壳只能砸主App可执行文件。人都想偷懒,所以AloneMonkey大神就做了一条命令砸壳的工具frida-ios-dump

如果我们单纯的要获取一个砸壳后的app,我们可以是直接从各种xx助手里面下载,注意的是这里下载的是越狱应用(不是正版应用),这些应用都是脱壳的应用。

MonkeyDev

如果已安装过的,可以直接跳过该步骤。

如果您对越狱开发了解的话,那么您应该听说过iOSOpenDev。他可以直接通过Xcode新建Hook的工程模板,然后编译生成安装deb。通过它来进行越狱开发十分方便。但很遗憾,作者在13年就不更新了。Xcode7及之后的版本,该环境就有很多问题。

MonkeyDev可以认为是iOSOpenDev的升级版。MonKeyDev不仅仅可以进行越狱开发,也可以进行非越狱的Hook的开发。该非越狱逆向环境自动集成,只需要一个砸壳的应用即可,非常方便。

安装卸载,请查看官方帮助文档,这里就不赘述了。

创建MonkeyApp项目

安装好环境后,重启Xcode,新建工程,往下拉,选择MonkeyApp
image然后填写Product Name,不需要管Target App。创建完成,就会得到这样一个工程。
image不要被这个工程吓一跳,其实很多东西,暂时都不用去管。

  • Scripts目录下,是重签名,打包的脚本,我们不需要做修改
  • Config目录下,是cycript的一些脚本下载以及methodtrace配置代码,我们也不需要做修改
  • TargetApp,先跳过,后面说
  • TestImgPlayDylib 是CaptainHook Tweak模板
  • Logos 是 Logos Tweak模板
  • LLDBTools 是用于LLDB调试的代码比如po pviews()
  • AntiAntiDebug 里面是反反调试的代码
  • fishhook 是自动集成的fishhook模块

拖入编译

还记不记得最开始获得的砸壳好了的ipa文件,然后右键项目中的TargetApp文件夹Show In Finder,把ipa文件拖入下面的位置(除了ipa文件外,.app文件夹也可以)
image然后就可以设置Team,编译,运行了。注意这里只能真机运行。还有需要注意的是MonkeyApp目前只支持普通app,不支持扩展(appex),如果您的app有扩展,使用MonkeyApp时,会将所有的扩展删除。

现在可以看到运行效果了,与原本的App没有两样。现在先不要去玩app,看一看其他的东西。

class-dump

顾名思义,就是用来dump目标对象的class信息的工具。它利用Objective-c语言的runtime特性,将存储在Mach-O文件中的头文件信息提取出来,并生成对应的.h文件。

MonekeyDev很贴心的集成了class-dump功能,可以再build settings最下面开启该功能。
image

默认值为NO,设置为YES后开启,再次build一下工程,就可以在工程目录下,看到该app的所有头文件信息了,然后我们就可以从这些头文件中进行探索了。
image

如何知道App使用了哪些第三方库

有时候想研究某个竞品APP时,需要了解其使用的第三方库,使用 class-dump 导出的头文件非常多,刚靠肉眼查看时,耗时耗力。为了解决这个痛点,便发明了这个工具来查看。下面是获取某个第三方APP使用的第三方库,可以查看pod库的star数,源地址。

该工具基于python写的,下载源码后修改file_catagory.py文件的IPA_HEADER_PATH为class-dump导出的头文件目录。执行python main.py即可。
image

看这些就是匹配上的第三方库。一些软件,也会把用到的第三放库写到一个licenses.txt文件中,放到bundle下面。对比了一下,这个脚本给出的第三方库还算比较准确。

该工具除了可以了解App使用的第三方库以外,还可以给class-dump出来的头文件进行分类。下载源码后修改file_catagory.py文件的IPA_HEADER_PATH为class-dump导出的头文件目录。执行python file_catagory.py
image
这样头文件就分类好了,然后再去找对应的话,也会比较方便了。

通过对其使用的第三方库了解,我们判断出这个软件,没有使用ffmpeg,那么他是如何进行GIF合成的呢?会是使用系统API么?

反编译

带着上面的问题,我们来到来反编译看看该app,是否使用了系统提供的合成GIF的接口。常用的反编译软件有IDA和Hopper,本文使用到的是Hopper。我们把砸壳好的app文件夹中的二进制文件,拖入到Hopper中。
image
然后就会看到左边一系列的函数列表,以及右边的一些函数的汇编代码。然后我们进行筛选,发现确实存在系统API。
image

代码注入(Logos Tweak为例)

为了证实我们的猜想,我们来hook一下这个方法。笔者对Logos Tweak更为熟悉,所以就在Logos Tweak中调用fishhook来hook这个函数CGImageDestinationAddImage

我们都知道,任何一个app都会有AppDelegate相关的类,那么我们从刚才的class-dump出来的头文件中,找到了BMAppDelegate头文件,那么我们就把这个头文件拖入工程,hook这个类的application:didFinishLaunchingWithOptions:方法,在方法中调用fishhook,将CGImageDestinationAddImage给替换掉,并且打上断点,运行。运行后操作手机触发GIF合成。
image

Theos的语言就是如果要hook某各类,就写上%hook 对应的Class,然后最后要有%end结尾,与Objective-C类的定义类似。然后里面去重写方法就好了,如果要调用原来的函数,那么使用%orig,当然也可以传递新的参数给%orig。而针对于C语言的fishhook,就需要调用rebind_symbols方法,传递函数名,新的函数指针,以及原始函数的函数指针的指针即可。

我们发现,断点处停止下来了,证明了我们的猜想是正确的。GIF合成就是通过系统API一帧一帧的加图片的,而且我们通过控制台还发现,这里加图片都是在同一个线程做的,并没有使用多线程。

我们知道了他如何合成GIF的,那么针对于每一帧图片,增加文字,他们是如何做的呢?我们当时通过ffmpeg是需要将GIF转为视频,将文字生成一张图片,将该图片作为水印添加到视频上,然后再转为GIF,所以才耗时比较长。

既然他们合成GIF用的是系统API,而且在第三方库中也没查到图片合成相关的库,那么大胆猜测,他们是将文字生成一张图片后,与每一帧图片进行合成,生成新的一组图片,然后再将这组图片合成GIF。那么我们就来试试UIGraphicsGetImageFromCurrentImageContext这个函数,看看是不是通过这个方法来得到的带有文字、贴纸的图片。

在手机上操作,给GIF加上一段文字“哈哈哈,然后合成。
方法调用了,通过动态调试,我们可以看到,文字转换为一张图片了。在合成GIF时,添加进来的每一帧图片,也就是带有文字的图片,说明这两张图片合成了一张新的图。

image

image

这样,我们就搞清楚了她是如何进行合成以及加水印的。

restore-symbol

其实上面用到了动态调试,但也仅仅是打了断点而已。有的肯定会有动态调试的需求。调试的时候,可能会出现调用栈无法解析,里面都是__lldb_unnamed_symbolXXXX之类的信息。原因是,Xcode显示调用堆栈中符号时,只会显示符号表中有的符号。为了我们调试过程的顺利,我们有必要把可执行文件中的符号表恢复回来。

MonkeyDev直接继承了restore-symbol,我们在Build Settings中直接设置为YES即可恢复符号表,想了解更多restore-symbol点这里

我们知道它在合成之前的编辑页面进行了拆帧,我们想知道,拆帧之后在该界面,它是要合成GIF图,然后再进行播放么?还是说如果原来是视频就播放视频,如果原来是GIF就播放GIF呢,于是我们通过Reveal来查看一下它的控件树。

Reveal

Reveal是开发利器,很多同学,应该也用过他来调试自己应用的界面吧。通过MonekeyDev,你也可以很容易的,调试其他app的界面。目前MonkeyDev集成的是V14版。连接成功后,可以在界面内看到app的控件层次结构。
image

如果不喜欢使用Reveal,该环境也自动集成了Cycript,可以下载Cycript SDK,然后进去SDK目录运行如下命令,Cycript查看界面也没有问题。(当然Cycript不仅仅是用来看控件树的,它还可以进行很多其他分析,有兴趣的可以了解一下)
image

回到Reveal,我们来到编辑页面,看一下层次。
image

我们发现,这里的与我们的猜想不一样,无论选择的是视频还是GIF,这里都是一个ImageView。

于是,看看这个ImageView是如何播放的,通常情况下,给ImageView传递一个图片数据,即可播放。但很遗憾,我们hook了setAnimationImages,方法没有调用。但是它调用了setImage方法,所以这里是他们自己来控制一段时间后,刷新ImageView设置下一阵图片,实现的播放动画的效果。
image

到这里我们分析的差不多了,我们了解了他在编辑页面,播放的是图片,而不是视频。最终合成GIF的时候,将文字转换为图片,然后图片与每一帧图片进行合成,之后通过系统API生成一个GIF。

小结

我们的需求达到了,我们清楚的了解了该app是如何实现的,那么我们的逆向实战,也就到这里了。

在这里我们,用到了MonkeyDev来快速、方便的实现非越狱下的逆向分析。MonekeyDev中集成了很多方便的工具,例如Reveal、cycript、class-dump、反反调试等工具,也帮我们省去了,重签名、打包等繁琐的步骤。

通过MonkeyDev集成的工具以及一些其他工具,如IDA等,让我们能够把一个app一点一点的扒开,进行学习。

本文简单的介绍了一些逆向工程的基础实践,希望可以帮助到刚刚结束逆向的同学。

猜你喜欢

转载自blog.csdn.net/TuGeLe/article/details/81411384