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
类成员变量会在安装时就会进行初始化,通过反射获取系统中的值,首先执行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