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);
}