敞开生长之旅!这是我参与「日新方案 2 月更文应战」的第 21 天,点击检查活动详情

前语

温馨提示,本文或许比较合适有必定根底的朋友阅览。为什么要提这个问题呢,由于平常多多少少也用到“我所谓的hook”的概念去处理问题,可是这个思维真的真的是hook这个概念的思维吗?我做的操作,我所称之为hook,那它真的能叫hook吗,如果不是,那又该称为什么?

什么是hook

什么是hook? hook是钩子,这我知道,就算英语不好的人拿个翻译也知道是这意思,关键它想表达什么思维,它被提出是为了处理什么问题。前端有 hook这个概念,Android也有,windows也有。很多人有告诉你详细怎样操作,但却没给你讲清楚它是什么?它是为了处理什么问题被提出的?是哪个逼先提出的?这些我都找不到。但如同是在windows里边最早提出这个概念的。

我的了解是,给一个已知的流程中添加特定的代码。“添加”这个词让人觉得怪怪的,换成“给一个已知的流程中hook特定的代码”,你别把hook想成钩子,把他当成一个特定的行为。

我举一个很凶恶的比方,经过高考,一所大学现已录取了小明,然后大帅成果不好,落选了,但他想上这所大学,所以把自己的信息替换成小明的信息。(这是违法行为,十分不发起,我怎样就只能想出这种比方了)

public class University {
    private Student student;
    public void examination(){
        BureauOfEducation.seach(new OnSeachListener{
            @Override
            public void onCallBack(Student xiaoming){
                this.student = xiaoming;
            }
        });
    }
}
try {
    Student dashuai = new Student();
    Class<?> cls = Class.forName("xxx.xxx.xxx.University");
    Field field = cls.getDeclaredField("student");
    field.setAccessible(true);
    field.set(university, dashuai);
}catch (Exception e){
    e.printStackTrace();
}

写的是伪代码,但应该不难看懂。这样在examination()办法调用之后再反射,就能替换目标了。你看这段代码,能被称为hook吗?由于已知流程是student为xiaoming,咱们经过操作改成了dashuai。我说的是代码,这件事就不是hook了,这叫hacker。这招狸猫换太子,我玩得也不少,其间最经典的便是插件化的占坑法。

这叫hook吗,我是用反射去改变原有的履行结果,所以反射 = hook ? 那往大的想。假设你有一个应用,这个应用不是我的。咱们都知道,每个线程都有一个Looper,线程会依照次序履行Message。好,先别管我能不能完成,怎样完成。我用一些办法,在你应用运行时,把一个我的Message刺进你的应用的MessageQueue,这是不是hook?我觉得算,由于在我了解,你的代码的已知流程便是依照MessageQueue的次序去履行,而我的行为改变了这个次序。再往更大的想,你有个app,我往你的app里边去注入代码,我把你的icon替换了或者什么的,这是不是hook?

所以又感觉“给一个已知的流程中hook特定的代码”如同又不太精确,“给一个已知的流程中hook特定的功能”如何?这儿的hook是一个行为,这个行为包含刺进、替换、删去等操作。

除了替换操作,还有其它操作,比方获取。我之前有碰到过这样的场景,运用GsyVideoPlaye的时分我想去设置撤销音频焦点,撤销是用这个办法AudioManager.abandonAudioFocus(onAudioFocusChangeListener),而这个onAudioFocusChangeListener是GSY内部私有的,未提供出来,这时分就需要用同的办法去获取(伪代码)

try {
    Class<?> cls = Class.forName("xxx.xxx.xxx.xxxx");
    Field field = cls.getDeclaredField("xxxxx");
    field.setAccessible(true);
    AudioManager.OnAudioFocusChangeListener onAudioFocusChangeListener = field.get(university, dashuai);
}catch (Exception e){
    e.printStackTrace();
}

然后AudioManager.abandonAudioFocus(onAudioFocusChangeListener)就行了。

那这是不是hook?其实这操感觉也没有影响到GSY内部的流程,我只是拿这个目标出来而言,是由于你内部没有提供get办法露出出来,我才会经过这种办法。

怎样去hook?

怎样去hook,其实在不明确这个概念的根底上怎样去做?很难,这个是没有一个明确的答案的,全要靠你对Android的了解和平常的经历堆集,去找到hook的点,这个没人能教你,即便看了他人的比方,也很学以致用,只能靠自己多看多写。

现已介绍了两种场景,这两种场景都是经过非正规的手段去影响代码,最后看一个。

在Mainifest定义了两个一样的Activity,然后外部跳转的时分,只履行了其间一个Activity的办法,我想让两个Activity都的逻辑都履行,那要怎样处理?这时有的人就想问了,怎样或许定义两个相同的Activity。在清单中,有一个直接<activity name = “xxx.A” …… /> ,别的一个用重定向<activity-alias name = “xxx.A” …… /> 这样的操作是可以的。

但如果我想整合两个的逻辑代码,我就需要把重定向的Activity当成一个一般的类,假设重定向的Activity是这样的。

public class Test extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        functionA();
        functionB();
    }
    @Override
    protected void onResume() {
        super.onResume();
        functionC();
        functionD();
    }
}

我得为它写个代理

public class TestProxy {
    private Test test;
    public TestProxy(){
        try {
            Class<?> cls = Class.forName("xxx.xxx.xxx.Test");
            test = (Test) cls.newInstance();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    protected void onCreate(Bundle savedInstanceState){
        try {
            if (test != null) {
                Class<?> cls = Class.forName("xxx.xxx.xxx.Test");
                Method method = cls.getDeclaredMethod("onCreate", Bundle.class);
                method.invoke(test, savedInstanceState);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    protected void onResume() {
        try {
            if (test != null) {
                Class<?> cls = Class.forName("xxx.xxx.xxx.Test");
                Method method = cls.getDeclaredMethod("onResume");
                method.invoke(test);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

然后我在另一个Activity的生命周期中去调用TestProxy对应的办法,这样可以吗?其实是不行的,为什么?由于原办法中有super,这样调用会履行到它的父类Activity的办法,必定炸。所以咱们要改成这样。咱们不能去调用onCreate和onResume,那样会走super,我不想走super,所以我直接代理里边的逻辑。

public class TestProxy {
    private Test test;
    public TestProxy(){
        try {
            Class<?> cls = Class.forName("xxx.xxx.xxx.Test");
            test = (Test) cls.newInstance();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    protected void onCreate(Bundle savedInstanceState){
        try {
            if (test != null) {
                Class<?> cls = Class.forName("xxx.xxx.xxx.Test");
                Method method = cls.getDeclaredMethod("functionA");
                method.invoke(test);
                Method method2 = cls.getDeclaredMethod("functionB");
                method2.invoke(test);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    protected void onResume() {
        ......
    }
}

这样就能绕过super,但实际的场景没这么简略,假设在functionA中有拿跳转的Intent的值,你怎样做?比方这样

private void functionA(){
    Intent intent = getIntent();
    ......
}

你现在怎样getIntent(),由于这儿是经过TestProxy去走Test的,而不是打开Activity的办法走Test,这儿咱们现已把Test当成一个一般的类而不是Activity了,这儿getIntent()必定拿不到,那这么办?没关系,咱们看源码(Activity的源码)

public Intent getIntent() {
    return mIntent;
}
@UnsupportedAppUsage
/*package*/ Intent mIntent;

哎哟,它这儿是大局的一个mIntent,那我就能用我第一个比方的办法把我的intent给替换进去,这样getIntent()就有值了。

那这一系列的操作是否能被称为hook呢?

我持续假定,这儿不止是getIntent()这么多简略,还有很多个当地有相似的操作, 难道我要一个一个当地进行处理?

那我就想用别的一种办法去处理,我把你这个类复制过来(假设没有混淆),然后我在这个根底上做一些操作,然后打到dex中,用插件化的思维,或者用热更的思维去让它先加载。能明白是什么意思吧,便是我使用双亲委派机制,让我这个复制并改代码的类先加载。那这种操作其实基本能算热更那边的了,还能称作hook吗?