FastHook——远超YAHFA的优异稳定性

FastHook在实际应用中表现出优于YAHFA的稳定性,无需备份原方法,减少了潜在问题。文章详细介绍了FastHook的先天优势和出色细节处理,如JIT状态检查、线程状态恢复等,确保了框架的高效和安全性。通过与YAHFA的对比,突显了FastHook在Android Hook领域的优势。
摘要由CSDN通过智能技术生成

一、 概述

经过实际项目大量测试验证,FastHook表现出了远超YAHFA的优异稳定性。用户反馈未出现Hook引发的稳定性问题、压力测试也未发生Hook引发的稳定问题。之所以FastHook拥有优异的稳定性,除了框架实现原理的优越性之外,还得益于FastHook出色的细节处理
本文将通过FastHook实现原理优越性与一些出色的细节处理来解释为何FastHook拥有优异的稳定性,最后对比YAHFA框架。

二、先天优势

如果你还未了解FastHook,请移步FastHook——一种高效稳定、简洁易用的Android Hook框架
FastHook相较YAHFA框架原理上最大的优势、也是最大的亮点便是:不需要备份原方法!不需要备份原方法!不需要备份原方法!
科学上有一个著名的“奥卡姆剃刀定律”,什么意思呢?如果一个现象有两个或者多个不同的理论解释,那么选最简单的那个。做Hook框架,也可以用剃刀定律来做指导:实现相同的功能,选对系统状态改动最小的
“备份原方法”是一种隐患颇多的方式,引发了诸如方法解析出错、Moving GC空指针等问题。尽管其他框架通过一些手段来提高稳定性,比如保证方法不被再次解析、检查Moving GC是否移动了原方法相关对象等,但是这些都不是理论安全的,就像地上有个坑,你不去补上,而是让人不要去踩
反观FastHook,Hook时对系统原有状态的改变是最小的。

  1. Inline模式改变的仅是几个字节的指令,因平台而异,不篡改任何方法。
  2. EntryPoint模式替换了方法EntryPoint,但是原方法将强制为解释执行,也可等价的看为未做修改。

简而言之,FastHook就是用Hook方法hook原方法,原方法hook Forward方法来实现最小改动hook。完美地从实现层面解决了YAHFA框架不能解决的问题,而且无需做一些其他操作,YAHFA框架都需要一些其他的操作来提高稳定性,而FastHook不需要做任何其他处理,更简洁、更优雅

三、比其他框架更出色的细节处理

3.1 JIT状态检查

如果你看过YAHFA框架代码,你会发现没有一个框架做了JIT状态检查。JIT状态检查的目的是为了保证hook的安全性,但这也不是理论安全的,也无法做到理论安全。这是为什么呢?

3.1.1 Inline模式

如果原方法未编译则需要进行手动JIT编译。那么问题来了,什么时候编译才是安全的呢。下面列举出所有可能出现的情景:

  1. 原方法未进行JIT编译,此时手动JIT编译时安全的
  2. 原方法未进行JIT编译,即将进入编译等待队列或已进入编译等待队列,此时手动JIT编译是不安全的
  3. 原方法正在JIT编译,此时手动JIT编译是不安全的
  4. 原方法编译完成,此时手动编译是安全的

上述4中情景,其中2、3是不安全的。如果要保证手动JIT编译的安全性,必须做到以下两点:

  1. 禁止JIT编译,防止从1变化到2
  2. 能够判断2、3,当处于2、3状态时,等待其变化到4

现在来看看FastHook到底是怎么处理的

int CheckJitState(JNIEnv *env, jclass clazz, jobject target_method) {
    void *art_method = (void *)(*env)->FromReflectedMethod(env, target_method);
    //添加kAccCompileDontBother,禁止JIT、AOT编译
    AddArtMethodAccessFlag(art_method, kAccCompileDontBother);
    uint32_t hotness_count = GetArtMethodHotnessCount(art_method);
    if(hotness_count >= kHotMethodThreshold) {
        //hotness_count >= hot_threshold,肯定就不是1了,看看是2、3、4中的哪一个
        long entry_point = (long)GetArtMethodEntryPoint(art_method);
        if((void *)entry_point == art_quick_to_interpreter_bridge_) {
            void *profiling = GetArtMethodProfilingInfo(art_method);
            void *save_entry_point = GetProfilingSaveEntryPoint(profiling);
            if(save_entry_point) {
                //JIT垃圾回收会改变方法EntryPoint,虽然方法已经编译了,但是EntryPoint也可能是art_quick_to_interpreter_bridge
                return kCompile;
            }else {
                //JIT状态保存在profiling中,通过其来判断是否是正在编译,如果不是可能是正在等待或者已经编译失败。
                bool being_compiled = GetProfilingCompileState(profiling);
                if(being_compiled) {
                    return kCompiling;
                }else {
                    return kCompilingOrFailed;
                }
            }
        }
        return kCompile;
    }else {
        //hotness_count < hot_threshold,可能是1,也可能是2,即将进入编译等待队列,统一加一个增量,如果此时大于hot_threshold,就认为是2,反之是1
        uint32_t assumed_hotness_count = hotness_count + kHotMethodMaxCount;
        if(assumed_hotness_count > kHotMethodThreshold) {
            return kCompiling;
        }
    }
    return kNone;
}
class ProfilingInfo {
 private:
  ProfilingInfo(ArtMethod* method, const std::vector<uint32_t>& entries);

  // Number of instructions we are profi
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值