一个Demo了解android中的hook技术

最近了解了下android中的hook技术,一段代码黑掉了activity的启动过程,Demo中涉及到framework层activity的启动机制,JAVA的反射+代理机制,不得不说掌握这个技术需要深厚的技术功底才行。文章地址:在这里,写这篇博客是记录下自己对其中用到的技术的理解。
调用startActivity会进入framework,然后通过IActivityManager对象的startActivity方法启动activity,Demo中的做法总结下就是以下几步:

  1. 通过反射,拿到IActivityManager对象;
  2. 自定义一个代理类的调度类,它要实现InvocationHandler的invoke方法;
  3. 以IActivityManager为参数,创建调度类实例;
  4. 以IActivityManager和调度类实例为参数,生成一个代理类的实例;
  5. 用代理类的实例,替换第一步拿到的IActivityManager对象。

    下面通过代码,一步一步解析其中的原理。

    拿到IActivityManager对象

    startActivity会调用到Instrumentation类中的execStartActivity方法,并执行其中的下面的代码:

try {
         intent.migrateExtraStreamToClipData();
         intent.prepareToLeaveProcess();
         int result = ActivityManagerNative.getDefault()
             .startActivity(whoThread, who.getBasePackageName(), intent,
                     intent.resolveTypeIfNeeded(who.getContentResolver()),
                     token, target != null ? target.mEmbeddedID : null,
                     requestCode, 0, null, options);
         checkStartActivityResult(result, intent);
     } catch (RemoteException e) {
         throw new RuntimeException("Failure from system", e);
        }

ActivityManagerNative.getDefault()返回的是一个IActivityManager对象:
这里写图片描述
gDefault是什么?
这里写图片描述
它是一个Singleton类型的对象,通过get方法,可以拿到其中的实例:
这里写图片描述
这个实例,就是IActivityManager,它包含了一系列的启动activity的方法:
这里写图片描述
hook的核心思想,是通过反射,将系统对象替换成我们创建的代理类对象,那么我们先要获得这个IActivityManager对象,也是通过反射。


关于反射,参考这里
通过反射,我们可以拿到一个类的private成员或private方法,具体步骤如下:

    class Point{  
        private int x;  
        public int y;  
        public Point(int x, int y) {  
            super();  
            this.x = x;  
            this.y = y;  
            }  
    }  

    public class FiledReflect {  
        public static void main(String[] args) throws Exception {  

            Point p1=new Point(5, 39);  
            Point p2=new Point(3, 9);  
                        </span>  
            //p1.getClass()是拿到p1对应的类,getField("y")是拿到这个类对应的某个field 
            Field fieldY = p1.getClass().getField("y"); 
            //为了拿到field的具体值,需要某个具体的instance                                            
            int y = (int) fieldY.get(p1);  
            System.out.println(y);  

            //对私有成员变量,可以进行暴力反射  
            Field fieldX = p1.getClass().getDeclaredField("x");  
            fieldX.setAccessible(true);//暴力反射  
            int x = (int) fieldX.get(p1);//取对应p1对象的x字段的值  
            System.out.println(x);                
            }  
    }  
    **field是属于某个类的,fieldvalue是属于某个对象的**

回到Demo中,通过下面的代码获得IActivityManager实例,注释很清楚了:
            //通过完整的类名拿到class
            Class<?> ActivityManagerNativeClss = Class.forName("android.app.ActivityManagerNative");
            //拿到这个类的某个field
            Field gDefaultFiled = ActivityManagerNativeClss.getDeclaredField("gDefault");
            //field为private,设置为可访问的
            gDefaultFiled.setAccessible(true);
            //拿到ActivityManagerNative的gDefault的Field的实例
            //gDefault为static类型,不需要传入具体的对象
            Object gDefaultFiledValue = gDefaultFiled.get(null);

            //拿到Singleton类
            Class<?> SingletonClass = Class.forName("android.util.Singleton");
            //拿到类对应的field
            Field mInstanceField = SingletonClass.getDeclaredField("mInstance");
            //field是private
            mInstanceField.setAccessible(true);
            //gDefaultFiledValue是Singleton的实例对象
            //拿到IActivityManager实例
            Object iActivityManagerObject = mInstanceField.get(gDefaultFiledValue);

到这里,终于拿到了系统startActivity用到的IActivityManager,下面我们要创建一个代理类替换它。

自定义一个代理类的调度类

java有个代理类:

这里写图片描述
代理类实现了一些申明的接口,和调用InvocationHandler的方法。不是很好理解,我们再看InvocationHandler:
这里写图片描述
简言之,就是代理类的方法,都会通过InvocationHandler的invoke方法来处理。注释中也给出了用法,通过override它的invoke方法实现代理功能(就是做一些其它事)。

下面就来实现这个自定义InvocationHandler:

private class AmsInvocationHandler implements InvocationHandler {

        private Object iActivityManagerObject;

        private AmsInvocationHandler(Object iActivityManagerObject) {
            this.iActivityManagerObject = iActivityManagerObject;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            Log.i("HookUtil", method.getName());
            //我要在这里搞点事情
            if ("startActivity".contains(method.getName())) {
                Log.e("HookUtil","Activity已经开始启动");
                Log.e("HookUtil","小弟到此一游!!!");
            }
            return method.invoke(iActivityManagerObject, args);
        }
    }
就按照注释中的做法,override invoke方法,处理一些其它业务。
代理类实例,传入对应的参数就行了:
AmsInvocationHandler handler = new AmsInvocationHandler(iActivityManagerObject);

为啥要自定义一个InvocationHandler?因为创建一个代理类实例,需要对应的InvocationHandler。

生成一个代理类的实例

这里写图片描述

            AmsInvocationHandler handler = new AmsInvocationHandler(iActivityManagerObject);
            Class<?> IActivityManagerIntercept = Class.forName("android.app.IActivityManager");
            Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                    new Class<?>[]{IActivityManagerIntercept}, handler);
传入当前线程的classloader,代理对象需要实现的接口,和InvocationHandler实例,就创建了一个代理类实例。接口就是系统的IActivityManager类。

替换第一步拿到的IActivityManager对象

依然是通过反射:
mInstanceField.set(gDefaultFiledValue, proxy);
将gDefaultFiledValue这个Singleton类的mInstance field的value改为创建的proxy对象。

完整代码和在Demo中的调用

public class HookUtil {
    private Class<?> proxyActivity;
    private Context context;

    public HookUtil(Class<?> proxyActivity, Context context) {
        this.proxyActivity = proxyActivity;
        this.context = context;
    }

    public void hookAms() {
        try {
            //通过完整的类名拿到class
            Class<?> ActivityManagerNativeClss = Class.forName("android.app.ActivityManagerNative");
            //拿到这个类的某个field
            Field gDefaultFiled = ActivityManagerNativeClss.getDeclaredField("gDefault");
            //field为private,设置为可访问的
            gDefaultFiled.setAccessible(true);
            //拿到ActivityManagerNative的gDefault的Field的实例
            //gDefault为static类型,不需要传入具体的对象
            Object gDefaultFiledValue = gDefaultFiled.get(null);

            //拿到Singleton类
            Class<?> SingletonClass = Class.forName("android.util.Singleton");
            //拿到类对应的field
            Field mInstanceField = SingletonClass.getDeclaredField("mInstance");
            //field是private
            mInstanceField.setAccessible(true);
            //gDefaultFiledValue是Singleton的实例对象
            //拿到IActivityManager
            Object iActivityManagerObject = mInstanceField.get(gDefaultFiledValue);

            AmsInvocationHandler handler = new AmsInvocationHandler(iActivityManagerObject);
            Class<?> IActivityManagerIntercept = Class.forName("android.app.IActivityManager");
            Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                    new Class<?>[]{IActivityManagerIntercept}, handler);
            mInstanceField.set(gDefaultFiledValue, proxy);

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private class AmsInvocationHandler implements InvocationHandler {

        private Object iActivityManagerObject;

        private AmsInvocationHandler(Object iActivityManagerObject) {
            this.iActivityManagerObject = iActivityManagerObject;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            Log.i("HookUtil", method.getName());
            //我要在这里搞点事情
            if ("startActivity".contains(method.getName())) {
                Log.e("HookUtil","Activity已经开始启动");
                Log.e("HookUtil","小弟到此一游!!!");
            }
            return method.invoke(iActivityManagerObject, args);
        }
    }
}
在Application类oncreate中,调用:
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        HookUtil hookUtil=new HookUtil(SecondActivity.class, this);
        hookUtil.hookAms();
    }
}
之后在app的任意地方调用startActivity都能看到代理对象在invoke中添加的log了。
Demo始终是最好的学习方法。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值