Android基础-Hook技术介绍

372 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

Hook介绍

Hook一般用在Android技术安全以及逆向开发当中,也是Android安全人员必须具备的技术能力。Hook技术主要目的是绕过系统限制,修改代码执行内容动态化调用一些API等常规功能。 根据Android平台语言分类Hook分为:Hook JavaHook NativeHook Java是通过反射和代理方法实现;Hook Native则是通过JNI接口函数完成。

代理模式

代理模式也称为委托模式:为其他对象提供一种代理以控制对这个对象的访问称为代理模式。代理模式作为设计模式,通俗理解像是生活中代购,通过中间一层关系实现一个任务。具体代码实现可以参考代理模式文章。

同时代理也分为静态代理和动态代理两种形式。静态代理和动态代理区别在于动态代理在代码运行时反射动态生成代理类。

public class DynamicProxy implements InvocationHandler{

    private Object obj;
    
    public DynamicProxy(Object obj){
        this.obj = obj;
    }

    @override
    public Object invoke(Object arg,Method arg1,Object[] arg2) throws Throwable{
        Object result = method.invoke(obj,args);
        // 代理的对象获取到之后 在中间执行其他操作
        return result;
    }
}

RealSubject realSubject = new RealSubject();

DynamicProxy dynamicProxy = new DynamicProxy(realSubject);
ClassLoader loader = realSubject.getClass().getClassLoader();

RealSubject subject = (RealSubject) Proxy.newProxyInstance(loader,new Class[]{RealSubject.class},proxy);
subject.Request();

动态代理方法实现代理接口InvocationHandler,被代理类具体方法在invoke方法中执行实现。Hook的时候使用Proxy.newProxyInstance()生成动态代理类,当被Hook对象执行方法Request()时就会执行代理对象的invoke方法。

同时动态代理类还能修改被代理类执行结果,例如invoke返回结果是int值且值为10,可将result修改成希望返回结果值比如11或者其他数值。

Hook实现

Hook用于劫持对象,被劫持对象叫做Hook点。在Android开发中常用方法是startActivity,以此为例子来实现hook操作。在startActivityForResult方法源码中已知使用mInstrumentationexecStartActivity方法来启动ActivitymInstrumentationActivity的成员变量,因此选择InstrumentationHook点,用代理Instrumentation来替代原始的Instrumentation来完成Hook

public class InstrumentationProxy extends Instrumentation {

    private static final String TAG = "InstrumentationProxy";
    Instrumentation mInstrumentation;
    
    public InstrumentationProxy(Instrumentation instrumentation) {
        mInstrumentation = instrumentation;
    }
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        Log.d(TAG, "Hook" + "hookhookhookhook");
        Class[] pareTyples = {Context.class, IBinder.class, IBinder.class,
                Activity.class, Intent.class, int.class, Bundle.class};
        Object[] pareVaules = {who, contextThread, token, target,
                intent, requestCode, options};
        String methodName = "execStartActivity";
        try {
           // 通过反射找到Instrumentation的execStartActivity方法
            Method execStartActivity = mInstrumentation.getClass().getDeclaredMethod(methodName, pareTyples);
            execStartActivity.setAccessible(true);
            return (ActivityResult) execStartActivity.invoke(mInstrumentation, pareVaules);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

实现InstrumentationProxy代理类之后就在主工程中去代理Activity中原来mInstrumentation对象。其中因为InstrumentationProxy代理改造了execStartActivity方法,当Activity调用startActivity方法时就会打印日志信息从而监控到方法调用。

        Class clazz = Activity.class;
        try {
            Field field = clazz.getDeclaredField("mInstrumentation");//通过Activity.class 拿到 mInstrumentation字段
            field.setAccessible(true);
            Instrumentation mInstrumentation = (Instrumentation) field.get(this); //根据activity内mInstrumentation字段 获取Instrumentation对象
            Instrumentation instrumentationProxy = new InstrumentationProxy(mInstrumentation); ////创建代理对象
            field.set(this, instrumentationProxy); //进行替换
        } catch (Exception e) {
            e.printStackTrace();
        }

Hook总结

hook最好选择静态变量和单例,比较容易定位和代理;寻找hook的节点最好是静态变量和单例对象并且是public的对象和方法;Hook过程可能会遇到使用反射,由于Android不同版本兼容性不同,因此还需要做好API兼容配置。