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

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

故事概要

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

跟以前写《逆向及修复最新iOS版少数派客户端的闪退bug》文章时不一样,在文章写到这里的时候,我其实尚未彻底分析完崩溃的缘由。我是分析途中决定开始写这篇文章的,决定一边分析一遍写,这样才会尽量的保留分析的全过程,固然最后能不能分析到关键点,且看下去吧,由于我也不知道。ios

问题描述

  • 最新版 4.1.3 稀土掘金 app 在使用 github 登录成功后会 crash
  • 是在登录成功后才 crash,由于再次进入后会发现已经登录成功
  • 可能不是全部人都会遇到这个bug,根据后来的分析,多是Github的某个我的信息中缺乏某些设置致使的受权后crash

着手分析

###找到崩溃缘由
分析一个bug的开始,固然是从崩溃缘由找起,前一篇文章,咱们使用了lldb调试,在触发 app 崩溃的时候,从 Terminal 找到了关键词:EXE_BAD_ACCESS。此次,咱们使用最简单的,查看系统日志来定位崩溃缘由,使用最新的 Mac,打开控制台,链接手机并清空多余的系统日志打印,而后触发崩溃,观察控制台中输出的内容(若是输出日志过多,可使用进程名Xitu做为关键词,进行过滤)。若是能够仍是不要使用关键词过滤,由于不是全部的崩溃都是因为 app 自己的问题致使的,正如签名破坏的 app 在没有越狱的手机上会安装失败,这个安装失败的缘由查找就须要在日志中的 SpringBoard 进程打印中进行查找
SpringBoardApple用来管理咱们iPhone应用的一个应用,能够理解为咱们 PC 端的桌面,它还负责应用的桌面排列、管理通知中心等)。git

正如咱们但愿的同样,崩溃的缘由最多见不过了:
github

这个崩溃就颇有意思了,开发者对NSNull调用了length方法,由于找不到该方法而崩溃!固然开发者确定是不会主动对NSNull类型调用length方法,猜测:swift

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

大胆猜想:结合这个崩溃是发生在登录的时候,须要从网络获取登录信息,那么会不会是程序在对获取到的登录信息进行处理的时候致使的崩溃(好比登录用户的一些信息没有设置,因此程序从登录信息里取到的是空值)。网络

分析入口

1.找到登录界面的控制类


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

susnms-iPhone:~ root# ps -e | grep Xitu
24240 ??         0:00.00 (Xitu.Widget)
24241 ??         0:00.00 (XituShare)
24242 ??         0:00.00 (Xitu)
24283 ??         0:02.03 /var/containers/Bundle/Application/994C4217-88DD-4F55-A016-55BDD5998C49/Xitu.app/Xitu
24288 ttys000    0:00.01 grep Xitu
susnms-iPhone:~ root# cycript -p 24283 common.cy ; cycript -p 24283
cy# currentVCWithKeyWindow ()
#"<XTGithubLoginViewController: 0x15e0ef340>"
cy#复制代码

currentVCWithKeyWindow() 来自我本身写的一个脚本,你能够前往common.cy下载。下载以后,将其放到/var/root目录下便可。
使用时,只须要在cycript注入的时候加上便可。
其中还有一些好用的函数,好比快速打印UIControl全部的targetactionapp

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

使用class-dump等工具,导出app的全部类头文件,使用logify.pl工具生成XTGithubLoginViewController类的全部hook函数,使用theos编写安装插件后,触发崩溃,而后在控制台查找该类的函数调用逻辑。以下:
函数

能够发现,程序是在-[XTGithubLoginViewController viewWillDisappear:]调用以后才收到的崩溃的通知。因此颇有可能崩溃的缘由不是XTGithubLoginViewController致使的,而是在XTGithubLoginViewController类返回登录信息后。工具

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

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

callback!太熟悉不过了,这不就是咱们常常在开发中使用的设置回调block的时候使用的参数名吗!!而且,该callback在类初始化的时候被设置,类方法-(id)onAuthCompleted:调用以后才被回调。结合这个方法名onAuthCompleted,这个逻辑是否是很像:咱们在登录成功以后,使用block传回了咱们的登录信息进行处理。

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

继续深刻callback的回调过程

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

使用cycript注入打印该控制器类名,并尝试github的受权登陆入口:

susnms-iPhone:~ root# cycript -p Xitu common.cy ; cycript -p Xitu
cy# currentVCWithKeyWindow ()
#"<XTLoginViewController: 0x12e43f740>"
cy# [#0x12e43f740 github
githubBt      githubLogin:
cy# [#0x12e43f740 githubLogin: nil]复制代码

发现成功触发登陆,因此能够肯定githubLogin:方法确实是登陆的入口

1.githubLogin:实现

Hopper中查看该方法的实现:

-[XTLoginViewController githubLogin:]:
0000000100194c24         sub        sp, sp, #0x50                               ; Objective C Implementation defined at 0x1009d2708 (instance method), DATA XREF=0x1009d2708
0000000100194c28         stp        x20, x19, [sp, #0x30]
0000000100194c2c         stp        x29, x30, [sp, #0x40]
0000000100194c30         add        x29, sp, #0x40
0000000100194c34         mov        x19, x0
0000000100194c38         adrp       x8, #0x100ab4000                            ; @selector(detectSocketStatus)
0000000100194c3c         ldr        x0, [x8, #0x878]                            ; objc_cls_ref_LoginCloud,__objc_class_LoginCloud_class
0000000100194c40         adrp       x8, #0x100aa0000                            ; @selector(computeTime:)
0000000100194c44         ldr        x1, [x8, #0xd0]                             ; "singleClass",@selector(singleClass)
0000000100194c48         bl         imp___stubs__objc_msgSend                   ; login = [LoginCloud singleClass]
0000000100194c4c         mov        x29, x29
0000000100194c50         bl         imp___stubs__objc_retainAutoreleasedReturnValue
0000000100194c54         mov        x20, x0
0000000100194c58         adrp       x8, #0x100914000
0000000100194c5c         ldr        x8, [x8, #0x448]                            ; __NSConcreteStackBlock_100914448,__NSConcreteStackBlock
0000000100194c60         str        x8, [sp, #0x8]
0000000100194c64         movz       w8, #0xc200
0000000100194c68         stp        w8, wzr, [sp, #0x10]
0000000100194c6c         adr        x8, #0x100194cc4
0000000100194c70         nop
0000000100194c74         str        x8, [sp, #0x18]
0000000100194c78         adrp       x8, #0x100923000
0000000100194c7c         add        x8, x8, #0x9d0                              ; 0x1009239d0
0000000100194c80         str        x8, [sp, #0x20]
0000000100194c84         mov        x0, x19
0000000100194c88         bl         imp___stubs__objc_retain
0000000100194c8c         str        x0, [sp, #0x28]
0000000100194c90         adrp       x8, #0x100aa7000                            ; @selector(isTableViewScrolledToBottom)
0000000100194c94         ldr        x1, [x8, #0x848]                            ; "githubLoginCallback:",@selector(githubLoginCallback:)
0000000100194c98         add        x2, sp, #0x8
0000000100194c9c         mov        x0, x20
0000000100194ca0         bl         imp___stubs__objc_msgSend                   ; [login githubLoginCallback: block]
0000000100194ca4         mov        x0, x20
0000000100194ca8         bl         imp___stubs__objc_release
0000000100194cac         ldr        x0, [sp, #0x28]
0000000100194cb0         bl         imp___stubs__objc_release
0000000100194cb4         ldp        x29, x30, [sp, #0x40]
0000000100194cb8         ldp        x20, x19, [sp, #0x30]
0000000100194cbc         add        sp, sp, #0x50
0000000100194cc0         ret复制代码

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

LoginCloud *login = [LoginCloud singleClass];
[login githubLoginCallBack: callback];复制代码

2.[LoginCloud singleClass]实现

Hopper查看以下:

-[LoginCloud githubLoginCallback:]:
sub        sp, sp, #0x80 ; Objective C Implementation defined at 0x1009c7cc8 (instance method), DATA XREF=0x1009c7cc8
stp        x26, x25, [sp, #0x30]
stp        x24, x23, [sp, #0x40]
stp        x22, x21, [sp, #0x50]
stp        x20, x19, [sp, #0x60]
stp        x29, x30, [sp, #0x70]
add        x29, sp, #0x70
mov        x22, x0
mov        x0, x2
bl         imp___stubs__objc_retain
mov        x21, x0
adrp       x8, #0x100ab4000 ; @selector(detectSocketStatus)
ldr        x0, [x8, #0x6a0] ; objc_cls_ref_UIStoryboard,_OBJC_CLASS_$_UIStoryboard
adrp       x8, #0x100aa0000 ; @selector(computeTime:)
ldr        x1, [x8, #0x788] ; "storyboardWithName:bundle:",@selector(storyboardWithName:bundle:)
adrp       x2, #0x100949000 ; @"%@ %@ %@"
add        x2, x2, #0x500 ; @"Login"
movz       x3, #0x0
bl         imp___stubs__objc_msgSend ; loginSb = [UIStoryboard storyboardWithName: @"Login" bundle: nil]
mov        x29, x29
bl         imp___stubs__objc_retainAutoreleasedReturnValue
mov        x19, x0
adrp       x8, #0x100aa0000 ; @selector(computeTime:)
ldr        x1, [x8, #0x790] ; "instantiateViewControllerWithIdentifier:",@selector(instantiateViewControllerWithIdentifier:)
adrp       x2, #0x10094f000 ; @"nickname"
add        x2, x2, #0xd00 ; @"githubVC"
bl         imp___stubs__objc_msgSend ; githubVC = [loginSb instantiateViewControllerWithIdentifier: @"githubVC"]
mov        x29, x29
bl         imp___stubs__objc_retainAutoreleasedReturnValue
mov        x20, x0
adrp       x8, #0x100914000
ldr        x8, [x8, #0x448] ; __NSConcreteStackBlock_100914448,__NSConcreteStackBlock
str        x8, sp
movz       w8, #0xc200
stp        w8, wzr, [sp, #0x8]
adr        x8, #0x10015ccbc
nop
str        x8, [sp, #0x10]
adrp       x8, #0x100921000
add        x8, x8, #0x6f0 ; 0x1009216f0
stp        x8, x21, [sp, #0x18]
mov        x0, x21
bl         imp___stubs__objc_retain
mov        x21, x0
mov        x0, x22
bl         imp___stubs__objc_retain
str        x0, [sp, #0x28]
adrp       x8, #0x100aa6000 ; @selector(hasText)
ldr        x1, [x8, #0x440] ; "setCallBack:",@selector(setCallBack:)
mov        x2, sp
mov        x0, x20      ; [githubVC setCallBack: black]
bl         imp___stubs__objc_msgSend
adrp       x8, #0x100ab4000 ; @selector(detectSocketStatus)
ldr        x0, [x8, #0x938] ; objc_cls_ref_UINavigationController,_OBJC_CLASS_$_UINavigationController
adrp       x8, #0x100a9f000
ldr        x1, [x8, #0xd78] ; "alloc",@selector(alloc)
bl         imp___stubs__objc_msgSend
adrp       x8, #0x100aa1000 ; @selector(setUpdatedAt:)
ldr        x1, [x8, #0x948] ; "initWithRootViewController:",@selector(initWithRootViewController:)
mov        x2, x20
bl         imp___stubs__objc_msgSend
mov        x22, x0
adrp       x8, #0x100ab4000 ; @selector(detectSocketStatus)
ldr        x0, [x8, #0x820] ; objc_cls_ref_UIApplication,_OBJC_CLASS_$_UIApplication
adrp       x8, #0x100aa1000 ; @selector(setUpdatedAt:)
ldr        x1, [x8, #0x2d8] ; "sharedApplication",@selector(sharedApplication)
bl         imp___stubs__objc_msgSend
mov        x29, x29
bl         imp___stubs__objc_retainAutoreleasedReturnValue
mov        x23, x0
adrp       x8, #0x100a9f000
ldr        x1, [x8, #0xf88] ; "delegate",@selector(delegate)
bl         imp___stubs__objc_msgSend
mov        x29, x29
bl         imp___stubs__objc_retainAutoreleasedReturnValue
mov        x24, x0
adrp       x8, #0x100aa1000 ; @selector(setUpdatedAt:)
ldr        x1, [x8, #0x930] ; "window",@selector(window)
bl         imp___stubs__objc_msgSend
mov        x29, x29
bl         imp___stubs__objc_retainAutoreleasedReturnValue
mov        x25, x0
adrp       x8, #0x100aa1000 ; @selector(setUpdatedAt:)
ldr        x1, [x8, #0x938] ; "rootViewController",@selector(rootViewController)
bl         imp___stubs__objc_msgSend
mov        x29, x29
bl         imp___stubs__objc_retainAutoreleasedReturnValue
mov        x26, x0
mov        x0, x25
bl         imp___stubs__objc_release
mov        x0, x24
bl         imp___stubs__objc_release
mov        x0, x23
bl         imp___stubs__objc_release
adrp       x8, #0x100aa0000 ; @selector(computeTime:)
ldr        x1, [x8, #0x220] ; "presentViewController:animated:completion:",@selector(presentViewController:animated:completion:)
orr        w3, wzr, #0x1
mov        x0, x26
mov        x2, x22
movz       x4, #0x0
bl         imp___stubs__objc_msgSend
mov        x0, x26
bl         imp___stubs__objc_release
mov        x0, x22
bl         imp___stubs__objc_release
ldr        x0, [sp, #0x28]
bl         imp___stubs__objc_release
ldr        x0, [sp, #0x20]
bl         imp___stubs__objc_release
mov        x0, x21
bl         imp___stubs__objc_release
mov        x0, x20
bl         imp___stubs__objc_release
mov        x0, x19
bl         imp___stubs__objc_release
ldp        x29, x30, [sp, #0x70]
ldp        x20, x19, [sp, #0x60]
ldp        x22, x21, [sp, #0x50]
ldp        x24, x23, [sp, #0x40]
ldp        x26, x25, [sp, #0x30]
add        sp, sp, #0x80
ret复制代码

解释以下:

UIStoryboard *loginSb = [UIStoryboard storyboardWithName: @"Login" bundle: nil]
UIViewController *githubVC = [loginSb instantiateViewControllerWithIdentifier: @"githubVC"]
[githubVC setCallBack: black]
UINavigatinController *nav = [[UINavigatinController alloc] initWithRootViewControler: githubVC];
UIViewController *vc = [UIApplication sharedApplication].delegate.window.rootViewController;
[vc presentViewController: nav animaed: YES completion: nil];复制代码
  • 初始化UIStoryBoard,并从中初始化控制器githubVC
  • githubVC控制器设置回调block(另外一个callback参数回调)
  • 显示控制器

其实这里的githubVC应该就是XTGithubLoginViewController类型,咱们可使用cycript确认一下:

cy# var sb = [UIStoryboard storyboardWithName: @"Login" bundle: nil]
#"<UIStoryboard: 0x12e0ebf00>"
cy# [#0x12e0ebf00 instantiateViewControllerWithIdentifier: @"githubVC"]
#"<XTGithubLoginViewController: 0x12e382e00>"复制代码

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

分析callback实现

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

1.获取block的函数地址

由于咱们知道这个callback做为参数传给了-[LoginCloud githubLoginCallback:]方法,因此lldb链接服务(如何lldb能够看个人第一篇文章)后,咱们在这个方法上打个断点,同理设置-[XTGithubLoginViewController setCallBack:]断点。
根据Block的内存结构能够查找block的函数实现地址和参数返回值类型,具体能够看这篇文章。开源就是好,节省步骤,咱们可使用facebook开源的chisel。触发断点后,获取到block的内存地址后,使用pblock打印block:

获取到第一个 block 的函数实现地址:0x0000000100194cc4
参数及返回值:void ^(bool, NSString *)

获取到第二个 block 的函数实现地址:0x000000010015ccbc
参数及返回值:void ^(NSDictionary *, ThirdLoginModel *, NSDictionary *)

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

知道了 block 的参数的类型,那么咱们能够写个 tweakshook 这个 block 而后打印一下,传递的这些参数内容,根据回传顺序,咱们先打印第二个block

%hook XTGithubLoginViewController

-(callBackType)callBack {
  callBackType block = ^(NSDictionary *dic1, ThirdLoginModel *model, NSDictionary *dic2) {
    HBLogInfo(@"dic1: %@, model: %@, dic2: %@", dic1, [model debugDescription], dic2);
  };
  return block;
}
%end // end hook复制代码

打印结果以下:

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

-(NSDictionary *)onAuthCompleted:(id)arg1 {
  HBLogInfo(@"%s", __func__);
  NSDictionary *result = %orig;
  NSMutableDictionary *dict = [result mutableCopy];
  for (NSString *key in result.allKeys) {
    if ([result[key] isKindOfClass:[NSNull class]]) {
      HBLogInfo(@"%s key: %@", __func__, key);
      [dict removeObjectForKey:key];
    }
  }
  HBLogInfo(@"result: %@", dict);
  return dict;
}复制代码

打包,安装后发现,并无修复这个bug,仍是报找不到length方法的日志。看来咱们尚未找到 bug 点。

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

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

解释后主要是如下过程:

// swift class
ZEHud *hud = [Xitu.ZEHud sharedInstance];
[hud showHud];
[AVUser loginWithAuthData:  dict platform: @"github" block: block] // block Imp: 0x000000010015cdf8 Signature: void ^(AVUser *, NSError *);复制代码

能够看到,其中也有一个block,打印一下block的内容,根据Xituimage的偏移获得block实现的地址:0x100208df8-0x00000000000ac000=0x10015CDF8,在Hopper中找到:

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

能够看到该方法-[LoginCloud thirdLogRefreshCurrentUserDatatype:ThirdData:Error:CallBack:]中还有一个block参数,继续打印:

能够发现获得的block:
0x0000000100194cc4,void ^(bool, NSString *)
和咱们获得的第一个block是同一个。

既然第二个block参数尚未分析完,第一个block已经火烧眉毛的回来了,那么咱们先在Hopper中查看一下这个block的实现:

分析到这里ni后app不当心退出了,那么咱们在这里从新启动,既然已经知道这个block的地址,能够直接经过查看Hopper内的每一个objc_msgSend的地址下断点:


能够知道该block的调用以下:

login = [LoginCloud singleClass];
[login refreshGithubLogin];
[XTLoginViewController callback];复制代码

既然分析到了这里,程序尚未崩溃,那么说程序这以前都没有问题,那么问题可能出在了XTLoginViewController这个callback中,那么咱们打印一下这个block

获得:0x00000001001935d0,void ^(bool, NSString *)

那么咱们继续深刻,打印一下这个block的实现,步骤如上,若是不想每一个objc_msgSend都手动打断点,能够在这个block上打断点,而后si,进入后,一步一步向下执行,等到运行到objc_msgSend的时候,打印这个方法的内容。可是在这里下到的断点都没有执行程序就已经崩溃了!!!说明什么?说明这个传入的callback尚未执行,程序已经crash了。

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

肯定bug点

分析到了这个方法-[LoginCloud thirdLogRefreshCurrentUserDatatype:ThirdData:Error:CallBack:],那么咱们去Hopper中看看它的内部实现:

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

从新启动,lldb下断点:

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

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

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

loc_10015d1a8:
add        x8, sp, #0xb0 ; CODE XREF=-[LoginCloud thirdLogRefreshCurrentUserDatatype:ThirdData:Error:CallBack:]+660
stp        xzr, x8, [sp, #0xb0]
orr        w8, wzr, #0x30
stp        w28, w8, [sp, #0xc0]
adr        x8, #0x10015d84c
nop
fmov       d0, x8
adr        x8, #0x10015d85c
nop
ins        v0, x8
add        x8, sp, #0xb0
stur       q0, [x8, #0x18]
ldr        x0, [x25, #0x618]
mov        x24, x27
mov        x1, x24
bl         imp___stubs__objc_msgSend
mov        x29, x29
bl         imp___stubs__objc_retainAutoreleasedReturnValue
mov        x23, x0
adrp       x2, #0x10094b000 ; @"Z"
add        x2, x2, #0xc60 ; @"self_description"
mov        x1, x26
bl         imp___stubs__objc_msgSend
mov        x29, x29
bl         imp___stubs__objc_retainAutoreleasedReturnValue
str        x0, [sp, #0xd8]
mov        x0, x23
bl         imp___stubs__objc_release
ldr        x8, [sp, #0xb8]
ldr        x0, [x8, #0x28]
adrp       x8, #0x100aa0000 ; @selector(computeTime:)
ldr        x1, [x8, #0x340] ; "length",@selector(length)
bl         imp___stubs__objc_msgSend
ldr        x20, [sp, #0x18]
cmp        x0, #0x24
b.lo       loc_10015d24c复制代码

能够看到,ldr x0, [x8, #0x28]。这个null是从[x8, #0x28]加载的。可是咱们在这个代码块中(包括整个-[LoginCloud thirdLogRefreshCurrentUserDatatype:ThirdData:Error:CallBack:]方法内)都没有找到。这个状况就比较特殊了,自从学习逆向以来,仍是第一次碰到这种状况。该调用者是从一个历来没有被写入过内容的寄存器中读取的。应该也是由于这个缘由才致使读取的内容为null吧。

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

AVUser *user = [AVUser currentUser];
NSString *name = [user valueForKey: @"username"];
NSString *headImg = [user ValueForKey: @"avatar_large"];
NSString *descrip = [user ValueForKey: @"self.description"];
//...复制代码

咱们可使用cycript来打印一下这个user是什么:

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

果真,其中有一个打印是null。并且正好出如今length调用的上方:

因此颇有可能,这个bug是由于经过keyself.descriptionuser中获取了一个没有的值,并对他调用了length致使的。

那么咱们经过theos建立一个插件,而后hook一下这个方法,返回一个咱们设置了值的AVUser对象,

%hook AVUser
+(id)currentUser {
  AVUser *user = %orig;
  id descri = [user valueForKey: @"self_description"];
  if ([descri isKindOfClass: [NSNull class]]) {
    HBLogWarn(@"the value for key: self_description is null");
    [user updateValue: @"" forKey: @"self_description"];
  }
  return user;
}

%end复制代码

安装后,再次登录发现登录成功,并无crash,终于修复了这个bug,发现已经写了很多字了。

总结

最后的修复bug的tweaks能够在这里下载XituHook

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

在分析的过程当中,分析到的还有一些文中没有写到的block和方法,而且在其中发现了length关键词,激动不已啊,可是当我深刻分析的时候发现,开发者都作了防御

if ([obj isKindOfClass:[NSNull class]]) {
    obj = @"";
}复制代码

预防出现NSNull的状况,因此都不是 crash 的罪魁祸首。我猜想self_description是获取的 github 上的某个我的信息(根据名字推测是自我介绍?)。若是想分析的话也能够,须要从AVUser这个类若是得到这些信息开始分析。可是我想本文分享到这里已经差很少了。

写在最后

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

相关文章
相关标签/搜索