反射+代理实现 API Hook(以 ActivityManager 为例)

最近看Android招聘信息,发现一个自己不太会的点 hook 机制,查了一下资料,现在整理一下。特别感谢acxingyun同学提供了详细的姿势。点击地址

什么是 Hook

hook翻译过来是钩子的意思。 目的就是在事件传送到终点前截获并监控事件的传输,像个钩子钩上事件一样,并且能够在钩上事件时,处理一些自己特定的事件。而代理模式正好可以做到这种效果。

代理对象

了解了hook的目的,下面就是怎样勾上整个事件,来监测事件的每一步。
拿 Activity 来举例,Activity 的相关的启动方法在IActivityManager接口里面都已经定义好了,但正常情况下我们无法获取相关的对象的信息,更无法修改,所以在这里我们要借用反射来获取到相应的 Class 对象,将IActivityManager的实例替换为我们自己的代理对象,在代理对象里面去做我们想做的事情。
无图无真相。上图
在这里插入图片描述

ActivityManagerNative的实例持有一个Singleton<IActivityManager>类型的对象 gDefault,gDefault持有IActivityManager的实例mInstance,
正常状态持有的mInstance实例 我们通过 Proxy.newProxyInstance()做出一个代理对象来,再利用反射机制重新给 gDefault对象,这样我们就能在InvocationHandler中监听整个事件并处理。

具体实现

public class APIHook {
    
    

    /**
     * ActivityManager 替换为代理
     * @throws ClassNotFoundException
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    public void hookAM() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
    
    
        Class<?> amnClass = Class.forName("android.app.ActivityManagerNative");
        //获取单例对象 Singleton<IActivityManager> ,变量名 gDefault 私有
        Field gDefaultField = amnClass.getDeclaredField("gDefault");
        //禁止JAVA 进行语言访问检查,private 等修饰的就可以访问操作了
        gDefaultField.setAccessible(true);
        //If the underlying field is a static field, the obj argument is ignored; it may be null.
        //静态属性,直接传入 Null。获取ActivityManagerNative 中的 gDefault
        Object gDefault = gDefaultField.get(null);

        Class<?> singletonClass = Class.forName("android.util.Singleton");
        Field mInstanceField = singletonClass.getDeclaredField("mInstance");
        mInstanceField.setAccessible(true);

        //调用 Singleton 的 get方法 取出 instance 对象
        //instance 对象即 ActivityManager
        Object instance = mInstanceField.get(gDefault);

        //创建代理 handler
        ActivityManagerHandler handler = new ActivityManagerHandler(instance);
        //反射 IActivityManager接口 Class 文件
        Class<?> amClass = Class.forName("android.app.IActivityManager");
        //创建代理对象
        Object amProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{
    
    amClass}, handler);

        //将gDefault中的 Instance 置换为代理
        mInstanceField.set(gDefault,amProxy);
    }
    /**
     * 代理对象回调
     */
    private class ActivityManagerHandler implements InvocationHandler {
    
    
        private Object am;

        public ActivityManagerHandler(Object am) {
    
    
            this.am = am;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
            Log.e("APIHook","正在调用的方法--->"+method.getName());
            return method.invoke(am,args);
        }
    }
}

知识点:对于 private 修饰的变量,我们需要调用Field.setAccessible(true);来禁止 JAVA 语法检查才能给变量赋值哟

这个代码是可以直接用的,把玩时在 Application 的 onCreate 中调用

new APIHook().hookAM();

就写到这里啦,权当又复习了一遍代理和反射,大家一起加油啊

猜你喜欢

转载自blog.csdn.net/lijie2664989/article/details/79919767
今日推荐