android插件开发-就是你了!启动吧!插件的activity(二)
2016-04-14 21:53
513 查看
这篇博客是上篇的延续,在阅读之前先阅读第一部分:第一部分
我们在启动插件的activity时,通过替换component成功欺骗AMS获得了启动一个activity所必须的一些资源。不过,我们还没有把获取的那些资源都转移到插件的activity之下。这一节就是解决这个问题。
所有的答案都是分析源码之后得到的,所以我们还和之前一样继续分析源码,看下AMS是怎么把资源关联到一个activity上的,这样我们才有可能转移这些资源到插件的activity之下。
在上一篇博文中我们分析到了startActivityLocked函数。在这个函数里面,我们在获得启动一个activity的信息,和要启动的activity信息之后,我们转到了startActivityUncheckedLocked函数:
之后调用的是startActivityLocked方法:
这个函数做的事情很简单,就是把要启动的activity压到活动栈的栈顶,这里又证实了我们之前的猜想:
看下是否是最前面的一个任务活动栈(很好理解,因为你不可能一次只开一个应用,你可能在使用手机时打开QQ,微信,微博,他们都各自对应很多个活动栈),是的话就准备响应他
stack.resumeTopActivityLocked:
最后都转发到了resumeTopActivityInnerLocked
查看下:
在确认一切无误之后调用startPausingLocked方法
prev.app.thread返回一个IApplicationThread,它通知Ui线程可以终止当前正在响应的activity了,我要开启新的activity了!
在本例中就是:
为了验证我们的猜想,还是继续阅读源码:
Handler中的处理代码:
IBinder被保存在Message的obj中,之后调用handlePauseActivity函数:
回到AMS中:
NEXT:
由于篇幅限制,我们只分析到这里,之后,AMS就准备真正启动activity了:
这里回调ui线程开始真正启动一个activity
ActivityThread.java
还是和之前一样,把AMS返回的资源封装到ActivityClientRecord之后,发送message,并把资源存储在Message的obj中。
之后进入H的处理函数:
它的handleMessage函数
这里有个小知识,在handler调用handleMessage,会调用dispathMessage用来分发Message,我们从下面的源码可以看出,如果我们的message自己有callback,那么就调用Message自己的callback,否则看下handler本身的mCallback,如果mCallback不为空,那么就调用mCallback的handleMessage,并且如果返回true则直接结束,否则调用我们重载的handleMessage函数。
那么思路来了,我们首先hook H这个类的的mCallbak域,替换成我们自己的callback,当检测到处理的消息是针对StubActivity时,我们获得原来的启动插件的Intent,然后替换下component(和原来逆向的过程)
主要代码:
效果:
这里解释下我们是如何hookAMS的,考虑之前我们分析如何hook一个系统服务的例子:例子,当我们启动一个activity的时候,会调用:
我们可以看下:
而这个gDefault呢是一个Singleton对象:
而他的具体实现在:
可见只要我们hook mInstance就行,而它被实例化IActivityManager对象,之后我们只需拦截其中的IActivityManager对象的startActivity方法,检测是否是标识启动一个插件activity,然后替换其中的component。
我们在启动插件的activity时,通过替换component成功欺骗AMS获得了启动一个activity所必须的一些资源。不过,我们还没有把获取的那些资源都转移到插件的activity之下。这一节就是解决这个问题。
所有的答案都是分析源码之后得到的,所以我们还和之前一样继续分析源码,看下AMS是怎么把资源关联到一个activity上的,这样我们才有可能转移这些资源到插件的activity之下。
在上一篇博文中我们分析到了startActivityLocked函数。在这个函数里面,我们在获得启动一个activity的信息,和要启动的activity信息之后,我们转到了startActivityUncheckedLocked函数:
final int startActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, Bundle options, TaskRecord inTask) { ... //用来判断是否需要重新创建一个新的任务栈来启动这个activity //在我们这里例子里面 我们newTask会一直是false boolean newTask = false; boolean keepCurTransition = false; ... // Should this be considered a new task? if (r.resultTo == null && inTask == null && !addingToTask && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { ... } else if (sourceRecord != null) { } else .... .... //调用ActivityStack的成员函数 targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options); ... return ActivityManager.START_SUCCESS; }
之后调用的是startActivityLocked方法:
final void startActivityLocked(ActivityRecord r, boolean newTask, boolean doResume, boolean keepCurTransition, Bundle options) { ... if (!newTask) { // If starting in an existing task, find where that is... boolean startIt = true; for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { task = mTaskHistory.get(taskNdx); if (task.getTopActivity() == null) { // All activities in task are finishing. continue; } if (task == r.task) { // Here it is! Now, if this is not yet visible to the // user, then just add it without starting; it will // get started when the user navigates back to it. if (!startIt) { if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task " + task, new RuntimeException("here").fillInStackTrace()); task.addActivityToTop(r); r.putInHistory(); mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen, (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId, r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind); if (VALIDATE_TOKENS) { validateAppTokensLocked(); } ActivityOptions.abort(options); return; } break; } else if (task.numFullscreen > 0) { startIt = false; } } } ... //把要启动的activity压入活动栈中 task.addActivityToTop(r); task.setFrontOfTask(); ... //我们显然是要让activity显示 所以这里一定会执行 if (doResume) { mStackSupervisor.resumeTopActivitiesLocked(this, r, options); } }
这个函数做的事情很简单,就是把要启动的activity压到活动栈的栈顶,这里又证实了我们之前的猜想:
AMS解析Intent,获得ActivityRecord,而ActivityRecord用来表示一个activity,至于如何解析Intent获得ActivityRecord,内容和网上一些intent解析过程一样intent解析过程 ,而我们在代码中是显示启动一个activity,所以我们替换ComponeneName就可以
接下来我们继续转到StackSupervisor中去查看resumeTopActivitiesLocked看下是否是最前面的一个任务活动栈(很好理解,因为你不可能一次只开一个应用,你可能在使用手机时打开QQ,微信,微博,他们都各自对应很多个活动栈),是的话就准备响应他
stack.resumeTopActivityLocked:
最后都转发到了resumeTopActivityInnerLocked
查看下:
final boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) { ... // We need to start pausing the current activity so the top one // can be resumed... boolean dontWaitForPause = (next.info.flags&ActivityInfo.FLAG_RESUME_WHILE_PAUSING) != 0; boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, true, dontWaitForPause); if (mResumedActivity != null) { if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Pausing " + mResumedActivity); //调用start[ pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause); } ... return true; }
在确认一切无误之后调用startPausingLocked方法
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming, boolean dontWait) { ... if (prev.app != null && prev.app.thread != null) { if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev); try { ... //prev.app.thread返回一个IApplicationThread prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing, userLeaving, prev.configChangeFlags, dontWait); } catch (Exception e) { ... } } else { ... } ... }
prev.app.thread返回一个IApplicationThread,它通知Ui线程可以终止当前正在响应的activity了,我要开启新的activity了!
prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing, userLeaving, prev.configChangeFlags, dontWait);
在本例中就是:
这里prev.appToken是一个IBinder对象,其他的都是Boolean还有Configuration类型,我们不难得出,AMS区分activity是谁就是通过这个IBinder对象(Token是IBinder的实现类)!!!
为了验证我们的猜想,还是继续阅读源码:
Handler中的处理代码:
IBinder被保存在Message的obj中,之后调用handlePauseActivity函数:
//mActivities是一个map,通过IBinder映射activity client record! final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
这里通过IBinder找到要停止的类,!看来AMS就是通过这个IBinder类区分要操作的activity!那么,我们只要能够替换掉本该给StubActivity的IBinder,那么PluginActivity是不是就名正言顺的获得了AMS的认可,间接也就获得了声明周期呢!
之后的操作:回到AMS中:
@Override public final void activityPaused(IBinder token) { final long origId = Binder.clearCallingIdentity(); synchronized(this) { //通过这个IBinder找到任务栈 ActivityStack stack = ActivityRecord.getStackLocked(token); if (stack != null) { //开始准备启动新的activity了 stack.activityPausedLocked(token, false); } } Binder.restoreCallingIdentity(origId); }
NEXT:
//ActivityStack.java final void activityPausedLocked(IBinder token, boolean timeout) { if (DEBUG_PAUSE) Slog.v( TAG, "Activity paused: token=" + token + ", timeout=" + timeout); final ActivityRecord r = isInStackLocked(token); if (r != null) { //正常停止了就要移出检测停止activity超时的消息 mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); if (mPausingActivity == r) { if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r + (timeout ? " (due to timeout)" : " (pause complete)")); //完成停止activity completePauseLocked(true); } else { EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE, r.userId, System.identityHashCode(r), r.shortComponentName, mPausingActivity != null ? mPausingActivity.shortComponentName : "(none)"); } } }
由于篇幅限制,我们只分析到这里,之后,AMS就准备真正启动activity了:
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, boolean andResume, boolean checkConfig) throws RemoteException { ... ProfilerInfo profilerInfo = profileFile != null ? new ProfilerInfo(profileFile, profileFd, mService.mSamplingInterval, mService.mAutoStopProfiler) : null; app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP); //回调到Ui 第二个参数就是之前分析的IBinder app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken, System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration), r.compat, r.launchedFromPackage, r.task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo); ... return true; }
这里回调ui线程开始真正启动一个activity
ActivityThread.java
还是和之前一样,把AMS返回的资源封装到ActivityClientRecord之后,发送message,并把资源存储在Message的obj中。
之后进入H的处理函数:
它的handleMessage函数
这里有个小知识,在handler调用handleMessage,会调用dispathMessage用来分发Message,我们从下面的源码可以看出,如果我们的message自己有callback,那么就调用Message自己的callback,否则看下handler本身的mCallback,如果mCallback不为空,那么就调用mCallback的handleMessage,并且如果返回true则直接结束,否则调用我们重载的handleMessage函数。
那么思路来了,我们首先hook H这个类的的mCallbak域,替换成我们自己的callback,当检测到处理的消息是针对StubActivity时,我们获得原来的启动插件的Intent,然后替换下component(和原来逆向的过程)
主要代码:
/** * Created by chan on 16/4/8. */ public class HookApplication extends Application { @TargetApi(Build.VERSION_CODES.KITKAT) @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); try { //获得ActivityManagerNative Class<?> serviceManagerClz = Class.forName("android.app.ActivityManagerNative", false, getClassLoader()); //获得ActivityManagerNative.getDefault静态方法 Method getDefaultMethod = serviceManagerClz.getDeclaredMethod("getDefault"); //获得原始的IActivityManager对象 Object rawIActivityManagerInterface = getDefaultMethod.invoke(null); //我们自己的Hook的对象 Object hookIActivityManagerInterface = Proxy.newProxyInstance( getClassLoader(), new Class[]{Class.forName("android.app.IActivityManager", false, getClassLoader())}, new AMSHook(rawIActivityManagerInterface) ); //反射ActivityManagerNative的gDefault域 Field gDefaultField = serviceManagerClz.getDeclaredField("gDefault"); gDefaultField.setAccessible(true); Object gDefaultObject = gDefaultField.get(null); //他的类型是Singleton Class<?> singletonClz = Class.forName("android.util.Singleton", false, getClassLoader()); //把他的mInstance域替换掉 成为我们自己的Hook对象 Field mInstanceField = singletonClz.getDeclaredField("mInstance"); mInstanceField.setAccessible(true); mInstanceField.set(gDefaultObject, hookIActivityManagerInterface); Class<?> activityThreadClz = Class.forName("android.app.ActivityThread", false, getClassLoader()); Method method = activityThreadClz.getDeclaredMethod("currentActivityThread"); Object activityThreadObject = method.invoke(null); Field mHField = activityThreadClz.getDeclaredField("mH"); mHField.setAccessible(true); Object mHObject = mHField.get(activityThreadObject); Field handlerCallbackField = Handler.class.getDeclaredField("mCallback"); for(Field f : Handler.class.getDeclaredFields()) { Log.d("chan_debug", f.getName()); } handlerCallbackField.setAccessible(true); Object callbackObject = handlerCallbackField.get(mHObject); Object hookHObject = new MessageHook(callbackObject, getClassLoader()); handlerCallbackField.set(mHObject, hookHObject); } catch (ClassNotFoundException | IllegalAccessException | NoSuchMethodException | InvocationTargetException | NoSuchFieldException e) { e.printStackTrace(); } } }
package com.chan.hook.handle; import android.content.ComponentName; import android.content.Intent; import android.os.Handler; import android.os.Message; import com.chan.hook.StubActivity; import com.chan.hook.util.Constant; import java.lang.reflect.Field; /** * Created by chan on 16/4/14. */ public class MessageHook implements Handler.Callback { private Handler.Callback m_base; private static final int LAUNCH_ACTIVITY = 100; private Field m_intentField; public MessageHook(Object base, ClassLoader classLoader) throws ClassNotFoundException, NoSuchFieldException { m_base = (Handler.Callback) base; //获取ActivityClientRecord的class Class<?> activityClientRecordClz = Class.forName("android.app.ActivityThread$ActivityClientRecord", false, classLoader); //获得它的intent m_intentField = activityClientRecordClz.getDeclaredField("intent"); m_intentField.setAccessible(true); } @Override public boolean handleMessage(Message msg) { //检测到时启动一个activity if (msg.what == LAUNCH_ACTIVITY) { try { //msg.obj是android.app.ActivityThread$ActivityClientRecord对象,请参考前面的源码解析 Intent intent = (Intent) m_intentField.get(msg.obj); ComponentName componentName = intent.getComponent(); //检测到是启动StubActivity if(componentName != null && componentName.getClassName().equals(StubActivity.class.getCanonicalName())) { //获得之前启动插件的intent Intent raw = intent.getParcelableExtra(Constant.EXTRA_RAW_INTENT); //替换成插件的component intent.setComponent(raw.getComponent()); } } catch (IllegalAccessException e) { e.printStackTrace(); } } //之后的操作还是和原来一样 return m_base != null && m_base.handleMessage(msg); } }
package com.chan.hook.am; import android.content.ComponentName; import android.content.Intent; import com.chan.hook.StubActivity; import com.chan.hook.util.Constant; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * Created by chan on 16/4/13. */ public class AMSHook implements InvocationHandler { private Object m_base; public AMSHook(Object base) { m_base = base; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //拦截startActivity方法 if ("startActivity".equals(method.getName())) { //查找原始的intent对象 Intent raw = null; final int size = (args == null ? 0 : args.length); int i = 0; for (; i < size; ++i) { if (args[i] instanceof Intent) { raw = (Intent) args[i]; break; } } //看下是否是启动插件中的activity if (raw.getBooleanExtra(Constant.EXTRA_INVOKE_PLUGIN, false)) { //获得原始的ComponentName ComponentName componentName = raw.getComponent(); //创建一个新的Intent Intent intent = new Intent(); //把Component替换为StubActivity的 这样就不会被系统检测到 启动一个没有在AndroidManifest.xml //中声明的activity intent.setComponent(new ComponentName(componentName.getPackageName(), StubActivity.class.getCanonicalName())); //保存原始的intent intent.putExtra(Constant.EXTRA_RAW_INTENT, raw); //替换为新的Intent args[i] = intent; } } //还是按往常一样调用各种函数 return method.invoke(m_base, args); } }
package com.chan.hook.util; /** * Created by chan on 16/4/13. */ public interface Constant { String EXTRA_INVOKE_PLUGIN = "com.chan.hook.util.invoke_plugin"; String EXTRA_RAW_INTENT = "com.chan.hook.util.raw_intent"; }
package com.chan.hook.util; import android.app.Activity; import android.content.Intent; /** * Created by chan on 16/4/14. */ public class Utils { public static void invokePluginActivity(Activity activity, Class<?> who) { Intent intent = new Intent(activity, who); intent.putExtra(Constant.EXTRA_INVOKE_PLUGIN, true); activity.startActivity(intent); } }
效果:
代码下载
猛击此处这里解释下我们是如何hookAMS的,考虑之前我们分析如何hook一个系统服务的例子:例子,当我们启动一个activity的时候,会调用:
我们可以看下:
而这个gDefault呢是一个Singleton对象:
而他的具体实现在:
可见只要我们hook mInstance就行,而它被实例化IActivityManager对象,之后我们只需拦截其中的IActivityManager对象的startActivity方法,检测是否是标识启动一个插件activity,然后替换其中的component。
//看下是否是启动插件中的activity if (raw.getBooleanExtra(Constant.EXTRA_INVOKE_PLUGIN, false)) { //获得原始的ComponentName ComponentName componentName = raw.getComponent(); //创建一个新的Intent Intent intent = new Intent(); //把Component替换为StubActivity的 这样就不会被系统检测到 启动一个没有在AndroidManifest.xml //中声明的activity intent.setComponent(new ComponentName(componentName.getPackageName(), StubActivity.class.getCanonicalName())); //保存原始的intent intent.putExtra(Constant.EXTRA_RAW_INTENT, raw); //替换为新的Intent args[i] = intent; }
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories