Activity Hook

来源:互联网 发布:男士 爽肤水 知乎 编辑:程序博客网 时间:2024/06/06 01:37

1, Activity Hook

Activity,Service等组件是有生命周期的,它们统一由系统服务AMS管理;

Activity的详细启动流程在此就不论述了。主要步骤如下,

1, 从App进程调用startActivity等一系列方法;

2, 通过IPC调用进入系统进程system_server,完成Activity管理以及一些校检工作;

3, 回到APP进程完成真正的Activioty对象创建。

Hook Activity有2个问题需要解决,

1,必须在AndroidManifest.xml中显式声明要启动的Activity;

2,Activity 生命周期的管理

问题2随着问题的解决也跟着解决了,所以主要是问题1.

问题1的解决方案如下:

首先在步骤1中假装启动一个已经在AndroidManifest.xml里面声明过的替身Activity,然后让这个Activity进入AMS

进程接受检验;最后在第三步的时候换成真正需要启动的Activity;这样就成功欺骗了AMS进程,达到Hook的目的。

这一过程简称为穿马甲和脱马甲。

1.1 注册

AndroidManifest.xml里面的确注册了大量的空的activity

<activity     android:name=".stub.ActivityStub$P00$Standard00"     android:allowTaskReparenting="true"     android:excludeFromRecents="true"     android:exported="false"     android:hardwareAccelerated="true"     android:label="@string/stub_name_activity"     android:launchMode="standard"     android:noHistory="true"     android:theme="@style/DroidPluginTheme">     <intent-filter>             <action android:name="android.intent.action.MAIN" />             <category android:name="com.morgoo.droidplugin.category.PROXY_STUB" />     </intent-filter>     <meta-data             android:name="com.morgoo.droidplugin.ACTIVITY_STUB_INDEX"             android:value="0" /></activity>•••

这些类在ActivityStub中实现如下,

public static class P00{public static class SingleInstance00 extends SingleInstanceStub {     }public static class SingleTask00 extends SingleTaskStub {     }•••

完全就是一些空类。

1.2 使用替身Activity绕过AMS

在AMS章节论述过, 调用AMS的startActivity方法其实是调用startActivity的beforeInvoke方法。

IactivityManagerHookHandle的内部类startActivity的beforeInvoke方法如下,

RunningActivities.beforeStartActivity();boolean bRet = true;if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {bRet = doReplaceIntentForStartActivityAPILow(args);} else {     bRet = doReplaceIntentForStartActivityAPIHigh(args);     •••

1,判断是否有可用的activity, 预先占坑的stub activity数量是有限的,如果没有的话得腾出一个来。

RunningActivities的beforeStartActivity如下,

public static void beforeStartActivity() {   synchronized (mRunningActivityList) {   for (RunningActivityRecord record : mRunningActivityList.values()) {        if (record.stubActivityInfo.launchMode == ActivityInfo.LAUNCH_MULTIPLE) {             continue;        }else if (record.stubActivityInfo.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) {             doFinshIt(mRunningSingleTopActivityList);        }else if (record.stubActivityInfo.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {              doFinshIt(mRunningSingleTopActivityList);         }else if (record.stubActivityInfo.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {              doFinshIt(mRunningSingleTopActivityList);          }      }  }}

可以看到,除了MULTIPLE启动模式的activity record,其余的都会调用doFinshIt()方法。

doFinshIt方法如下,

private static void doFinshIt(Map<Integer, RunningActivityRecord> runningActivityList) {    if (runningActivityList != null && runningActivityList.size() >= PluginManager.STUB_NO_ACTIVITY_MAX_NUM - 1) {    List<RunningActivityRecord> activitys = new ArrayList<>(runningActivityList.size());        activitys.addAll(runningActivityList.values());        Collections.sort(activitys, sRunningActivityRecordComparator);        RunningActivityRecord record = activitys.get(0);        if (record.activity != null && !record.activity.isFinishing()) {            record.activity.finish();        }    }}

2,根据android版本调用不同的方法, doReplaceIntentForStartActivityAPIHigh方法主要逻辑如下,

A,从参数列表里获取intent参数

int intentOfArgIndex = findFirstIntentIndexInArgs(args);

B, 通过intent获取待启动的ActivityInfo

ActivityInfo activityInfo = resolveActivity(intent);

C, 调用selectProxyActivity()选出一个合适的stub activity,主要是根据launch mode还有theme进行判断。

ComponentName component = selectProxyActivity(intent);

D, 创建一个新的intent,其component指向选出来的stub activity,flags和原始intent相同,

同时将原始的intent作为一个EXTRA_TARGET_INTENT参数设置到这个新的intent里面.

Intent newIntent = new Intent();try {    ClassLoader pluginClassLoader = PluginProcessManager.getPluginClassLoader(component.getPackageName());    setIntentClassLoader(newIntent, pluginClassLoader);  } catch (Exception e) {     Log.w(TAG, "Set Class Loader to new Intent fail", e); }newIntent.setComponent(component);newIntent.putExtra(Env.EXTRA_TARGET_INTENT, intent);//这里是真是的intentnewIntent.setFlags(intent.getFlags());

E,最后,如果是宿主程序启动的插件,加上FLAG_ACTIVITY_NEW_TASK在一个新的task中启动插件

if (TextUtils.equals(mHostContext.getPackageName(), callingPackage)) {      newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);      •••

到此,一个假intent就包装完成了,这个假的intent是AndroidManifest.xml里面已经注册过的,因此可以通过AMS的检验。

1.3 恢复真身

在第三步的过程中, AMS进程转移到App进程也是通过Binder调用完成的,承载这个功能的Binder对象是IApplicationThread;

在App进程它是Server端,在Server端接受Binder远程调用的是Binder线程池,Binder线程池通过Handler将消息转发给App的主线程;

详细的hook点说明过程请看http://blog.csdn.net/u012439416/article/details/70666659

总之,需要把ActivityThread里面处理消息的Handler类H的的mCallback 变量Hook为自定义callback类的对象。

1.3.1 callback Hook

Hook的步骤如下,

HookFactory的installHook方法中有关Hook callback的代码如下,

installHook(new PluginCallbackHook(context), classLoader);

PluginCallbackHook相对于前面的Hook方法有些不同。这是Hook一个callback对象。

PluginCallbackHook的createHookHandle返回的结果为null

protected BaseHookHandle createHookHandle() {    return null;}

PluginCallbackHook的onInstall主要逻辑如下,

1,获取到当前的ActivityThread对象  

Object target = ActivityThreadCompat.currentActivityThread();Class ActivityThreadClass = ActivityThreadCompat.activityThreadClass();

2,获取ActivityThread对象的mH变量,也就是H对象(主线程,一个进程中仅有一个)

Field mHField = FieldUtils.getField(ActivityThreadClass, "mH");Handler handler = (Handler) FieldUtils.readField(mHField, target);

3,获取H的mCallback变量,

Field mCallbackField = FieldUtils.getField(Handler.class, "mCallback");//*这里读取出旧的callback并处理*/Object mCallback = FieldUtils.readField(mCallbackField, handler);

4,构造PluginCallback对象,

PluginCallback value = mCallback != null ? new PluginCallback(mHostContext, handler, (Handler.Callback) mCallback) : new PluginCallback(mHostContext, handler, null);value.setEnable(isEnable());

5,将H的mCallback变量设置为PluginCallback。

mCallbacks.add(value);//将PluginCallback对象添加到mCallbacks  ArrayList中FieldUtils.writeField(mCallbackField, handler, value);

1.3.2 获取intent

PluginCallback的定义如下,

public class PluginCallback implements Handler.Callback {

handleMessage方法有关LAUNCH_ACTIVITY消息处理如下,

if (msg.what == LAUNCH_ACTIVITY) {    return handleLaunchActivity(msg);}

这样,当AMS检查完启动Activity时,会调用PluginCallback的handleMessage方法,在此就可以把替身恢复成真身。

handleLaunchActivity方法的主要逻辑如下,

1, 从Message的obj字段里取出intent,注意这里是AMS返回过来的之前创建的那个假intent,然后从其

EXTRA_TARGET_INTENT字段里取出原始intent

Object obj = msg.obj;Intent stubIntent = (Intent) FieldUtils.readField(obj, "intent");//ActivityInfo activityInfo = (ActivityInfo) FieldUtils.readField(obj, "activityInfo", true);stubIntent.setExtrasClassLoader(mHostContext.getClassLoader());Intent targetIntent = stubIntent.getParcelableExtra(Env.EXTRA_TARGET_INTENT);

2, 根据原始intent解析出ComponentName和ActivityInfo

ComponentName targetComponentName = targetIntent.resolveActivity(mHostContext.getPackageManager());ActivityInfo targetActivityInfo = PluginManager.getInstance().getActivityInfo(targetComponentName, 0);if (targetActivityInfo != null) {if (targetComponentName != null && targetComponentName.getClassName().startsWith(".")) {                        targetIntent.setClassName(targetComponentName.getPackageName(), targetComponentName.getPackageName() + targetComponentName.getClassName());    }

3, 把这个原始intent以及解析出来的ActivityInfo重新写回Message中,这样后续的处理中就会用这个原始的intent了。

Intent newTargetIntent = new Intent();newTargetIntent.setComponent(targetIntent.getComponent());newTargetIntent.putExtra(Env.EXTRA_TARGET_INFO, targetActivityInfo);if (stubActivityInfo != null) {     newTargetIntent.putExtra(Env.EXTRA_STUB_INFO, stubActivityInfo);}FieldUtils.writeDeclaredField(msg.obj, "intent", newTargetIntent);

这样,最后真正启动的是未在AndroidManifest.xml中显式声明要启动的Activity。

原创粉丝点击