综合

逆向分析及修复稀土掘金iOS版客户端闪退bug

时间:2010-12-5 17:23:32  作者:探索   来源:时尚  查看:  评论:0
内容摘要:感觉是要搞个《逆向分析及修复app》系列的节奏啊大家好,我又来了。上次给大家分享了《逆向及修复最新iOS版少数派客户端的闪退bug》,@了一些iOS界的大神

逆向分析及修复稀土掘金iOS版客户端闪退bug

感觉是分析复稀要搞个《逆向分析及修复app》系列的节奏啊

大家好,我又来了。上次给大家分享了《逆向及修复最新iOS版少数派客户端的及修金闪退bug》,@了一些iOS界的大神,没想到受到了大家的转发和关注,还真有点小激动。同时也在微博的土掘评论下收到了稀土掘金的欢迎,还给我开了专栏权限,希望可以到稀土掘金上分享写作。备感荣幸的版客同时,突然发现,一直在稀土掘金上面看文章的我,一直没有注册账户(好像注册了但是忘了密码,在1Password中也没有记录,看来是很久之前登陆过了)。这也是户端这次故事的开始,收到稀土掘金的评论后,马上打开了早已不知道什么时候下载的 掘金 app。果然,没有登陆记录,作为开发者马上用 GitHub 授权进行登陆,尴尬的闪退事情发生了,在授权成功后,app闪退了。好尴尬啊,好吧稀土掘金上的分析复稀处女座就献给你了。

跟之前写《逆向及修复最新iOS版少数派客户端的及修金闪退bug》文章时不同,在文章写到这里的时候,我其实还没有完全分析完崩溃的原因。我是土掘分析途中决定开始写这篇文章的,决定一边分析一遍写,这样才会尽可能的保留分析的全过程,当然最后能不能分析到关键点,且看下去吧,因为我也不知道。

  • 最新版 4.1.3 稀土掘金 app 在使用 github 登陆成功后会 crash
  • 是版客在登陆成功后才 crash,因为再次进入后会发现已经登陆成功
  • 可能不是所有人都会遇到这个bug,根据后来的分析,可能是Github的某个个人信息中缺少某些设置导致的授权后crash

###找到崩溃原因
分析一个bug的开始,当然是从崩溃原因找起,前一篇文章,我们使用了lldb调试,在触发 app 崩溃的时候,从 Terminal 找到了关键词:。这次,我们使用最简单的户端,查看系统日志来定位崩溃原因,使用最新的 Mac,打开控制台,连接手机并清空多余的系统日志打印,然后触发崩溃,观察控制台中输出的内容(如果输出日志过多,可以使用进程名作为关键词,进行过滤)。如果可以还是闪退不要使用关键词过滤,因为不是所有的崩溃都是由于 app 本身的问题导致的,正如签名破坏的 app 在没有越狱的手机上会安装失败,这个安装失败的原因查找就需要在日志中的 进程打印中进行查找
(是用来管理我们iPhone应用的一个应用,可以理解为我们 PC 端的桌面,它还负责应用的桌面排列、管理通知中心等)。分析复稀

正如我们希望的及修金一样,崩溃的原因最常见不过了:

这个崩溃就很有意思了,开发者对调用了方法,因为找不到该方法而崩溃!当然开发者肯定是不会主动对类型调用方法,猜想:

  • 开发不知道对象会变成类型,说明这个对象可能是在运行时确定的
  • 调用,说明开发者认为这个类应该是属于类型的

大胆猜测:结合这个崩溃是发生在登陆的时候,需要从网络获取登陆信息,那么会不会是程序在对获取到的登陆信息进行处理的时候导致的崩溃(比如登陆用户的一些信息没有设置,所以程序从登陆信息里取到的是空值)。

分析入口

1.找到登陆界面的土掘控制类

分析从该登陆界面出发,使用cycript注入app后,打印找到该界面的控制器:

currentVCWithKeyWindow() 来自我自己写的一个脚本,你可以前往common.cy下载。下载之后,将其放到目录下即可。
使用时,只需要在cycript注入的时候加上即可。
其中还有一些好用的函数,比如快速打印所有的和

2.查看登陆界面的调用流程

使用等工具,导出的所有类头文件,使用工具生成类的所有函数,使用编写安装插件后,触发崩溃,然后在控制台查找该类的函数调用逻辑。如下:

可以发现,程序是在调用之后才收到的崩溃的通知。所以很有可能崩溃的原因不是导致的,而是在类返回登陆信息后。

仔细研究的调用过程,发现了几个有意思的方法,调用顺序如下:

  • -(void)setCallBack:
  • -(id)onAuthCompleted:
  • -(id)callBack

!太熟悉不过了,这不就是我们经常在开发中使用的设置回调的时候使用的参数名吗!!并且,该在类初始化的时候被设置,类方法调用之后才被回调。结合这个方法名,这个逻辑是不是很像:我们在登陆成功之后,使用block传回了我们的登陆信息进行处理。

分析到这里,其实我们已经可以使用lldb调试,打印该的内存地址,然后去掉内存地址偏移后,在中查看block的实现,看是否该block导致的崩溃。但是作为逆向的学习,我们可以先继续深入,查看一下这个block的传递调用过程。

继续深入的回调过程

既然该回调是在被初始化的时候被设置的,那么我们先找到是谁初始化的该登陆控制器:

使用注入打印该控制器类名,并尝试的授权登录入口:

发现成功触发登录,所以可以确定方法确实是登录的入口

1.实现

在中查看该方法的实现:

内容很简单,调用了单例类,并传入回调callback参数:

2.[LoginCloud singleClass]实现

在查看如下:

解释如下:

  • 初始化,并从中初始化控制器
  • 给控制器设置回调block(另一个callback参数回调)
  • 显示控制器

其实这里的应该就是类型,我们可以使用确认一下:

现在的传递应该很清楚了:点击 github 登录按钮时,调用单例,并传入,根据传入的初始化登录控制器,并设置另外的一个回调,在登陆成功后,通过该进行回调。

分析callback实现

block有点特殊,它也是一个对象类型。在这里我们涉及到了两个block的传递,为了方便之后的分析,我们可以先将两个block的实现地址和传递的参数类型都打印出来。接下来的操作中可能会因为程序多次重启导致文中上下显示的内存地址不一致的情况,我每次都会进行说明。

1.获取block的函数地址

因为我们知道这个作为参数传给了方法,所以lldb连接服务(如何lldb可以看我的第一篇文章)后,我们在这个方法上打个断点,同理设置断点。
根据的内存结构可以查找的函数实现地址和参数返回值类型,具体可以看这篇文章。开源就是好,节省步骤,我们可以使用开源的chisel。触发断点后,获取到的内存地址后,使用打印block:

获取到第一个 block 的函数实现地址:
参数及返回值:

获取到第二个 block 的函数实现地址:
参数及返回值:。

2.获取的具体传回参数值

知道了 block 的参数的类型,那么我们可以写个 来 这个 block 然后打印一下,传递的这些参数内容,根据回传顺序,我们先打印第二个:

打印结果如下:

果然发现第二个字典中,出现了多个值为的,很有可能就是我们要找的点。那么这个字典是从哪里来的呢?想起当时打印类的调用顺序的时候,发现的一个函数,它返回的就是一个这样的字典,那么我们一下方法,过滤掉这些值为的看看:

打包,安装后发现,并没有修复这个bug,还是报找不到方法的日志。看来我们还没有找到 bug 点。

3.获得 block 的具体实现过程

在第二个回调上下断点,触发后,进入实现内部,根据lldb打印出实现内部的每个方法的方法调用者和函数名、参数值。过程如下(主要是找到了这个的实现地址后,在Hopper中找到这个代码块,可以发现对于这个 block 的内部方法,Hopper是没有注释调用者、selector、参数的,我们可以对Hopper中的这些代码中的所有显示为的地方下一个断点,一一触发,分别打印每个方法的调用者和调用方法,传递参数等信息),主要过程如下:

解释后主要是以下过程:

可以看到,其中也有一个,打印一下block的内容,根据image的偏移得到block实现的地址:,在Hopper中找到:

在这个block上下断点,运行后来到该断点出:进入该实现内部,断点定位到如图中的唯一一个方法上,获取其调用信息:

可以看到该方法中还有一个参数,继续打印:

可以发现得到的:
,。
和我们得到的第一个是同一个。

既然第二个参数还没有分析完,第一个已经迫不及待的回来了,那么我们先在Hopper中查看一下这个block的实现:

分析到这里后app不小心退出了,那么我们在这里重新启动,既然已经知道这个的地址,可以直接通过查看内的每个的地址下断点:


可以知道该block的调用如下:

既然分析到了这里,程序还没有崩溃,那么说程序这之前都没有问题,那么问题可能出在了这个中,那么我们打印一下这个:

得到:,。

那么我们继续深入,打印一下这个的实现,步骤如上,如果不想每个都手动打断点,可以在这个上打断点,然后,进入后,一步一步向下执行,等到运行到的时候,打印这个方法的内容。但是在这里下到的断点都没有执行程序就已经崩溃了!!!说明什么?说明这个传入的还没有执行,程序已经crash了。

  • 从正向开发的角度,传入这个,可能是在程序满足一定条件的时候才会回调这个,来传递信息或处理一些事情。
  • 那么程序应该是崩溃在这个被执行前,我们需要查看一下这个函数的内部实现。
  • 可能后知后觉了,现在仔细想想,我前面说的分析到这个函数时,使用下一步后,程序不小心退出了!!,所以应该不是不小心退出了,而是这个函数的内部实现导致了崩溃。
确定bug点

分析到了这个方法,那么我们去中看看它的内部实现:

果然,如图看到的,我们看到了之前在控制台输出的崩溃的关键词,既然找到了这里,我们利用这个调用函数的地址:来下一个断点,看看调用者是谁,是不是真的是null,从而导致的bug。

重新启动,lldb下断点:

如图,调用者为null,并且后,完美的崩溃了。文章写了这么多,终于找到这个bug点了!!!激动啊。

找到了bug点,那么我们应该研究一下如何修复这个bug,首先我们需要知道为什么开发者会用这个null,调用了length,程序发生了什么导致了这个调用者为。

我们来仔细看一下,这个函数调用所在的代码块的汇编代码:

可以看到,。这个null是从加载的。但是我们在这个代码块中(包括整个方法内)都没有找到。这个情况就比较特殊了,自从学习逆向以来,还是第一次碰到这种情况。该调用者是从一个从来没有被写入过内容的寄存器中读取的。应该也是因为这个原因才导致读取的内容为null吧。

换个思路,我们结合上下文找线索。我们翻译一下上下文方法调用的汇编代码:

我们可以使用来打印一下这个是什么:

然后顺便把汇编中的内容,打印一下:

果然,其中有一个打印是。而且正好出现在调用的上方:

所以很有可能,这个bug是因为通过:从中获取了一个没有的值,并对他调用了导致的。

那么我们通过创建一个插件,然后一下这个方法,返回一个我们设置了值的对象,

安装后,再次登陆发现登陆成功,并没有,终于修复了这个bug,发现已经写了不少字了。

最后的修复bug的可以在这里下载XituHook。

其实在我自己分析的时候,分析的内容还要多,也比较杂。逆向分析正是这样,我们需要顺着程序执行的顺序分析,但是事情往往不如我们想的这样简单,分析的岔路很多,特别是这里,涉及到了多个的回调,需要对的实现原理及其内存结果比较熟悉才行。

在分析的过程中,分析到的还有一些文中没有写到的和方法,并且在其中发现了关键词,激动不已啊,但是当我深入分析的时候发现,开发者都做了防护

预防出现NSNull的情况,所以都不是 的罪魁祸首。我猜测是获取的 github 上的某个个人信息(根据名字推测是自我介绍?)。如果想分析的话也可以,需要从这个类如果获得这些信息开始分析。但是我想本文分享到这里已经差不多了。

求工作,求工作,求工作,重要的事情说三遍。现在的iOS就业形势,不提也罢,兴趣所致,跪着走完。

copyright © 2024 powered by 带水拖泥网   sitemap