使用动态代理拦截Android Activity的启动

来源:互联网 发布:张逗张花小卖部 淘宝 编辑:程序博客网 时间:2024/05/22 01:28

使用动态代理拦截Android Activity的启动

1.动态代理拦截的要求

  • 要代理的类要实现接口
  • 要能获取目标类的对象

2.对startActivity进行拦截

activity的启动过程通过分析源码知道了实际内部调用了ActivityManagerNative.getDefault()
.startActivity
这个方法,而getDefault这个方法内部是

static public IActivityManager getDefault() {        return gDefault.get();    }

这个gDefault是一个静态常量Singleton。

所以关键就是通过反射获取到gDefault然后拦截它的startactivity方法。
ActivityManagerNative是一个继承了Binder的类,还实现了IActivityManager这个接口.相当于AIDL中系统生成的Stub类。然后ActivityManagerNative是一个抽象类,它的实现类是ActivityManagerService。
ActivityManagerNative内部还有一个类,ActivityManagerProxy,这个类就是这个Binder的远端代理类。我们要拦截的就是这个ActivityManagerProxy对象的startActivity方法。

public static void hookAms() {        try {            Class<?> activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative");            Field gDefaultField = activityManagerNativeClass.getDeclaredField("gDefault");            gDefaultField.setAccessible(true);            Object gDefault = gDefaultField.get(null);            Class<?> singleton = Class.forName("android.util.Singleton");            Field mInstanceField = singleton.getDeclaredField("mInstance");            mInstanceField.setAccessible(true);            Object rawIActivityManager = mInstanceField.get(gDefault);            Class<?> iActivityManager = Class.forName("android.app.IActivityManager");            Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class[]{iActivityManager}            ,new AmsHookBinderInvocationHanlder(rawIActivityManager));            mInstanceField.set(gDefault,proxy);        } catch (ClassNotFoundException e) {            e.printStackTrace();        } catch (NoSuchFieldException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        }    }

然后在我们的InvocationHandler中

 @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        if("startActivity".equals(method.getName())){            Intent raw;            int index = 0;            for(int i=0;i< args.length;i++){                if(args[i] instanceof Intent){                    index = i;                    break;                }            }            raw = (Intent) args[index];            Intent newIntent = new Intent();            String targetPackage = "com.github";            ComponentName componentName = new ComponentName(targetPackage, FileInterSendActivity.class.getName());            newIntent.setComponent(componentName);            newIntent.putExtra("_tartget_intent",raw);            Log.d("hook",Thread.currentThread().getName());            Log.d("hook","拦截activity的启动成功"+"\n" +                    "component:"+((Intent)args[2]).getComponent().getPackageName()+","+((Intent)args[2]).getComponent().getClassName());            args[index] = newIntent;            Log.d("hook","change activity component successful"+"\n" +                    "component:"+((Intent)args[2]).getComponent().getPackageName()+","+((Intent)args[2]).getComponent().getClassName());            return method.invoke(obj,args);        }        return method.invoke(obj, args);    }

运行项目,会发现我们成功的将想要启动的activity替换掉了。