在此立志:我要努力大学毕业进BAT
实习目标:Activity不需要注册在清单即可通过intent启动。
- 有些文章叫做hook技术。大致内容为监听方法或者的调用或触发,期间修改方法参数或者返回值达到无须需改app源码即可修改app。如Xpose有插件可防止qq撤销消息。
- 我们今天监听activity的启动然后进行方法修改,期间会用动态代理和大量的反射
Activity启动流程分析 第一章
首先学习activity启动流程:
假设我们界面有个按钮 点击后触发activity的onclick方法
//activity
public void onClick(View view) {
Intent intent = new Intent(this,SecondActivity.class);
startActivity(intent);
}
复制代码
按照我们的理解接下来的事情自然就是界面跳转了。我主要研究的startActivity内部实现的调用过程到启动一个activity流程。
这个方法的实现是在ContextImpl.java 中然后我们再看继承关系图:
结果方法继承关系里面并没有ContextImpl.java 。他其实被包裹在ContextWrapper.java中
//ContextWrapper.java
public class ContextWrapper extends Context {
Context mBase;
...省略其他代码
@Override
public void startActivity(Intent intent) {
mBase.startActivity(intent);
}
...省略其他代码
}
复制代码
可以看到里面有个mBase属性对象。在调用startActivity的时候会调用mBase的startActivity方法。这个mBase就是ContextImpl.java
现在来分析ContextImpl.java
我们看看这个类的startActivity的方法的实现
//ContextImpl.java
@Override
public void startActivity(Intent intent) {
warnIfCallingFromSystemProcess();
startActivity(intent, null);
}
复制代码
直接看startActivity(intent, null); 方法,前一个方法是用于进程检测的。
@Override
public void startActivity(Intent intent, Bundle options) {
//...省略代码 只看方法的重点
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null,
(Activity) null, intent, -1, options);
}
复制代码
可以看到此方法调用 mMainThread.getInstrumentation() 获取一个对象然后调用对象的方法啊execStartActivity。其中 mMainThread对象的声明为final ActivityThread mMainThread;
mMainThread.getInstrumentation() 查看实现如下
//ActivtyThread.java
public Instrumentation getInstrumentation()
{
return mInstrumentation;
}
复制代码
所以我们继续查看Instrumentation 对象的execStartActivity方法
//Instrumentation.java
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {
//...省略方法其他方法
int result = ActivityManagerNative.getDefault().startActivity(一堆参数);
//...
return null;
}
复制代码
上面的代码就是传入的参数特别的参数太多。所以用四个字代替。我们看到ActivityManagerNative.getDefault() 获取一个对象然后调用startActivity()
ActivityManagerNative.getDefault() 方法探究
//ActivityManagerNative.java
static public IActivityManager getDefault() {
return gDefault.get();
}
复制代码
gDefault是ActivityManagerNative.java属性对象
声明如下:
//ActivityManagerNative.java
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
复制代码
Singleton又是什么?这个是一个单例工具类,当你第一次调用此类的get() 会回调使用则自己实现的抽象方法create() 进而进行单例操作
此类完整代码:
//Singleton.java
package android.util;
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;
}
}
}
复制代码
这类我们以后反射其拿到IActivityManager 大家可以简单可以
大家再回过头看看实现抽象的方法create()
//ActivityManagerNative.java
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
//从系统服务管理器获取activityMnagerServer(这是一个系统服务,所以需要AIDL进行进程间通信)
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
//利用IBinder 对象转化为对应实例化接口
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
复制代码
从上面的例子可以看到,看到ActivityManagerNative.getDefault() 返回了远程服务对象IActivityManager 接口。
再回头看看Instrumentation 对象的execStartActivity方法
//Instrumentation.java
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {
//...省略方法其他方法
int result = ActivityManagerNative.getDefault().startActivity(一堆参数);
//...
return null;
}
复制代码
可以得出以下结论:获取一个远程服务对象接口IActivityManager(并且是单例)然后调用IActivityManager的startActivity方法。此时会回调到远程服务ActivityManagerServer的真正startActivity接口方法。里面会检查你传入的Intent对象的Activity是否在清单内,如果你的activity不在清单内,就是一个异常.但是我们不需要再看ActivityManagerServer源码,因为它是另一个app进程,所以无法干涉。如果可以干涉的话,Android系统就太不稳定了,肯定会有流氓开发者,让你按上一个app干扰其他app那不是奔了?所以我们无法hook到远程服务,但是IActivityManager我们却是可以用手脚的。因为那是服务端存在客户端一个代理接口对象、(这一块需要大家简单了解下AIDL的知识。如果不理解没关系,只需记住它做些手脚)
看完上面的很乱吧,看下图来屡屡思路:
(大家可以右键保存看大图)
这时候读者一定会问?既然ActivityManagerServer启动activity我们无法干涉那么后面干嘛?
我们仔细看上面的图IActivityManager调用的时候会把要启动的Activity的Intent去给ActivityManagerServer,假设我们此时用动态代理在IActivityManager调用远程服务之前,把在一个在清单文件注册过的Activity的Intent替换不就通过校验了?当然这时候你又会想既然替换通过了验证,但是我们真正要启动的Activity都被替了哪还有什么意义?这里过验证后系统没有真正启动Activity,因为这个ActivityManagerServer最终回调到 * * 启动的 。这里大家先不要着急,先通过验证再说。
第一个小目标 实现替换
假设我们我们有3个Activity第一个为MainActivity,第二个PlaceholdActivity 第三个为SecondActivity
- MainActivity 在清单文件注册过,继承哪个Activity都无所谓
- PlaceholdActivity 在清单文件注册过,继承哪个Activity都无所谓
- SecondActivity没有清单文件注册过,并且继承Activity。(不要继承不然会失败AppCompatActivity)
其中我们在MainActivity 点击一个按钮触发其onclick方法调用startActivity跳转到SecondActivity
//MainActivity.java
public void onClick(View view) {
Intent intent = new Intent(this,SecondActivity.class);
startActivity(intent);
}
复制代码
按照常理因为SecondActivity没有在清单文件注册过所以会失败,我们的目的把这个意图换成启动PlaceholdActivity 的意图
步骤1
创建一个Application类名为APP.java(记得在清单文件中关联哦)
重写attachBaseContext方法
- attachBaseContext方法介绍:
此方法会在oncreate方法前调用,还记得我们说的ContextWrapper和ContextImpl吗?再回过头看看吧(application也是继承ContextWrapper的)。
public class ContextWrapper extends Context {
Context mBase;
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
复制代码
attachBaseContext是在传入mBase对象赋值的回调的。此时你其实就可以使用上下文了。
此时我们的代码如下:
//APP.java
public class APP extends Application {
//后面有用,用于保存原目标Activity的Intent的KEY 大家先看看
private static final String KEY_EXTRA_TARGET_INTENT = "EXTRA_TARGET_INTENT";
private static final String TAG = "APP";
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
}
}
复制代码
我们前面分析知道想要在过Activity的清单文件校验,就必须在IActivityManager调用startActivity方法之前进行Intent意图替换。
所以我们直接用动态代理IActivityManager的IActivityManager
首先要动态代理你必须拿到IActivityManager对象。而此对象保存在ActivityManagerNative的属性对象gDefault中。我们调用属性对象gDefault(Singleton类)的get方法或者得到其内部属性对象mInstance
mInstance是用于保存一个单例对象的实例。很混乱?再回头看看Singleton
//Singleton.java
public abstract class Singleton<T> {
//保存对象实例
private T mInstance;
//Singleton对象为空会调用此方法得到实例
protected abstract T create();
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
复制代码
好了先获取gDefault对象吧,我们看看ActivityManagerNative对其的声明
//ActivityManagerNative.java
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
复制代码
我们发现是个静态类,所以我们应该笑了,静态类通过反射机制很容易得到对象实例
- 所以得到如下代码:
public class APP extends Application {
private static final String KEY_EXTRA_TARGET_INTENT = "EXTRA_TARGET_INTENT";
private static final String TAG = "APP";
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
try {
Class<?> ams_class = Class.forName("android.app.ActivityManagerNative");
Field gDefault = ams_class.getDeclaredField("gDefault");
//因为gDefault是private所以用发射机制打破访问限制
gDefault.setAccessible(true);
//得到gDefault实例对象 因为是stati属性所以get传入null即可得到
Object gDefault_instance = gDefault.get(null);
//单例工具类class
Class<?> singleton_class = Class.forName("android.util.Singleton");
//单例工具类的一个属性对象保存的是IActivityManager对象实例
Field mInstance = singleton_class.getDeclaredField("mInstance");
//打破封装访问
mInstance.setAccessible(true);
//传入gDefault对象实例得到IActivityManager对象实例
final Object mInstance_instance = mInstance.get(gDefault_instance);
//动态代理,这个不会我真不知道讲下去。此方法会返回一个实现IActivityManager接口的实例对象
//拿着这个对象 替换单例工具类gDefault 的mInstance即可实现
Object proxyInstance = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), mInstance_instance.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// IActivityManager接口方法太多 我们只关心它启动activity的接口方法
if (method.getName().equals("startActivity")) {
//获取调用此方法传入的参数 我们这里只要Intent替换,所以只需要Intent替换
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
if (arg instanceof Intent) {
Intent intent = new Intent();
//PlaceholdActivity因为在清单文件注册过所以 创建一个新的Intent替换原来的SecondActivity的意图。后面用到
// 并将其保存到新的Intent中
ComponentName componentName = new ComponentName("com.example.fmy.myapplication", PlaceholdActivity.class.getName());
intent.setComponent(componentName);
intent.putExtra(KEY_EXTRA_TARGET_INTENT, ((Intent) arg));
args[i] = intent;
return method.invoke(mInstance_instance, args);
}
}
}
return method.invoke(mInstance_instance, args);
}
});
// 替换单例工具类gDefault 的mInstance实现动态代理
mInstance.set(gDefault_instance, proxyInstance);
} catch (Exception e) {
e.printStackTrace();
}
}
}
复制代码
你运行代码后发现不管startActivity填入什么意图都是启动PlaceholdActivity就证明你成功了
Activity启动流程分析之Handler源码分析 第二章
这里只会简单讲讲不会过多。不然后脱离主旨
Looper循环器,不断从对应线程MassgaeQueue中死循环拿取mesaage然后发送到对应的handler
handler有消息的会调用如下方法:
//Handler.java
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//如果mCallback 不为空并且处理后返回true 那么不会调用我们自己写handlerMessage处理消息。
handleMessage(msg);
}
}
复制代码
- 我们后面会用此mCallback 就行再次替换Intent
我们知道在学习javase的时候我们程序是从main方法进入,那么安卓的app入门方法在哪?ActivityThread.java中存在的main方法就是
//ActivityThread.java
public static void main(String[] args) {
//...代码省略
//Looper类一些初始化,会从ThreadLocal对象拿取当前线程的Looper
//关于ThreadLocal不做过多讲述,简单就是用线程资源拷贝。
//假设你有个变量a 你可以拷贝三份到ThreadLocal中不同的线程
Looper.prepareMainLooper();
//...代码省略
ActivityThread thread = new ActivityThread();
//开启一个子线程,又可以叫binder线程用于ams(ActivityManagerServer通AIDL来进程间通信,如果有信息那么从子线程发送message给主线程handler进行处理)
thread.attach(false);
//...代码省略
//开启一个死循环遍历messageQueue(里面存放messager),如果有就,所以前面才开启一个子线程,不然怎么跟AMS通信?
Looper.loop();
//...代码省略
}
复制代码
看看 thread.attach(false); 方法
//ActivityThread.java
private void attach(boolean system) {
sCurrentActivityThread = this;
//...忽略代码
}
复制代码
我们只看到方法 把当前线程对象(ActivityThread)保存到sCurrentActivityThread 我们后面会用到所以这里讲解。
我们前面第一章分析中到通过IActivityManager发送startActivity方法到ActivityManagerServer中,然后我们知道知道在ActivityThread类中main方法死循环前开启了个子线程,这个线程会接收ActivityManagerServer回馈的信息,信息封装在Message中然后添加主线程MeassgeQueue中,当主线程的Loop遍历到有信息的时候交给主线程的Handler处理。
- 当ActivityManagerServer收到startActivity信息的信息的时候,会发送一个message给Handler处理。其中message.what=LAUNCH_ACTIVITY,LAUNCH_ACTIVITY=100
我们看看Activity中Handler在哪
public class AcitvityThread{
final H mH = new H();
private class H extends Handler {
//。。。代码省略
}
}
复制代码
我们在看看mH这个Handler怎么处理private class H extends Handler 的事件
//ActivityThread.java
class H extends Handler{
//.....
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
//从msg拿出ActivityClientRecord 对象,里面包含启动activity的Intent
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
//带着ActivityClientRecord 对象启动activity
handleLaunchActivity(r, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
}
}
}
复制代码
大致可简单的理解从msg获取ActivityClientRecord 对象,内包含启动Intent启动意图等信息,还记得我们前面保存一个Intent到一个新的Intent上吗?这时候其实你可以从这个Intent取出来了。调用handleLaunchActivity去启动,最后大家想生命周期可以自己往下深究。
//ActivityThread.java
static final class ActivityClientRecord {
IBinder token;
int ident;
Intent intent;
String referrer;
//....
}
复制代码
先来整理回调流程:
(因为懒省略很多细节)
所以我们可以handler的mCallback赋值,此时handler回调lancherActivity的时候,我们知道mCallback不为空的时候,先会回调mCallback再根据其返回值在判断要不要对其自己实现的handlerMessage(true的时候不会调用自己实现的方法)
所以先获取handler对象,而handler对象存在ActivityThread中,再来看看ActivityThread.java对其的声明
//ActivityThread.java
public final class ActivityThread {
final H mH = new H();
}
复制代码
因为只mH不是静态类型,所以先要获取ActivityThread 对象才能获取mH实例。
还记得我们前面分析ActivityThread的main方法吗?
内部调用
public static void main(String[] args) {
//...
ActivityThread thread = new ActivityThread();
thread.attach(false);
//...
}
复制代码
再看看 thread.attach(false); 方法
//ActivityThread.java
private void attach(boolean system) {
sCurrentActivityThread = this;
//....
}
复制代码
可以看到ActivityThread对象保存在ActivityThread的属性对象sCurrentActivityThread
//ActivityThread.java
public final class ActivityThread {
//...
private static ActivityThread sCurrentActivityThread;
//...
}
复制代码
静态属性证明你懂的,你可以利用反射机制获取对象
所以综上代码综合可得:
//APP.java
package com.example.fmy.myapplication;
public class APP extends Application {
private static final String KEY_EXTRA_TARGET_INTENT = "EXTRA_TARGET_INTENT";
private static final String TAG = "APP";
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
//过检验代码省略
Class<?> activityThread_class = Class.forName("android.app.ActivityThread");
Field sCurrentActivityThread_field = activityThread_class.getDeclaredField("sCurrentActivityThread");
sCurrentActivityThread_field.setAccessible(true);
//获取静态属性对象实例
Object activityThread_instance = sCurrentActivityThread_field.get(null);
Field mH_field = activityThread_class.getDeclaredField("mH");
mH_field.setAccessible(true);
//获取handler对象实例
Object mH_insance = mH_field.get(activityThread_instance);
Field mCallback_field = Handler.class.getDeclaredField("mCallback");
mCallback_field.setAccessible(true);
//给handler添加mCallback
mCallback_field.set(mH_insance, new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == 100) {
//得到ActivityClientRecord
Object obj = msg.obj;
try {
//得到Intent对象
Field intent_field = obj.getClass().getDeclaredField("intent");
intent_field.setAccessible(true);
Intent intent = (Intent) intent_field.get(obj);
//取出我们前面存在Intent里的原本没有注册在清单文件的Activity的Intent
Intent target_intent = intent.getParcelableExtra(KEY_EXTRA_TARGET_INTENT);
if (target_intent != null) {
intent.setComponent(target_intent.getComponent());
}
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onCreate() {
super.onCreate();
}
}
复制代码
最后整合下过验证的代码如下:
//APP.java
package com.example.fmy.myapplication;
public class APP extends Application {
private static final String KEY_EXTRA_TARGET_INTENT = "EXTRA_TARGET_INTENT";
private static final String TAG = "APP";
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
try {
Class<?> ams_class = Class.forName("android.app.ActivityManagerNative");
Field gDefault = ams_class.getDeclaredField("gDefault");
//因为gDefault是private所以用发射机制打破访问限制
gDefault.setAccessible(true);
//得到gDefault实例对象 因为是stati属性所以get传入null即可得到
Object gDefault_instance = gDefault.get(null);
//单例工具类class
Class<?> singleton_class = Class.forName("android.util.Singleton");
//单例工具类的一个属性对象保存的是IActivityManager对象实例
Field mInstance = singleton_class.getDeclaredField("mInstance");
//打破封装访问
mInstance.setAccessible(true);
//传入gDefault对象实例得到IActivityManager对象实例
final Object mInstance_instance = mInstance.get(gDefault_instance);
//动态代理,这个不会我真不知道讲下去。此方法会返回一个实现IActivityManager接口的实例对象
//拿着这个对象 替换单例工具类gDefault 的mInstance即可实现
Object proxyInstance = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), mInstance_instance.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// IActivityManager接口方法太多 我们只关心它启动activity的接口方法
if (method.getName().equals("startActivity")) {
//获取调用此方法传入的参数 我们这里只要Intent替换,所以只需要Intent替换
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
if (arg instanceof Intent) {
Intent intent = new Intent();
//PlaceholdActivity因为在清单文件注册过所以 创建一个新的Intent替换原来的SecondActivity的意图。后面用到
// 并将其保存到新的Intent中
ComponentName componentName = new ComponentName("com.example.fmy.myapplication", PlaceholdActivity.class.getName());
intent.setComponent(componentName);
intent.putExtra(KEY_EXTRA_TARGET_INTENT, ((Intent) arg));
args[i] = intent;
return method.invoke(mInstance_instance, args);
}
}
}
return method.invoke(mInstance_instance, args);
}
});
// 替换单例工具类gDefault 的mInstance实现动态代理
mInstance.set(gDefault_instance, proxyInstance);
//过检验结束
Class<?> activityThread_class = Class.forName("android.app.ActivityThread");
Field sCurrentActivityThread_field = activityThread_class.getDeclaredField("sCurrentActivityThread");
sCurrentActivityThread_field.setAccessible(true);
//获取静态属性对象实例
Object activityThread_instance = sCurrentActivityThread_field.get(null);
Field mH_field = activityThread_class.getDeclaredField("mH");
mH_field.setAccessible(true);
//获取handler对象实例
Object mH_insance = mH_field.get(activityThread_instance);
Field mCallback_field = Handler.class.getDeclaredField("mCallback");
mCallback_field.setAccessible(true);
//给handler添加mCallback
mCallback_field.set(mH_insance, new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == 100) {
//得到ActivityClientRecord
Object obj = msg.obj;
try {
//得到Intent对象
Field intent_field = obj.getClass().getDeclaredField("intent");
intent_field.setAccessible(true);
Intent intent = (Intent) intent_field.get(obj);
//取出我们前面存在Intent里的原本没有注册在清单文件的Activity的Intent
Intent target_intent = intent.getParcelableExtra(KEY_EXTRA_TARGET_INTENT);
if (target_intent != null) {
intent.setComponent(target_intent.getComponent());
}
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onCreate() {
super.onCreate();
}
}
复制代码
GIT源码附上源码连接