Hook机制之AMS

Hook机制之AMS

前文提到Android的四大组件无一不与AMS相关,也许读者还有些许疑惑;这里我就挑一个例子,依据Android源码来说明,一个简单的startActivity是如何调用AMS最终通过IPC到system_server的。

不论读者是否知道,我们使用startActivity有两种形式:

直接调用Context类的startActivity方法;这种方式启动的Activity没有Activity栈,因此不能以standard方式启动,必须加上FLAG_ACTIVITY_NEW_TASK这个Flag。
调用被Activity类重载过的startActivity方法,通常在我们的Activity中直接调用这个方法就是这种形式;
Context.startActivity
我们查看Context类的startActivity方法,发现这竟然是一个抽象类;查看Context的类继承关系图如下:
在这里插入图片描述
我们看到诸如Activity,Service等并没有直接继承Context,而是继承了ContextWrapper;继续查看ContextWrapper的实现:

@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里面的方法,代码如下

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

代码相当简单;我们知道了两件事:

其一,我们知道了在Service等非Activity的Context里面启动Activity为什么需要添加FLAG_ACTIVITY_NEW_TASK;
其二,真正的startActivity使用了Instrumentation类的execStartActivity方法;继续跟踪:

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
Activity类的startActivity方法相比Context而言直观了很多;这个startActivity通过若干次调用辗转到达startActivityForResult这个方法,在这个方法内部有如下代码:

Instrumentation.ActivityResult ar =
    mInstrumentation.execStartActivity(
        this, mMainThread.getApplicationThread(), mToken, this,
        intent, requestCode, options);

可以看到,其实通过Activity和ContextImpl类启动Activity并无本质不同,他们都通过Instrumentation这个辅助类调用到了ActivityManagerNative的方法。

Hook AMS
OK,我们到现在知道;其实startActivity最终通过ActivityManagerNative这个方法远程调用了AMS的startActivity方法。那么这个ActivityManagerNative是什么呢?

ActivityManagerNative实际上就是ActivityManagerService这个远程对象的Binder代理对象;每次需要与AMS打交道的时候,需要借助这个代理对象通过驱动进而完成IPC调用。

我们继续看ActivityManagerNative的getDefault()方法做了什么:

static public IActivityManager getDefault() {
    return gDefault.get();
}

gDefault这个静态变量的定义如下:

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机制之动态代理

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看看会发生什么:

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]

反射 这里面涉及到几个的反射方法
Class.forName()
通过类名获取Class对象。
setAccessible()
改变访问对象的可见性,常常用来访问private属性的对象。
invoke(Object receiver, Object… args)
通过对象和参数列表执行方法。
get(Object object)
获取Feild的值。
怎么Hook 先理清Hook是什么?翻译过来是钩子。
我的理解是一些已有的API不能满足新的需求,可以通过Hook来改变其功能,或在原API前后插入新的代码,或改变其返回值,或干脆覆盖掉原来的代码,来实现新的需求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值