在前面的文章中咱们介绍了DroidPlugin的Hook机制,也就是代理方式和Binder Hook;插件框架经过AOP实现了插件使用和开发的透明性。在讲述DroidPlugin如何实现四大组件的插件化以前,有必要说明一下它对ActivityManagerServiche以及PackageManagerService的Hook方式(如下简称AMS,PMS)。java
ActivityManagerService对于FrameWork层的重要性不言而喻,Android的四大组件无一不与它打交道:android
startActivity
最终调用了AMS的startActivity
系列方法,实现了Activity的启动;Activity的生命周期回调,也在AMS中完成;startService,bindService
最终调用到AMS的startService和bindService方法;AMS
中完成(静态广播在PMS
中完成)getContentResolver
最终从AMS
的getContentProvider
获取到ContentProvider而PMS
则完成了诸如权限校捡(checkPermission,checkUidPermission
),Apk meta信息获取(getApplicationInfo
等),四大组件信息获取(query
系列方法)等重要功能。git
在上文Android插件化原理解析——Hook机制之Binder Hook中讲述了DroidPlugin的Binder Hook机制;咱们知道AMS
和PMS
就是以Binder方式提供给应用程序使用的系统服务,理论上咱们也能够采用这种方式Hook掉它们。可是因为这二者使用得如此频繁,Framework给他们了一些“特别优待”,这也给了咱们相对于Binder Hook更加稳定可靠的hook方式。github
阅读本文以前,能够先clone一份 understand-plugin-framework,参考此项目的ams-pms-hook
模块。另外,插件框架原理解析系列文章见索引。编程
前文提到Android的四大组件无一不与AMS
相关,也许读者还有些许疑惑;这里我就挑一个例子,依据Android源码来讲明,一个简单的startActivity
是如何调用AMS
最终经过IPC到system_server的。segmentfault
不论读者是否知道,咱们使用startActivity
有两种形式:app
Context
类的startActivity
方法;这种方式启动的Activity没有Activity栈,所以不能以standard方式启动,必须加上FLAG_ACTIVITY_NEW_TASK
这个Flag。Activity
类重载过的startActivity
方法,一般在咱们的Activity中直接调用这个方法就是这种形式;咱们查看Context
类的startActivity
方法,发现这居然是一个抽象类;查看Context
的类继承关系图以下:框架
咱们看到诸如Activity
,Service
等并无直接继承Context
,而是继承了ContextWrapper
;继续查看ContextWrapper
的实现:ide
1 2 3 4 |
@Override public void startActivity(Intent intent) { mBase.startActivity(intent); } |
WTF!! 果真人如其名,只是一个wrapper而已;这个mBase
是什么呢?这里我先直接告诉你,它的真正实现是ContextImpl
类;至于为何,有一条思路:mBase是在ContextWrapper构造的时候传递进来的,那么在ContextWrapper构造的时候能够找到答案
何时会构造ContextWrapper呢?它的子类Application
,Service
等被建立的时候。工具
能够在App的主线程AcitivityThread
的performLaunchActivit
方法里面找到答案;更详细的解析能够参考老罗的 Android应用程序启动过程源代码分析
好了,咱们姑且看成已经知道Context.startActivity最终使用了ContextImpl里面的方法,代码以下:
1 2 3 4 5 6 7 8 9 10 11 12 |
public void startActivity(Intent intent, Bundle options) { warnIfCallingFromSystemProcess(); if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) { throw new AndroidRuntimeException( "Calling startActivity() from outside of an Activity " + " context requires the FLAG_ACTIVITY_NEW_TASK flag." + " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivity( getOuterContext(), mMainThread.getApplicationThread(), null, (Activity)null, intent, -1, options); } |
代码至关简单;咱们知道了两件事:
FLAG_ACTIVITY_NEW_TASK
;startActivity
使用了Instrumentation
类的execStartActivity
方法;继续跟踪: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { // ... 省略无关代码 try { intent.migrateExtraStreamToClipData(); intent.prepareToLeaveProcess(); // ----------------look here!!!!!!!!!!!!!!!!!!! int result = ActivityManagerNative.getDefault() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null ? target.mEmbeddedID : null, requestCode, 0, null, null, options); checkStartActivityResult(result, intent); } catch (RemoteException e) { } return null; } |
到这里咱们发现真正调用的是ActivityManagerNative
的startActivity
方法;若是你不清楚ActivityManager
,ActivityManagerService
以及ActivityManagerNative
之间的关系;建议先仔细阅读我以前关于Binder的文章 Binder学习指南。
Activity类的startActivity
方法相比Context而言直观了不少;这个startActivity
经过若干次调用展转到达startActivityForResult
这个方法,在这个方法内部有以下代码:
1 2 3 4 |
Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options); |
能够看到,其实经过Activity和ContextImpl类启动Activity并没有本质不一样,他们都经过Instrumentation
这个辅助类调用到了ActivityManagerNative
的方法。
OK,咱们到如今知道;其实startActivity
最终经过ActivityManagerNative
这个方法远程调用了AMS
的startActivity
方法。那么这个ActivityManagerNative
是什么呢?
ActivityManagerNative实际上就是ActivityManagerService
这个远程对象的Binder代理对象;每次须要与AMS打交道的时候,须要借助这个代理对象经过驱动进而完成IPC调用。
咱们继续看ActivityManagerNative
的getDefault()
方法作了什么:
1 2 3 |
static public IActivityManager getDefault() { return gDefault.get(); } |
gDefault
这个静态变量的定义以下:
1 2 3 4 5 6 7 |
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() { protected IActivityManager create() { IBinder b = ServiceManager.getService("activity IActivityManager am = asInterface( return am; } }; |
因为整个Framework与AMS打交道是如此频繁,framework使用了一个单例把这个AMS
的代理对象保存了起来;这样只要须要与AMS
进行IPC调用,获取这个单例便可。这是AMS
这个系统服务与其余普通服务的不一样之处,也是咱们不经过Binder Hook的缘由——咱们只须要简单地Hook掉这个单例便可。
这里还有一点小麻烦:Android不一样版本之间对于如何保存这个单例的代理对象是不一样的;Android 2.x系统直接使用了一个简单的静态变量存储,Android 4.x以上抽象出了一个Singleton类;具体的差别可使用grepcode
进行比较:差别
咱们以4.x以上的代码为例说明如何Hook掉AMS
;方法使用的动态代理,若是有不理解的,能够参考以前的系列文章Android插件化原理解析——Hook机制之动态代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Class<?> activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative"); // 获取 gDefault 这个字段, 想办法替换它 Field gDefaultField = activityManagerNativeClass.getDeclaredField("gDefault"); gDefaultField.setAccessible(true); Object gDefault = gDefaultField.get(null); // 4.x以上的gDefault是一个 android.util.Singleton对象; 咱们取出这个单例里面的字段 Class<?> singleton = Class.forName("android.util.Singleton"); Field mInstanceField = singleton.getDeclaredField("mInstance"); mInstanceField.setAccessible(true); // ActivityManagerNative 的gDefault对象里面原始的 IActivityManager对象 Object rawIActivityManager = mInstanceField.get(gDefault); // 建立一个这个对象的代理对象, 而后替换这个字段, 让咱们的代理对象帮忙干活 Class<?> iActivityManagerInterface = Class.forName("android.app.IActivityManager"); Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[] { iActivityManagerInterface }, new IActivityManagerHandler(rawIActivityManager)); mInstanceField.set(gDefault, proxy); |
好了,咱们hook成功以后启动Activity看看会发生什么:
1 2 3 4 5 6 7 8 |
D/HookHelper﹕ hey, baby; you are hook!! D/HookHelper﹕ method:activityResumed called with args:[android.os.BinderProxy@9bc71b2] D/HookHelper﹕ hey, baby; you are hook!! D/HookHelper﹕ method:activityIdle called with args:[android.os.BinderProxy@9bc71b2, null, false] D/HookHelper﹕ hey, baby; you are hook!! D/HookHelper﹕ method:startActivity called with args:[android.app.ActivityThread$ApplicationThread@17e750c, com.weishu.upf.ams_pms_hook.app, Intent { act=android.intent.action.VIEW dat=http://wwww.baidu.com/... }, null, android.os.BinderProxy@9bc71b2, null, -1, 0, null, null] D/HookHelper﹕ hey, baby; you are hook!! D/HookHelper﹕ method:activityPaused called with args:[android.os.BinderProxy@9bc71b2] |
能够看到,简单的几行代码,AMS
已经被咱们彻底劫持了!! 至于劫持了能干什么,本身发挥想象吧~
DroidPlugin关于AMS
的Hook,能够查看IActivityManagerHook
这个类,它处理了我上述所说的兼容性问题,其余原理相同。另外,也许有童鞋有疑问了,你用startActivity
为例怎么能确保Hook掉这个静态变量以后就能保证全部使用AMS
的入口都被Hook了呢?
答曰:无他,惟手熟尔。
Android Framewrok层对于四大组件的处理,调用AMS
服务的时候,所有都是经过使用这种方式;如有疑问能够自行查看源码。你能够从Context
类的startActivity, startService,bindService, registerBroadcastReceiver, getContentResolver 等等入口进行跟踪,最终都会发现它们都会使用ActivityManagerNative的这个AMS
代理对象来完成对远程AMS的访问。
PMS
的获取也是经过Context完成的,具体就是getPackageManager
这个方法;咱们姑且看成已经知道了Context的实如今ContextImpl类里面,直奔ContextImpl
类的getPackageManager
方法:
1 2 3 4 5 6 7 8 9 10 11 12 |
public PackageManager getPackageManager() { if (mPackageManager != null) { return mPackageManager; } IPackageManager pm = ActivityThread.getPackageManager(); if (pm != null) { // Doesn't matter if we make more than one instance. return (mPackageManager = new ApplicationPackageManager(this, pm)); } return null; } |
能够看到,这里干了两件事:
PMS
的代理对象在ActivityThread
类里面ContextImpl
经过ApplicationPackageManager
对它还进行了一层包装咱们继续查看ActivityThread
类的getPackageManager
方法,源码以下:
1 2 3 4 5 6 7 8 |
public static IPackageManager getPackageManager() { if (sPackageManager != null) { return sPackageManager; } IBinder b = ServiceManager.getService("package"); sPackageManager = IPackageManager.Stub.asInterface(b); return sPackageManager; } |
能够看到,和AMS
同样,PMS
的Binder代理对象也是一个全局变量存放在一个静态字段中;咱们能够如法炮制,Hook掉PMS。
如今咱们的目的很明切,若是须要Hook PMS
有两个地方须要Hook掉:
ActivityThread
的静态字段sPackageManager
getPackageManager
方法获取到的ApplicationPackageManager
对象里面的mPM
字段。如今使用代理Hook应该是轻车熟路了吧,经过上面的分析,咱们Hook两个地方;代码信手拈来:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// 获取全局的ActivityThread对象 Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread"); Object currentActivityThread = currentActivityThreadMethod.invoke(null); // 获取ActivityThread里面原始的 sPackageManager Field sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager"); sPackageManagerField.setAccessible(true); Object sPackageManager = sPackageManagerField.get(currentActivityThread); // 准备好代理对象, 用来替换原始的对象 Class<?> iPackageManagerInterface = Class.forName("android.content.pm.IPackageManager"); Object proxy = Proxy.newProxyInstance(iPackageManagerInterface.getClassLoader(), new Class<?>[] { iPackageManagerInterface }, new HookHandler(sPackageManager)); // 1. 替换掉ActivityThread里面的 sPackageManager 字段 sPackageManagerField.set(currentActivityThread, proxy); // 2. 替换 ApplicationPackageManager里面的 mPM对象 PackageManager pm = context.getPackageManager(); Field mPmField = pm.getClass().getDeclaredField("mPM"); mPmField.setAccessible(true); mPmField.set(pm, proxy); |
好了,Hook完毕咱们验证如下结论;调用一下PMS
的getInstalledApplications
方法,打印日志以下:
1 2 |
03-07 15:07:27.187 8306-8306/com.weishu.upf.ams_pms_hook.app D/IActivityManagerHandler﹕ hey, baby; you are hook!! 03-07 15:07:27.187 8306-8306/com.weishu.upf.ams_pms_hook.app D/IActivityManagerHandler﹕ method:getInstalledApplications called with args:[0, 0] |
OK,咱们又成功劫持了PackageManager
!!DroidPlugin 处理PMS的代码能够在IPackageManagerHook
查看。
在结束讲解PackageManager的Hook以前,咱们须要说明一点;那就是Context
的实现类里面没有使用静态全局变量来保存PMS
的代理对象,而是每拥有一个Context
的实例就持有了一个PMS
代理对象的引用;因此这里有个很蛋疼的事情,那就是咱们若是想要彻底Hook住PMS
,须要精确控制整个进程内部建立的Context
对象;所幸,插件框架中,插件的Activity,Service,ContentProvider,Broadcast等全部使用到Context的地方,都是由框架控制建立的;所以咱们要当心翼翼地替换掉全部这些对象持有的PMS
代理对象。
我前面也提到过,静态变量和单例都是良好的Hook点,这里很好地反证了这句话:想要Hook掉一个实例变量该是多么麻烦!
写到这里,关于DroidPlugin的Hook技术的讲解已经完结了;我相信读者或多或少地认识到,其实Hook并非一项神秘的技术;一个干净,透明的框架少不了AOP,而AOP也少不了Hook。
我所讲解的Hook仅仅使用反射和动态代理技术,更增强大的Hook机制能够进行字节码编织,好比J2EE普遍使用了cglib和asm进行AOP编程;而Android上现有的插件框架仍是加载编译时代码,采用动态生成类的技术理论上也是可行的;以前有一篇文章Android动态加载黑科技 动态建立Activity模式,就讲述了这种方式;如今全球的互联网公司不排除有用这种技术实现插件框架的可能 ;我相信不远的将来,这种技术也会在Android上大放异彩。
了解完Hook技术以后,接下来的系列文章会讲述DroidPlugin对Android四大组件在插件系统上的处理,插件框架对于这一部分的实现是DroidPlugin的精髓,Hook只不过是工具而已。学习这部份内容须要对于Activity,Service,Broadcast以及ContentProvider的工做机制有必定的了解,所以我也会在必要的时候穿插讲解一些Android Framework的知识;我相信这必定会对读者大有裨益。