您的位置:首页 > 移动开发 > Android开发

Hook startActivity()

2016-12-06 18:04 169 查看
【本人也是小白一个,如有错误欢迎大家指出,本文是个人理解所得。】

什么是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 startActiv