iOS 杂谈DevSupportiOS知识搜集

iOS逆向之Hopper进阶

2017-12-06  本文已影响171人  店长推荐

线上发现了一个bug,需要用自产的热补丁对 Bugly 的一个方法进行替换,改变返回值

/**
 *  App 是否发生了连续闪退
 *  如果启动SDK 且 5秒内 闪退,且次数达到 3次 则判定为连续闪退
 *
 *  @return 是否连续闪退
 */
+ (BOOL)isAppCrashedOnStartUpExceedTheLimit;

可是重试了很多次发现热补丁都没生效。。。

后面发现我们调用该 Bugly 方法过早,热补丁又生效过迟,所以热补丁一直失效!

没办法直接拦截 [Bugly isAppCrashedOnStartUpExceedTheLimit] 返回 YES。 那我们只能在闪退次数累加的时候进行拦截了,不让它次数累加起来

按照 bugly的注释 次数累加是在应用闪退的时候进行的,这个时候我们热补丁已经加载,可以发挥作用了。 可是我们怎么知道 Bugly 怎么累加闪退次数的呢?

Hopper 登场

之前有简略介绍过 Hopper 逆向的使用,但是找不到文章了。。。

找个项目=> Bugly SDK 丢进去 => 编译,由于是debug包,所以可以不用脱壳,直接找到生成的 app 文件,拖到 hopper 工程窗口中

等 Hopper 解析完,直接输入 Bugly 方法名,找到对应实现的汇编段, 点击 右上角的 if(b)f(x) 按钮。 hopper 会自动帮我们解析为 OC 代码。

其实可以看到 Bugly 只是个壳,具体逻辑是在 BLYSDKManager 类中实现的, 我们继续跟进

然后发现 [BLYSDKManager isAppCrashedOnStartUpExceedTheLimit] 也是个壳, 是直接返回 crashedOverFlow 方法值。

分析 crashedOverFlow 实现,发现其实是个 get 方法, 直接返回了 bool 类型的 _crashedOverFlow 变量,BLYSDKManager 也实现了该变量的 set 方法。

所以我们重点就变为了,哪里调用 setCrashedOverFlow: 方法??

看上图,其实直接搜关键字是搜不到调用方的,这个时候需要用到 strings tab

这个 strings 表示搜文本区

因为 OC 以发消息的方式进行方法调用,方法名都会以 strings的方式在常量区内。 后续的 XREF 表示引用到该变量的地方, 可直接点击跳转。

我们跟过去看看

发现只有在 -[BLYSDKManager startWithAppId:developmentDevice:config:]+249 这一个地方调用了该方法

我们过去看看该方法的实现

发现 Bugly 内部的次数累加是放在 NSUserDefaults 里面的。 key 名也找到了。 条件确实是 超过3次,就设置闪退标示位 为 true

但是哪里对这个值进行累加呢?

所以我们需要找哪里使用了这个 com.tencent.bugly.exitedonstartUplimitkey 字符串

方法1: 直接读汇编,名字太明显了

方法2: 还是通过 strings tab 进行搜索

引用区太长 直接通过代码来看吧

0000000100eff3f8         dq         0x0000000100f58d60 ; @"com.tencent.bugly.exitedonstartUplimitkey", XREF=
-[BLYSDKManager startWithAppId:developmentDevice:config:]+202,
-[BLYSDKManager startWithAppId:developmentDevice:config:]_block_invoke+49,
-[BLYAnalyticsManager willTerminateHandler]+72

可以看到 就3个地方有用到该变量。。 我们排查量就小了

    [BLYAnalyticsManager willTerminateHandler] 是把累加次数重置0,可以排除
    看方法名,应该是收到 系统 willTerminate 通知的时候调用,就是应用被正常终止时会调用
-[BLYSDKManager startWithAppId:developmentDevice:config:]+202,
-[BLYSDKManager startWithAppId:developmentDevice:config:]_block_invoke+49,

有两个区域引用,一个在 block 内,内部逻辑是 重置为0, 可以排除。

外部区域也有两个地方使用,一个 get,一个 set, 那真正累加的代码就是 set 那块的逻辑了

后面就是还原 bugly 内部逻辑了, OC 伪代码

- (void) startWithAppId:developmentDevice:config: {
    int count = [NSUserDefaults integerForKey:@"LimitKey"];
    if (count >= 3) {
        // 累加次数 大等于3,就设置连续闪退成立
        // isAppCrashedOnStartUpExceedTheLimit 方法返回 YES
        [self setCrashedOverFlow:YES];
    }
    // 对闪退次数进行累加, 并保存回 NSUserDefaults
    count += 1;
    [NSUserDefaults setInteger:count forKey:@"LimitKey"];
    
    // 如果5秒后 程序还在执行,把闪退次数 重置为 0
    dispatch_after(5, ^{
        [NSUserDefaults setInteger:0 forKey:@"LimitKey"]; 
    });
    ...
}

其实可以看到 bugly 整个累加的逻辑,现有的热补丁是没法插手的 。。。

而且 bugly 并不是在闪退的时候对次数进行累加,而是在一启动的时候就累加,只是 5秒后 或者 系统调用了 willTerminate 通知, 然后把次数重置为 0。

整体逻辑符合 bugly 自己的注释。。。

题外话

怎么判断是 5秒 过后?

    dispatch_time(0x0, 0x12a05f200)
    
    0x12a05f200 = 5000000000

    #define DISPATCH_TIME_NOW (0ull)
    #define NSEC_PER_SEC 1000000000ull

    dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)    
上一篇下一篇

猜你喜欢

热点阅读