Hook startActivity()
2016-12-06 18:04
169 查看
【本人也是小白一个,如有错误欢迎大家指出,本文是个人理解所得。】
Hook点怎么找呢?
总结一下就是:容易找又不容易变的对象
我们都知道Android系统有两个startActivity()方法,第一个是Context的startActivity()方法,第二个是Activity的startActivity()方法。
重点在这里:mMainThread.getInstrumentation().execStartActivity(…),由此可知startActivity()实际上是调用的
请注意
请看代码:
在Activity的attachBaseContext(…)方法中调用这个方法
然后启动一个Activity
最终起作用的是这句话,那么我们HookActivity类中的
因为据查看源码得知Activity的
然后启动一个Activity进行测试。
***下面贴出InstrumentationProxy的代码:
参考文章:http://weishu.me/2016/01/28/understand-plugin-framework-proxy-hook/
什么是Hook呢?
Hook的英文含义是钩子。你可以这么理解用钩子把要Hook的对象勾过来,然后再把我们的替换对象送回去。Hook其实就是把原来对象的替换成我们仿造的对象,替换完成为所欲为。还有一点就是,必须拿到当前对象里的某个属性进行Hook,否则你的hook是失败的,没有意义的。(注意是当前对象)
Hook点
所谓hook点就是你可以实现替换需求的某个对象。Hook点怎么找呢?
总结一下就是:容易找又不容易变的对象
被static、final修饰的变量和单例:在一个进程之内,
静态变量和单例变量是相对不容易发生变化的,因此非常容易定位,而普通的对象则要么无法标志,要么容易改变。我们根据这个原则找到所谓的Hook点。
有这么一句话“Hook的越早越好”为什么这么说呢?因为可以避免产生一些bug,Hook早的话,一些其他对本对象的操作或者本对象对其他的操作都是我们hook以后的对象了。不会产生一些莫名其妙的问题。
Hook例子
【本示例代码的Android版本是19】【注意有的ROM改写了系统源码可能Hook失败】
我们都知道Android系统有两个startActivity()方法,第一个是Context的startActivity()方法,第二个是Activity的startActivity()方法。
1、Hook Context类的startActivity()方法。
Context的startActivity()方法具体实现实在ContextImpl类中,
// code @Override 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); }
重点在这里:mMainThread.getInstrumentation().execStartActivity(…),由此可知startActivity()实际上是调用的
Instrumentation类中的
execStartActivity()方法。
请注意
mMainThread属性,他是
ActivityThread的实例,而ActivityThread 实际上是主线程,主线程只有一个,所以Hook他十分合适。而且ActivityThread类中还有
currentActivityThread()方法,可以获得当前的ActivityThread对象。
请看代码:
public static void hookStartActivity_context() { try { // 先获取到当前的ActivityThread对象 Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread"); currentActivityThreadMethod.setAccessible(true); //currentActivityThread是一个static函数所以可以直接invoke,不需要带实例参数 // 当前的currentActivityThread对象 Object currentActivityThread = currentActivityThreadMethod.invoke(null); // 原始的 mInstrumentation字段 Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation"); mInstrumentationField.setAccessible(true); // 得到当前currentActivityThread对象的mInstrumentation对象 Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread); // 创建代理对象 InstrumentationProxy instrumentationProxy = new InstrumentationProxy(mInstrumentation); // 将得到当前currentActivityThread对象中的mInstrumentation对象替换为我们的代理对象 mInstrumentationField.set(currentActivityThread, instrumentationProxy); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } }
在Activity的attachBaseContext(…)方法中调用这个方法
@Override protected void attachBaseContext(Context newBase) { super.attachBaseContext(newBase); HookHelper.hookStartActivity_context(); }
然后启动一个Activity
getApplicationContext().startActivity(new Intent(this, Main2Activity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); 因为上面的startActivity()方法里有 if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {...} 如果不addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),就会进入这个if方法体里面的代码,会报错。具体请看一下源码。
2、Hook Activity类的startActivity()方法。
找源码得知:Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, child, intent, requestCode, options);
最终起作用的是这句话,那么我们HookActivity类中的
mInstrumentation属性。
/** * @param activity:当前的Activity */ public static void hookStartActivity_activity(Activity activity) { try { Class clazz = Class.forName("android.app.Activity"); // 得到Activity类中的mInstrumentation字段 Field mInstrumentation_Field = clazz.getDeclaredField("mInstrumentation"); mInstrumentation_Field.setAccessible(true); // 得到当前Activity对象的mInstrumentation属性 Instrumentation instrumentation = (Instrumentation) mInstrumentation_Field.get(activity); // 创建代理类 InstrumentationProxy instrumentationProxy = new InstrumentationProxy(instrumentation); // 替换 mInstrumentation_Field.set(activity, instrumentationProxy); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } }
因为据查看源码得知Activity的
mInstrumentation属性是在attachBaseContext(…)方法之后赋值的,因此就不可以在attachBaseContext()方法里Hook,所以我这次是在onCreate(…)方法里Hook。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 调用Hook方法 HookHelper.hookStartActivity_activity(this); setContentView(R.layout.activity_main2); initView(); }
然后启动一个Activity进行测试。
startActivity(new Intent(this, Main2Activity.class));
***下面贴出InstrumentationProxy的代码:
public class InstrumentationProxy extends Instrumentation { private static final String TAG = "InstrumentationProxy"; private Object obj; // 原来的对象 public InstrumentationProxy(Object obj) { this.obj = obj; } public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { Toast.makeText(who, "hook success!", Toast.LENGTH_SHORT).show(); // Hook之前, 打印个日志! Log.d(TAG, "\n执行了startActivity, 参数如下: \n" + "who = [" + who + "], " + "\ncontextThread = [" + contextThread + "], \ntoken = [" + token + "], " + "\ntarget = [" + target + "], \nintent = [" + intent + "], \nrequestCode = [" + requestCode + "], \noptions = [" + options + "]"); try { Method method = Instrumentation.class.getDeclaredMethod("execStartActivity", Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class); return (ActivityResult) method.invoke(obj, who, contextThread, token, target, intent, requestCode, options); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } }
参考文章:http://weishu.me/2016/01/28/understand-plugin-framework-proxy-hook/
相关文章推荐
- Android插件化开发之Hook StartActivity方法
- Android插件化开发之Hook StartActivity方法
- Hook startActivity()函数追加一条日志
- Android插件化开发之Hook StartActivity方法
- Android插件化开发之Hook StartActivity方法
- 插件化开发---Hook之AMS\PMS、startActivity2种方式
- 调用startActivityForResult,onActivityResult无响应的问题
- Android中startActivityForResult()的用法
- startActivityForResult的用法和demo
- 0729 Activity之startActivityForResult
- startActivityForResult()的用法
- Android在调用startActivityForResult时注意的问题
- startActivityForResult用法详解
- 解决调用startactivityforresult后,onactivityresult立刻响应
- startActivityForResult()的用法
- startActivityForResult与startActivity的不同之处
- Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag
- (8)调用Fragment自身的startActivityForResult并在Fragment中接收返回结果
- Android startActivityForResult的使用
- 使用事件总线eventbus替代startactivityforresult