Android-Framework-Plugin插件话框架-Ho

2019-03-11  本文已影响0人  AntCoding

概括

在插件话的世界中Hook技术随处可见,而本次要讲解的内容Hook Activity的过程,主要分为两大块
  • Hook IActivityManger

  • Hook Instrumentation

知识铺垫

在讲解这篇文章之前我们需要Hook是什么,为什么在插件化框架中随处可见,我们首先做一下系统讲解

Hook的概念

Hook中文名"钩子",它使用的是一种面向切面的编程思想(业内专称 AOP编程思想),主要作用是在事件传递过程中对事件进行拦截、修改、监听,将自身的代码动态性替换进去,当这些方法被调用时,保证执行的是我们代码,已达到我们预期的效果

Hook所需知识

  • 反射技术
  • 动态代理技术

案例分解

1.在PluginApplication类的attachBaseContext函数中执行初始化操作
@Override
protected void attachBaseContext(Context base) {
   super.attachBaseContext(base);
   //这个地方之所以这样写,是因为如果是插件进程,initloader必须在applicaiotn启动时执行
   //而如果是宿主进程,initloader可以在这里执行,也可以在需要时再在宿主的其他组件中执行,
   // 例如点击宿主的某个Activity中的button后再执行这个方法来启动插件框架。
   //总体原则有3点:
   //1、插件进程和宿主进程都必须有机会执行initloader
   //2、在插件进程和宿主进程的initloader方法都执行完毕之前,不可和插件交互
   //3、在插件进程和宿主进程的initlaoder方法都执行完毕之前启动的组件,即使在initloader都执行完毕之后,也不可和插件交互
   //如果initloader都在进程启动时就执行,自然很轻松满足上述条件。
   if (ProcessUtil.isPluginProcess(this)) {
      //插件进程,必须在这里执行initLoader
      PluginLoader.initLoader(this);
   } else {
      //宿主进程,可以在这里执行,也可以选择在宿主的其他地方在需要时再启动插件框架
      PluginLoader.initLoader(this);
   }
}
2.调用PluginLoader的静态方法initLoader()执行初始化
/*** 初始化loader, 只可调用一次* * @param app*/
public static synchronized void initLoader(Application app) {
    ...
    //安装ActivityManagerProxy
    AndroidAppIActivityManager.installProxy();
    // 安装NotificationManagerProxy
     ​AndroidAppINotificationManager.installProxy();
    //安装PackageManagerProxy​
    AndroidAppIPackageManager.installProxy(FairyGlobal.getHostApplication().getPackageManager());
    if (isPluginProcess) {
        HackLayoutInflater.installPluginCustomViewConstructorCache();
        CompatForSupportv7ViewInflater.installPluginCustomViewConstructorCache();
        CompatForFragmentClassCache.installFragmentClassCache();
        CompatForFragmentClassCache.installSupportV4FragmentClassCache();//不可在主进程中同步安装,因为此时ActivityThread还没有准备好, 会导致空指针。
        new Handler().post(new Runnable() {
            @Overridepublic
            void run() {
                AndroidWebkitWebViewFactoryProvider.installProxy();
            }
        });
    }//本来宿主进程是不需要注入handlecallback的,这里加上是为了对抗360安全卫士等软件,提高Instrumentation的成功率​
    PluginInjector.injectHandlerCallback();//注入Instrumentation​
    PluginInjector.injectInstrumentation();
    PluginInjector.injectBaseContext(FairyGlobal.getHostApplication());
    ...
}
3. 上面叙述的Hook的位置,接下来我们讲解的重点就来了Hook IActivityManager 和 Hook Insrtumentation
3.1 AndroidAppIActivityManager.installProxy(); Hook IActvityManger原理
1> 这个方法是用于安装ActivityManagerProxy,我们看看方法中的实现:
public static void installProxy() {
    Object androidAppActivityManagerProxy = HackActivityManagerNative.getDefault();   //7.0
    Object androidAppIActivityManagerStubProxyProxy = ProxyUtil.createProxy(androidAppActivityManagerProxy, new AndroidAppIActivityManager());
    //O Preview版本暂时不能通过SDK_INT来区分 2017-5-18
    if (Build.VERSION.SDK_INT <= 25) { //SDK版本判断
        Object singleton = HackActivityManagerNative.getGDefault();
        //如果是androidAppIActivityManagerStubProxyProxy实例对应的类是否被singleton实例对应的类包装过
        if (singleton.getClass().isAssignableFrom(androidAppIActivityManagerStubProxyProxy.getClass())) {
            HackActivityManagerNative.setGDefault(androidAppIActivityManagerStubProxyProxy);
        } else {//否则是New一个被包装过的单例
            new HackSingleton(singleton).setInstance(androidAppIActivityManagerStubProxyProxy);
        }
    } else {
        //Android O 没有gDefault这个成员了, 变量被移到了ActivityManager这个类中
        Object singleton = HackActivityManager.getIActivityManagerSingleton();
        if (singleton != null) {
            new HackSingleton(singleton).setInstance(androidAppIActivityManagerStubProxyProxy); //2 new出一个HookSingleton
        } else {
            LogUtil.e("WTF!!");
        }
    }
    LogUtil.d("安装完成");
}
2>我们这里主要讲解25版本以上的,所以我么继续观察HackActivityManager类的静态函数方法getIActivityManagerSingleton()的实现
public class HackActivityManager {
    private static final String ClassName = "android.app.ActivityManager";
    private static final String Field_IActivityManagerSingleton = "IActivityManagerSingleton";
    public static Object getIActivityManagerSingleton() {
        return RefInvoker.getField(null, ClassName, Field_IActivityManagerSingleton);
    }
    public static Object setIActivityManagerSingleton(Object activityManagerSingleton) {
       return RefInvoker.getField(null, ClassName, Field_IActivityManagerSingleton);
    }
}
3>这里通过反射ActivityManager类获取IActivityManagerSingleton静态私有成员变量的值,此处我们进入源码看一下:
private static final Singleton<IActivityManager> IActivityManagerSingleton =
        new Singleton<IActivityManager>() {
            @Override
            protected IActivityManager create() {
                final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                final IActivityManager am = IActivityManager.Stub.asInterface(b);
                return am;
            }
        };
4>ServiceManager通过调用getService函数方法获取AMS的代理实例,进而调用AMS的startActivity方法函数。IActivityManager借助了Singleton类实现单例的创建,再来看Singleton是如何创建单例对象
public abstract class Singleton<T> {
    private T mInstance;
    protected abstract T create();
    public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                mInstance = create();
            }
            return mInstance;
        }
    }
}​
5>回到1>中的方法体我们接着解析标记//2的函数方法
public class HackSingleton {
    private static final String ClassName = "android.util.Singleton";
    private static final String Field_mInstance = "mInstance";
    private Object instance;
    public HackSingleton(Object instance) {
        this.instance = instance;
    }
    public void setInstance(Object object) {
        RefInvoker.setField(instance, ClassName, Field_mInstance, object);
    }
}
6>创建一个HookSingleton的实例,为Singleton的实例赋值,从前面Singleton类的代码可以得知mInstance字段的类型为T,此处T的类型为IActivityManager,所以我们通过 AndroidAppIActivityManager创建出了androidAppIActivityManagerStubProxyProxy实例,用用AndroidAppIActivityManager来替换IActivityManager,然后经Application中attachBaseContext()函数调用Hook IActivityManager(即最顶层方法)
3.2 PluginInjector.injectInstrumentation(); Hook Instrumentation 原理
1>注入Instrumentation主要是为了支持Activity
static void injectInstrumentation() {
   // 给Instrumentation添加一层代理,用来实现隐藏api的调用
   LogUtil.d("替换宿主程序Intstrumentation");
   HackActivityThread.wrapInstrumentation();
}
2>调用HackActivityThread类中静态方法 wrapInstrumentation()设置Hook Instrumentation
private Object instance;
private static final String ClassName = "android.app.ActivityThread";
private static final String Field_mInstrumentation = "mInstrumentation";
​
​public static void wrapInstrumentation() {
    //此处get()函数是从ThreadLocal中获取到当前线程​
    HackActivityThread hackActivityThread = get();
    if (hackActivityThread != null) {
        //通过反射获取Instrumentation的实例​
        Instrumentation originalInstrumentation = hackActivityThread.getInstrumentation();
        //此处PluginInstrumentationWrapper继承于Instrumentation,插件Activity免注册的主要实现原理
        if (!(originalInstrumentation instanceof PluginInstrumentionWrapper)) {
            hackActivityThread.setInstrumentation(new PluginInstrumentionWrapper(originalInstrumentation));
        }
    } else {
        LogUtil.e("wrapInstrumentation fail!!");
    }
}
//这个方法必须在主线程调用,因为它是从ThreadLocal中取出来的,在其他线程中取出来一定是null
public static synchronized HackActivityThread get() {
    if (hackActivityThread == null) {
        Object instance = currentActivityThread();
        if (instance != null) {
            hackActivityThread = new HackActivityThread(instance);
        }
    }
    return hackActivityThread;
}​​
public Instrumentation getInstrumentation() {
    return (Instrumentation) RefInvoker.getField(instance,
            ClassName, Field_mInstrumentation);
}​
public void setInstrumentation(Instrumentation instrumentation) {
    RefInvoker.setField(instance, ClassName,
            Field_mInstrumentation,
            instrumentation);
}​​
3>通过上面那一步我们已经成功的将我们 PluginInstrumentionWrapper设置进了Instrumentation,通过上篇Activity启动过程可知,关于Activity的生命周期状态的设置最终都是由Instrumentation相关函数调入到Activity中对应的方法实现的状态的设置;这里的PluginInstrumentationWrapper继承于Instrumentation,在super.父类方法之前执行了自己方法实现了函数覆盖重写.

This ALL! Thanks EveryBody!

上一篇下一篇

猜你喜欢

热点阅读