26
2017
09

对 VirtualApp hook过程的理解

VirtualApp是一个开源的Android App虚拟化引擎,允许在其中创建虚拟空间,并在这个虚拟空间中运行其他应用。就是一个容器,可以进行双开处理。网上的讲解已经很详细了,我想从一个例子加深一下对va hook过程的理解。
可以从简单的startactivity入手
首先是mirror里的ActivityManagerNative

public class ActivityManagerNative {
    public static Class<?> TYPE = RefClass.load(ActivityManagerNative.class, "android.app.ActivityManagerNative");
    public static RefStaticObject<Object> gDefault;
    public static RefStaticMethod<IInterface> getDefault;
}

类成员变量会在安装时就会进行初始化,通过反射获取系统中的值,首先执行RefClass.load

//realClass 通过反射获得,这里是"android.app.ActivityManagerNative"
    public static Class load(Class mappingClass, Class<?> realClass) {
        //mirror对象获取字段 这里是ActivityManagerNative 有三个字段 TYPE gDefault getDefault
        Field[] fields = mappingClass.getDeclaredFields();
        for (Field field : fields) {
            try {
                if (Modifier.isStatic(field.getModifiers())) {
                    Constructor<?> constructor = REF_TYPES.get(field.getType());
                    //如果map里有这个字段才会替换值,TYPE.class不在
                    if (constructor != null) {
                        //对字段设置值
                        field.set(null, constructor.newInstance(realClass, field));
                    }
                }
            }
            catch (Exception e) {
                // Ignore
            }
        }
        return realClass;
    }

如果是getDefault 就会反射调用他的构造器

 public RefStaticMethod(Class<?> cls, Field field) throws NoSuchMethodException {
                                  ...
 //如果方法名和mirror中的方法名一致,则拿到method对象,之后可以反射调用方法
            for (Method method : cls.getDeclaredMethods()) {
                if (method.getName().equals(field.getName())) {
                    this.method = method;
                    this.method.setAccessible(true);
                    break;
                }
            }

mirror中的activitymanager就初始完成了,主要是为了得到getDefault方法返回值 在系统中 就是一个aidl接口

    static public IActivityManager getDefault() {
        return ActivityManager.getService();
    }

对这个接口首先进行动态代理,在MethodInvocationStub中

    public MethodInvocationStub(T baseInterface, Class<?>... proxyInterfaces) {
    //被代理类
        this.mBaseInterface = baseInterface;
        if (baseInterface != null) {
            if (proxyInterfaces == null) {
                proxyInterfaces = MethodParameterUtils.getAllInterface(baseInterface.getClass());
            }
     //代理类 处理类HookInvocationHandler
            mProxyInterface = (T) Proxy.newProxyInstance(baseInterface.getClass().getClassLoader(), proxyInterfaces, new HookInvocationHandler());
        } else {
            VLog.d(TAG, "Unable to build HookDelegate: %s.", getIdentityName());
        }
    }

处理类也在内部

    private class HookInvocationHandler implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            MethodProxy methodProxy = getMethodProxy(method.getName());
            //如果methodProxy被添加过了并且设置为可以得到,则执行methodProxy.call.
            boolean useProxy = (methodProxy != null && methodProxy.isEnable());
            boolean mightLog = (mInvocationLoggingCondition != LogInvocation.Condition.NEVER) ||
                    (methodProxy != null && methodProxy.getInvocationLoggingCondition() != LogInvocation.Condition.NEVER);

            String argStr = null;
            Object res = null;
            Throwable exception = null;
            if (mightLog) {
                // Arguments to string is done before the method is called because the method might actually change it
                argStr = Arrays.toString(args);
                argStr = argStr.substring(1, argStr.length()-1);
            }
            try {
                if (useProxy && methodProxy.beforeCall(mBaseInterface, method, args)) {
                //执行methodproxy.call方法
                    res = methodProxy.call(mBaseInterface, method, args);
                    res = methodProxy.afterCall(mBaseInterface, method, args, res);
                } else {
                    res = method.invoke(mBaseInterface, args);
                }
                return res;

            } catch (Throwable t) {
                exception = t;
                if (exception instanceof InvocationTargetException && ((InvocationTargetException) exception).getTargetException() != null) {
                    exception = ((InvocationTargetException) exception).getTargetException();
                }
                throw exception;

            } finally {
                if (mightLog) {
                    int logPriority = mInvocationLoggingCondition.getLogLevel(useProxy, exception != null);
                    if (methodProxy != null) {
                        logPriority = Math.max(logPriority, methodProxy.getInvocationLoggingCondition().getLogLevel(useProxy, exception != null));
                    }
                    if (logPriority >= 0) {
                        String retString;
                        if (exception != null) {
                            retString = exception.toString();
                        } else if (method.getReturnType().equals(void.class)) {
                            retString = "void";
                        } else {
                            retString = String.valueOf(res);
                        }
                    // Log.println(logPriority, TAG, method.getDeclaringClass().getSimpleName() + "." + method.getName() + "(" + argStr + ") => " + retString);
                    }
                }
            }
        }
    }

这里要提到MethodProxy.添加MethodProxy方式有两种。一是调用 addMethodProxy,二是在 Stub 上添加 @Inject 注解。
关于 MethodProxies
叫这个名字的类很多,一个 MethodProxies 对应一个需要 Hook 的 framework 类型,需要 Hook 的方法以内部类(MethodProxy)的形式罗列在内部。

@Inject(MethodProxies.class)
public class ActivityManagerStub extends MethodInvocationProxy<MethodInvocationStub<IInterface>> {

将要 Hook 的方法集合 MethodProxies 绑定到 Stub 上。然后就是 Stub 对自己头上注解的解析,最终还是会调用到内部的 addMethodProxy 方法。

    protected void onBindMethods() {

        if (mInvocationStub == null) {
            return;
        }

        Class<? extends MethodInvocationProxy> clazz = this.getClass(); Inject inject = clazz.getAnnotation(Inject.class); if (inject != null) { Class<?> proxiesClass = inject.value();
            Class<?>[] innerClasses = proxiesClass.getDeclaredClasses(); for (Class<?> innerClass : innerClasses) {
                if (!Modifier.isAbstract(innerClass.getModifiers())
                        && MethodProxy.class.isAssignableFrom(innerClass)
                        && innerClass.getAnnotation(SkipInject.class) == null) {
                    addMethodProxy(innerClass);
                }
            }

        }
    }

注解实际还是调用的 addMethodProxy 。如类startactivity就是其中的内部类

static class StartActivity extends MethodProxy

用stub替换系统中的对像的值。就完成了插桩操作。

 public void inject() throws Throwable {
 ...
 ActivityManagerNative.gDefault.set(getInvocationStub().getProxyInterface());
 ...
 }

这个stub完成了 ,最后通过这个代理startactivity的call,完成一些操作。比如启动A1界面,替换成启动va设好的stubactivity

   public Object call(Object who, Method method, Object... args) throws Throwable {
...
 int res = VActivityManager.get().startActivity(intent, activityInfo, resultTo, options, resultWho, requestCode, VUserHandle.myUserId());
...
}

参考资料:
http://blog.csdn.net/ganyao939543405/article/details/76146760?ref=myread
VA 的源码注释: https://github.com/ganyao114/VA_Doc

上一篇:外卖App双列表联动 下一篇:sun.misc.BASE64Encoder找不到jar包的解决方法