前言
一般我们的app都是有登陆这个功能的,但是有些app并不是一进入就需要登陆的,而是用到一些和登陆相关的功能,比如要用到用户的信息这些。那么当我们要跳到购物车界面,但是我们的购物车是要用到用户信息的,那么我们就的先去登陆,登陆完成之后接着跳到购物车界面。
流程
一般情况下我们是点击之前判断有没有登陆,没有的话 去登陆,然后登陆完成之后,在登陆界面根据来源去跳转下一个页面,一般都是这样写的。但是假如业务复杂了有商品详情页,点赞,评论这些呢,有好多,那么是不是我们都要在登陆界面写很多这样无用的代码呢?肯定不能这样写了,这个时候就要想有什么办法能在跳转的过程中来判断有没有登陆呢,如果没有登陆,去登陆,登陆完成后跳转目标页面,有登陆,就直接跳转。那么重点来了,继续。
Hook
Hook技术也称为钩子函数,钩子函数世纪是一个处理消息的程序段,通过系统调用,把它挂入系统,在系统没有调用该函数前,钩子函数就先捕获消息,这样钩子函数的到控制权,这时钩子函数即可以加工处理该函数的执行行为。
Hook技术实现途径
- 找到hook点(就是寻找要改变的方法的过程)。
- 找到hook点,将我们要执行前的代码和执行后的代码插入到hook点
先看看效果:
。。。gif图片没传上去,贴了一个外部链接
http://note.youdao.com/noteshare?id=3ae2c1faeb9943337247d8fa4357b24e
如何实现Hook
1、利用系统内部提供的接口,通过实现该接口,然后输入进系统
2、动态代理
3、Hook的条件:要hook的那个方法所属的对象一定是静态的,如果不是静态的,是new出来的,那么拿不到系统的对象,hook就没有意义。
分析绕过AMS检查
- 那么我们要在登陆的过程中做事情,并且还不想侵入我们的业务代码,只能看源码里面在我们调用了startActivity之后系统干了什么事情,能不能找一个hook点
在调用startActivity的时候,内部是最终调用的startActivityForResult
Activity类
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
//这里调用的mInstrumentation里面的execStartActivity的方法 然后得到的返回值
Instrumentation.ActivityResult ar =mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options);
....
}
那么我们继续往下看,只看核心主干代码,其他细节的先不看
Instrumentation类
public ActivityResult execStartActivity(...){
...
//这里是又是通过ActivityManager.getService()得到一个对象 然后去startActivity的
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
...
}
从上面看出又调用到了ActivityManager这个类,当然每个版本的里面或多或少都有些不同 我这里是28(8.0),23的源码这里是ActivityManagerNative,所以hook的话 适配起来比较麻烦
ActivityManager
...
/**
*获取一个单例的对象
*/
public static IActivityManager getService(){
//这里调用的get 其实就是得到这个单例的对象
return IActivityManagerSingleton.get();
}
...
private static final Singleton<IActivityManager> IActivityManagerSingleton=new Singleton<IActivityManager>(){
@Override
protected void create(){
final IBinder b= ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am= IActivityManager.Sub.asInterface(b);
return am;
}
}
...
从上面看出 获得一个IActivityManager的对象,是从这个单例身上获取的, 也就是这个单例身上的mInstnce这个Field,然后是实现了onCreate方法去得到的一个IBinder对象,得到的
public abstract class Singleton<T> {
private T mInstance;// 其实这里返回的就是am
protected abstract T create();
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
上面的源码看完了,那么我们分析下到底该怎么做
我们最终肯定要得到系统通过Binder来返回的IActivityManager这个对象,而且不能new,你new来那和系统没有一毛钱关系,要想拿到这个IActivityManager对象,就要获取Singleton这个类对象,它正好是ActivityManager的一个静态属性。那么直接通过反射拿到ActivityManager的Class对象,然后拿它的静态属性,
-
先通过反射拿到ActivityManager的Class对象,然后拿它的静态属性IActivityManagerSingleton
Class<?> mActivityManagerClass=Class.forName("android.app.ActivityManager"); Field mActivitySigletonField=mActivityManagerClass.getDeclaredField("IActivityManagerSingleton");
-
因为这个属性是静态的,所以设置为可访问的
mActivitySigletonField.setAccessible(true); // 然后我们获取singleton这个对象 Object mSingletonObj=mActivitySigletonField.get(null);
-
然后我们开始拿IActivityManagerSingleton这个对象内部的mInstance属性,也就是我们最重要获取的
Class<?> mSingletonClass=Class.forName("android.util.Singleton"); Field mInstanceField=mzsingletonClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
// 然后获取mInstance这个对象
Object mIactivityManagerObj=mInstanceField.get(mSingletonObj);
4. 我们现在拿到了这个IActivityManager这个由IBinder返回的对象 ,我们要代理它, 绕过AMS检查。一般要想在目标对象之前插入我们的代码,有两种方法,第一种是设置接口,就是内部有提供接口,第二种就是动态代理,然后这里IActivityManager本身就是一个接口,那设置接口这条路行不通,只能是动态代理了(
[https://www.jianshu.com/p/bb8434645a76]:
)
```java
// 创建一个类,实现InvocationHandler这个接口 并且要持有真实对象的引用
startActivityInvocation startActivityInvocation = new startActivityInvocation(mIactivityManagerObj);
Object mProxyActivityManager = Proxy.newProxyInstance(getClass().getClassLoader(), mIactivityManagerObj.getClass().getInterfaces(), startActivityInvocation);
// 实现InVocationHandler接口
class startActivityInvocation implements InvocationHandler {
private final Object obj;
// 代理对象内部持有真实对象的引用
public startActivityInvocation(Object mIActivityManagerObj) {
this.obj = mIActivityManagerObj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Intent oldIntent = null;
if ("startActivity".equals(method.getName())) {
Log.e(TAG, "hook到了startActivity");
// hook startActivity 我们拿到要启动的Intent
int index = 0;// 记录一下是第几个参数
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Intent) {
index = i;
oldIntent = (Intent) args[i];
}
}
Intent newIntent = new Intent();
// 清单文件只有ProxyActivity是注册的,其他activity是没有注册的。
//为什么要用ProxyActivity 因为这一步是要绕过AMS的检查,清单文件注册了的
newIntent.setComponent(new ComponentName(context, ProxyActivity.class));
newIntent.putExtra("oldIntent", oldIntent);
//将真实的Intent隐藏起来创建新的 然后给系统
args[index] = newIntent; // 然后给原样放进去
}
return method.invoke(obj, args);
}
}
上面是将我们真实跳转的activity先给隐藏起来,躲过AMS的检查,到这里已经完成了第一步
偷梁换柱
我们的Activity在一系列的调用之后,它的创建消息是在ActivityThread里面的handle里面进行处理的,那么我们就要拿到ActivityThread这个对象的handler对象,但是个是成员变量里面直接new的,所以拿它肯定不行,那么在往上找看ActivityThread
ActivityThread类
...
private static volatile ActivityThread sCurrentActivityThread;
final H mH = new H();
...
可以看到有一个sCurrentActivityThread 这个静态属性,而且是自己持有自己,那这就好办了
-
先通过这个静态变量 拿到ActivityThread的对象
Class<?> mActivityThreadClass=Class.forName("android.app.ActivityThread"); Field sCurrentActivityThreadField=mActivityThreadClass.getDeclaredField("sCurrentActivityThread"); sCurrentActivityThreadField.setAccessible(true); Object mActivityThreadObj=sCurrentActivityThreadField.get(null); // 这一步就获取到了ActivityThread的对象
-
然后在获取对象里面的mH这个成员变量
Field mHField=mActivityThreadClass.getDeclaredField("mH"); mHField.setAccessible(true); // 然后我们通过get方法 拿ActivityThread对象身上的mh的对象 Object mHandleObj=mHField.get(mActivityThreadObj);
-
现在我们拿到了ActivityThread里面的handler对象,我们就要对这个handler的handleMessage方法进行hook,这里的话Handler里面有提供接口,所以直接注入接口就可以了,不需要动态代理。
handler 源码 ... // 这个是从消息队列取出来之后 第一个处理的方法 public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { // 如果callback 不为空,就去执行callback里面的handlemessage方法 if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } final Callback mCallback; /** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. */ public interface Callback { /** * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired */ public boolean handleMessage(Message msg); }
从上面handler的源码可以看出,但是这个里面没有提供setCallback这个方法,所以也只能通过反射,往里面注入一个callback
-
反射获取callback,注入一个callback
Class<?> mHandlerClass=Class.forName("android.os.Handler"); Field mHandleCallbackField=mHandleClass.getDeclaredField("mCallback"); mHandleCallbackField.setAccessible(true); // 给callback注入 HandlerCallback handlerCallback = new HandlerCallback((Handler) mHandlerObj); mHandleCallbackField.set(mHandleObj,); // 实现了Handle.Callback接口的类 用来注入用的 class HandleCallback implements Handle.Callback{ Objecrt mHandle;//持有真实的对象 最后要调用系统的handleMessage 不然activity都启动不了 public HandlerCallback(Handler handler) { this.mHandle = handler; } @Override public boolean handleMessage(Message msg) { Log.e("handle", msg.what + ""); // 继续让系统处理 if (msg.what == 100) { // 系统发出的启动activity的消息的what就是100 handleLauchActivity(msg); } handler.handleMessage(msg); return true; } } /** * 绕过AMS检查后,将我们真实的取出来 进行还原 * * @param msg */ private void handleLauchActivity(Message msg) { Object obj = msg.obj; try { Field mIntentField = obj.getClass().getDeclaredField("intent"); mIntentField.setAccessible(true); // proxyActivity 取出来是这样的 Intent realIntent = (Intent) mIntentField.get(obj); Intent oldIntent = realIntent.getParcelableExtra("oldIntent"); if (oldIntent != null) { // 然后在这里统一处理跳转activity //判断有没有登陆成功 SharedPreferences sharedPreferences = context.getSharedPreferences("plugin", context.MODE_PRIVATE); boolean isLogin = sharedPreferences.getBoolean("login", false); if (isLogin) { // 登陆了 按照原来的目标 realIntent.setComponent(oldIntent.getComponent()); } else { // 没有登陆 ComponentName newComponent=new ComponentName(context,LogActivity.class); realIntent.setComponent(newComponent); realIntent.putExtra("extraIntent",oldIntent.getComponent().getClassName()); } } } catch (Exception e) { e.printStackTrace(); } }
至此,大概流程就走完了,上面通过简单的分析了activity启动中一些重要的节点,然后进行hook,从而达到我们最终的目的,这样通过一个例子 熟悉了源码 也能在日常业务处理中用到。
我在下面贴一下整个的hook的处理过程,就一个类
public class HookUtil { /** * public abstract class Singleton<T> { * private T mInstance; * <p> * protected abstract T create(); * <p> * public final T get() { * synchronized (this) { * if (mInstance == null) { * mInstance = create(); * } * return mInstance; * } * } * } */ private static final String TAG = "HooUtil"; private Context context; /** * hook startActivity 让开启activity走自己的代理类 */ public void hookStartActivity(Context context) { this.context = context; // 基于28的源码 try { Class<?> mActivityManagerClass = Class.forName("android.app.ActivityManager"); //在拿到 IActivityManagerSingleton 这个静态属性 Field mActivitySigletonField = mActivityManagerClass.getDeclaredField("IActivityManagerSingleton"); //由于是私有的 要设置为可访问的 mActivitySigletonField.setAccessible(true); // 由于是静态的 我直接获取 singleton这个对像 Object mSingletonObj = mActivitySigletonField.get(null); //拿到了这个对象 但是我不需要这个对象,我是是需要它内部的mInstance这个对象 Class<?> mSingletonClass = Class.forName("android.util.Singleton"); Field mInstanceField = mSingletonClass.getDeclaredField("mInstance"); mInstanceField.setAccessible(true); //然后获取这个mInstance Object mIactivityManagerObj = mInstanceField.get(mSingletonObj); // 现在获取到了我们要处理的这个对象 ,然后我们通过动态代理 来生成一个代理对象 startActivityInvocation startActivityInvocation = new startActivityInvocation(mIactivityManagerObj); Object mProxyActivityManager = Proxy.newProxyInstance(getClass().getClassLoader(), mIactivityManagerObj.getClass().getInterfaces(), startActivityInvocation); mInstanceField.set(mSingletonObj, mProxyActivityManager);// 给将我们生成的动态代理的对象设置进去 hookActivityThreadMH(); } catch (Exception e) { e.printStackTrace(); Log.e(TAG, "hook startAcgtivity 出现了异常" + e.getMessage()); } } class startActivityInvocation implements InvocationHandler { private final Object obj; // 代理对象内部持有真实对象的引用 public startActivityInvocation(Object mIActivityManagerObj) { this.obj = mIActivityManagerObj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Intent oldIntent = null; if ("startActivity".equals(method.getName())) { Log.e(TAG, "hook到了startActivity"); // hook startActivity 我们拿到要启动的Intent int index = 0; for (int i = 0; i < args.length; i++) { if (args[i] instanceof Intent) { index = i; oldIntent = (Intent) args[i]; } } Intent newIntent = new Intent(); //为什么要用ProxyActivity 因为这一步是要绕过AMS的检查,清单文件注册了的 newIntent.setComponent(new ComponentName(context, ProxyActivity.class)); newIntent.putExtra("oldIntent", oldIntent); //将真实的Intent隐藏起来创建新的 然后给系统 args[index] = newIntent; } return method.invoke(obj, args); } } /** * hook ActivityThread中的handler,处理我们的启动actiivty的消息, */ private void hookActivityThreadMH() { //1 、我们要想办法代理处理那个HandleMessage() 所以就去找静态的, // 找handler,但是发现handler是new出来的,所以在找ActivityThread这个类,最后找到 // 一个 sCurrentActivityThread 这个字段,是静态,而且是它持有了自己,那么直接获取class对象 try { Class<?> mActivityThreadClass = Class.forName("android.app.ActivityThread"); Field mSCurrntActivityThreadField = mActivityThreadClass.getDeclaredField("sCurrentActivityThread"); //然后我们通过静态对象 可以获取到ActivityThread的对象,而且是系统创建的对象 mSCurrntActivityThreadField.setAccessible(true); Object mActivityThreadObj = mSCurrntActivityThreadField.get(null); //在获取反射获取mh Field mHandlerField = mActivityThreadClass.getDeclaredField("mH"); mHandlerField.setAccessible(true); //这一步我们就拿到了mH这个对象 Object mHandlerObj = mHandlerField.get(mActivityThreadObj); // 下面我就要考虑是用动态代理还是用设置接口,来让handlerMessage先处理我们的,通过handler的dispatchMessage的方法 而且内部是提供接口, // 那就这里不用动态代理了,用提供的接口 //接着还是用反射 ,给注入一个callback Class<?> mHandlerClass = Class.forName("android.os.Handler"); Field mHandlerCallbackField = mHandlerClass.getDeclaredField("mCallback"); mHandlerCallbackField.setAccessible(true); //给注入一个callback HandlerCallback handlerCallback = new HandlerCallback((Handler) mHandlerObj); mHandlerCallbackField.set(mHandlerObj, handlerCallback); } catch (Exception e) { e.printStackTrace(); } } class HandlerCallback implements Handler.Callback { private final Handler handler; public HandlerCallback(Handler handler) { this.handler = handler; } @Override public boolean handleMessage(Message msg) { Log.e("handle", msg.what + ""); // 继续让系统处理 9.0 的是159 9.0之下的是100 if (msg.what == 100 || msg.what == 159) { handleLauchActivity(msg); } handler.handleMessage(msg); return true; } } /** * 绕过AMS检查后,将我们真实的取出来 进行还原 * * @param msg */ private void handleLauchActivity(Message msg) { Object obj = msg.obj; try { Field mIntentField = obj.getClass().getDeclaredField("intent"); mIntentField.setAccessible(true); // proxyActivity 取出来是这样的 Intent realIntent = (Intent) mIntentField.get(obj); Intent oldIntent = realIntent.getParcelableExtra("oldIntent"); if (oldIntent != null) { // 然后在这里统一处理跳转activity //判断有没有登陆成功 SharedPreferences sharedPreferences = context.getSharedPreferences("plugin", context.MODE_PRIVATE); boolean isLogin = sharedPreferences.getBoolean("login", false); if (isLogin) { // 登陆了 按照原来的目标 realIntent.setComponent(oldIntent.getComponent()); } else { // 没有登陆 ComponentName newComponent=new ComponentName(context,LogActivity.class); realIntent.setComponent(newComponent); realIntent.putExtra("extraIntent",oldIntent.getComponent().getClassName()); } } } catch (Exception e) { e.printStackTrace(); } } /** * * // 这个是handler内部的处理方法 * // 我们发现它会 先看mCallback 不为空,去执行callback里面的 * // handleMessage 方法,返回为true 就直接返回了 * * public void dispatchMessage(Message msg) { * if (msg.callback != null) { * handleCallback(msg); * } else { * if (mCallback != null) { * if (mCallback.handleMessage(msg)) { * return; * } * } * handleMessage(msg); * } * } */ }
网友评论