代理Hook

来源:互联网 发布:休斯顿国际电影节知乎 编辑:程序博客网 时间:2024/06/02 02:15

【本人也是小白一个,如有错误欢迎大家指出,本文是个人理解所得。】

什么是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@Overridepublic 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(…)方法中调用这个方法

@Overrideprotected 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。

@Overrideprotected 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/

0 0
原创粉丝点击