如何使用VirtualApp的动态代理框架做动态代理
前言
VirtualApp是一个能够实现多开功能的沙盒,你可以在虚拟空间内任意的安装、启动和卸载APK,这一切都与外部隔离。要想实现对一个APP的虚拟化,就是不直接把APP安装进系统,同时又要提供APP运行过程中所需的一切,从而可以让它误以为自己是运行在正常系统中。这里就需要实现系统服务的虚拟化和相关路径的虚拟化。本文将侧重于分析系统服务虚拟化的过程,以及如何使用VA现有的动态代理框架去做代理。
几个关键类
- InvocationStubManager:统一注入代理对象
1.遍历MethodInvocationProxy调用其inject方法去注入代理类。- injectInternal方法中添加MethodInvocationProxy对象。
普通方法的动态代理涉及类
- MethodInvocationProxy:添加需要被代理的方法,处理动态代理类对象的替换逻辑。
1.一般在onBindMethods()中或者借助@Inject(...)注解,addMethodProxy(...)添加MethodProxy对象。
2.MethodInvocationProxy实现了IInjector接口,一般在inject()方法中用代理对象替换掉被代理的对象。
Binder的动态代理
- BinderInvocationProxy :
- abstract class BinderInvocationProxy extends MethodInvocationProxy<BinderInvocationStub>
- BinderInvocationStub
- class BinderInvocationStub extends MethodInvocationStub<IInterface> implements IBinder
几个关键方法
addMethodProxy(MethodProxy methodProxy)
protected void onBindMethods() {
super.onBindMethods();
addMethodProxy(new A1MethodProxy());
}
跟一下addMethodProxy()做了啥
public MethodProxy addMethodProxy(MethodProxy methodProxy) {
return mInvocationStub.addMethodProxy(methodProxy);
}
看看mInvocationStub是什么,在哪初始化的
public MethodInvocationProxy(T invocationStub) {
this.mInvocationStub = invocationStub;
onBindMethods();
afterHookApply(invocationStub);
}
继续跟在哪调用了MethodInvocationProxy的构造方法,T是什么
public AStub() {
//StoreApplication.mA是被代理的对象
//IA是被代理类实现的接口
super(new MethodInvocationStub<IA>(StoreApplication.mA));
}
原来T是MethodInvocationStub类型对象。接着上面,我们看下MethodInvocationStub#addMethodProxy方法做了啥
public MethodProxy addMethodProxy(MethodProxy methodProxy) {
if (methodProxy != null && !TextUtils.isEmpty(methodProxy.getMethodName())) {
if (mInternalMethodProxies.containsKey(methodProxy.getMethodName())) {
LogTool.w(TAG, String.format("The Hook(%s, %s) you added has been in existence.", methodProxy.getMethodName(),
methodProxy.getClass().getName()));
return methodProxy;
}
mInternalMethodProxies.put(methodProxy.getMethodName(), methodProxy);
}
return methodProxy;
}
mInternalMethodProxies是一个Hashmap,addMethodProxy方法只是将MethodProxy按方法名存储进了这个集合中。那被添加的方法是如何被成功代理的呢?
com.zooksoft.faker.hookbase.client.hook.base.MethodInvocationStub.HookInvocationHandler
private class HookInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodProxy methodProxy = getMethodProxy(method.getName());//根据方法名找到对应的MethodProxy
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 {
//start==具体的代理逻辑
if (useProxy && methodProxy.beforeCall(mBaseInterface, method, args)) {
res = methodProxy.call(mBaseInterface, method, args);
res = methodProxy.afterCall(mBaseInterface, method, args, res);
} else {
res = method.invoke(mBaseInterface, args);
}
return res;
//end==具体的代理逻辑
} 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);
}
}
}
}
}
我们看到了非常熟悉的InvocationHandler接口,反射调用到代理对象的方法时会回调到其invoke()方法,此时根据方法名找到对应的MethodProxy对象,接下来就是代理的逻辑了。
那么代理对象是在哪被构造的呢?
public MethodInvocationStub(T baseInterface, Class<?>... proxyInterfaces) {
this.mBaseInterface = baseInterface;//baseInterface即为被代理对象
if (baseInterface != null) {
if (proxyInterfaces == null) {
proxyInterfaces = MethodParameterUtils.getAllInterface(baseInterface.getClass());
}
mProxyInterface = (T) Proxy.newProxyInstance(baseInterface.getClass().getClassLoader(), proxyInterfaces, new HookInvocationHandler());
} else {
LogTool.d(TAG, "Unable to build HookDelegate:." + getIdentityName());
}
}
inject()
通过上面对onBindMethods()方法以及addMethodProxy(MethodProxy methodProxy)方法的跟踪分析,我们明白了代理对象是如何被构造的。那么代理对象是何时被替换的呢?
那就是我们的inject()方法了,举个栗子
public class AStub extends MethodInvocationProxy<MethodInvocationStub<IA>> {
public AStub() {
super(new MethodInvocationStub<IA>(StoreApplication.mA));
}
@Override
protected void onBindMethods() {
super.onBindMethods();
addMethodProxy(new A1MethodProxy());
}
@Override
public void inject() throws Exception {
IA proxyInterface = getInvocationStub().getProxyInterface();
StoreApplication.mA = proxyInterface;
}
@Override
public boolean isEnvBad() {
return false;
}
}
那inject()方法是在何时被调用的呢?
public void injectAll() throws Exception {
for (IInjector injector : mInjectors.values()) {
injector.inject();
}
}
injectAll()方法遍历调用了所有代理对象的注入方法,那哪里调用了injectAll
public static void setMyselfHooK(Context context) {
...
gCore.mContext = context;
VEnvironment.systemReady();
gCore.detectProcessType(context, context.getApplicationInfo().processName);
IORedirectManager.getInstance().init(gCore.getVDeviceInfo());
try {
InvocationStubManager invocationStubManager = InvocationStubManager.getInstance();
invocationStubManager.init();
invocationStubManager.injectAll();
} catch (Exception e) {
e.printStackTrace();
}
...
}
继续跟下哪里调用的setMyselfHooK
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
try {
LogToolManager.getInstance().init(VEnvironment.getLogsDir());
LogTool.w(Constant.AD_TAG, "-----AdApplication attachBaseContext");
HookCore.setMyselfHooK(base);
} catch (Exception e) {
LogTool.w(Constant.AD_TAG, "" + e.getMessage());
}
}
跟到这里我们清楚了所有代理类的注入时机是在Application#attachBaseContext方法中。
普通类方法的动态代理
相关类的构造
public interface IA {
int a1();
int a2();
}
被代理类
public class A implements IA {
public int a1(){
return 1;
}
public int a2(){
return 2;
}
}
代理方法抽象
public class A1MethodProxy extends MethodProxy {
@Override
public String getMethodName() {
return "a1";
}
@Override
public boolean beforeCall(Object who, Method method, Object... args) {
return super.beforeCall(who, method, args);
}
@Override
public Object afterCall(Object who, Method method, Object[] args, Object result) throws Throwable {
return super.afterCall(who, method, args, result);
}
@Override
public Object call(Object who, Method method, Object... args) throws Throwable {
return 111;
// return super.call(who, method, args);
}
}
public class AStub extends MethodInvocationProxy<MethodInvocationStub<IA>> {
public AStub() {
super(new MethodInvocationStub<IA>(StoreApplication.mA));
}
@Override
protected void onBindMethods() {
super.onBindMethods();
addMethodProxy(new A1MethodProxy());//添加需要被代理的方法
}
@Override
public void inject() throws Exception {
IA proxyInterface = getInvocationStub().getProxyInterface();
StoreApplication.mA = proxyInterface;
}
@Override
public boolean isEnvBad() {//代理是否生效的开关
return false;
}
}
添加代理桩
private void injectInternal() throws Exception {
addInjector(new ConnectivityStub());
addInjector(new ActivityManagerStub());
addInjector(new PackageManagerStub());
addInjector(new LocationManagerStub());
//addInjector(new DisplayStub()); Xpose Hook的
addInjector(new ISubStub());
addInjector(new PhoneSubInfoStub());
addInjector(new TelephonyStub());
addInjector(new WifiManagerStub());
addInjector(new BluetoothStub());
Class<?> clz = Class.forName("com.zookingsoft.themestore.proxytest.AStub");
IInjector iInjector = (IInjector)clz.newInstance();
addInjector(iInjector);//代理测试
}
Binder的动态代理
需要通过ServiceManager#getService获取binder对象的,可以采用如下动态代理方式。以hook ILocationManager接口方法为例。
private static JSONObject getLocation(Context context) {
LocationManager locationManager = (LocationManager) context.getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
JSONObject json = new JSONObject();
try {
List<String> allProviders = locationManager.getAllProviders();//通过ILocationManager调用
if (Util.checkPermission(context, android.Manifest.permission.ACCESS_COARSE_LOCATION) || Util.checkPermission(context, android.Manifest.permission.ACCESS_FINE_LOCATION)) {
for (String pro : allProviders) {
Location location = locationManager.getLastKnownLocation(pro);//通过ILocationManager调用
if (location != null) {
json.put("CLCT", getLocation(location));
}
}
}
} catch (Exception e) {
}
return json;
}
重点关注下context.getApplicationContext().getSystemService(Context.LOCATION_SERVICE)做了什么。我们知道ContextWrapper实际上把功能都委托给了ContextImp
ContextImp#getSystemService
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
继续跟到SystemServiceRegistry.getSystemService方法
android.app.SystemServiceRegistry#getSystemService
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
看下SYSTEM_SERVICE_FETCHERS是什么
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
new HashMap<String, ServiceFetcher<?>>();
找到往SYSTEM_SERVICE_FETCHERS这个hashmap添加元素的位置
android.app.SystemServiceRegistry#registerService
private static <T> void registerService(String serviceName, Class<T> serviceClass,
ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
找到调用SystemServiceRegistry#registerService方法的位置
final class SystemServiceRegistry {
...
static {
...
registerService(Context.LOCATION_SERVICE, LocationManager.class,
new CachedServiceFetcher<LocationManager>() {
@Override
public LocationManager createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(Context.LOCATION_SERVICE);
return new LocationManager(ctx, ILocationManager.Stub.asInterface(b));
}});
...
}
...
}
再看看ServiceManager.getServiceOrThrow做了什么
public static IBinder getServiceOrThrow(String name) throws ServiceNotFoundException {
final IBinder binder = getService(name);
if (binder != null) {
return binder;
} else {
throw new ServiceNotFoundException(name);
}
}
最终调用到getService()方法里。
android.os.ServiceManager#getService
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return Binder.allowBlocking(getIServiceManager().getService(name));
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
可以看到获取IBinder时先会去sCache缓存里找,找不到再会借助IServiceManage去找。
至此我们可以做一个结论:每次通过ServiceManager#getService获取binder对象,最终都是先从ServiceManager中的sCache缓存中获取的,如果我们预先将代理binder添加到集合中,那么每次通过ServiceManager#getService获取到的就是我们的代理binder对象。
下面要做的就是如何构造代理binder
step1:构造映射类。
public class ILocationManager {
public static Class<?> TYPE = RefClass.load(ILocationManager.class, "android.location.ILocationManager");
public static class Stub {
public static Class<?> TYPE = RefClass.load(Stub.class, "android.location.ILocationManager$Stub");
@MethodParams({IBinder.class})
public static RefStaticMethod<IInterface> asInterface;
}
}
之所以构造映射类,是为了通过封装减少繁琐的反射操作。RefClass.load(Stub.class, "android.location.ILocationManager$Stub")方法本质上就是给诸如RefStaticMethod等类型的反射字段赋值。
step2:构造代理相关类
@Inject(MethodProxies.class)
public class LocationManagerStub extends BinderInvocationProxy {
public LocationManagerStub() {
super(ILocationManager.Stub.asInterface, Context.LOCATION_SERVICE);
}
@Override
protected void onBindMethods() {
super.onBindMethods();
addMethodProxy(new FakeReplaceLastPkgMethodProxy("addProximityAlert", 0));
}
}
接下来分析下Binder代理关键的两个类BinderInvocationProxy,BinderInvocationStub
public abstract class BinderInvocationProxy extends MethodInvocationProxy<BinderInvocationStub> {
protected String mServiceName;
public BinderInvocationProxy(IInterface stub, String serviceName) {
this(new BinderInvocationStub(stub), serviceName);
}
public BinderInvocationProxy(RefStaticMethod<IInterface> asInterfaceMethod, String serviceName) {
this(new BinderInvocationStub(asInterfaceMethod, ServiceManager.getService.call(serviceName)), serviceName);
}
public BinderInvocationProxy(Class<?> stubClass, String serviceName) {
this(new BinderInvocationStub(stubClass, ServiceManager.getService.call(serviceName)), serviceName);
}
public BinderInvocationProxy(BinderInvocationStub hookDelegate, String serviceName) {
super(hookDelegate);
this.mServiceName = serviceName;
}
@Override
public void inject() throws Throwable {
getInvocationStub().replaceService(mServiceName);
}
@Override
public boolean isEnvBad() {
IBinder binder = ServiceManager.getService.call(mServiceName);
return binder != null && getInvocationStub() != binder;
}
需要关注的是如下几点:
- BinderInvocationProxy继承自MethodInvocationProxy
- BinderInvocationProxy的构造方法中传入的被代理binder对象是ServiceManager.getService.call(serviceName)获取的,本质上就是反射调用了ServiceManager#getService方法(ServiceMnager是hide的)
- getInvocationStub().replaceService(mServiceName)方法中完成代理binder的替换
那getInvocationStub().replaceService(mServiceName)是如何完成代理binder的替换的呢?其实就是在ServiceManager的sCache缓存集合里将代理binder添加进去了。
public void replaceService(String name) {
if (mBaseBinder != null) {
ServiceManager.sCache.get().put(name, this);
}
}
下面再来分析另一个Binder代理的关键类BinderInvocationStub
public class BinderInvocationStub extends MethodInvocationStub<IInterface> implements IBinder {
private static final String TAG = BinderInvocationStub.class.getSimpleName();
private IBinder mBaseBinder;//被代理的binder,本质上是一个IA.Stub
public BinderInvocationStub(RefStaticMethod<IInterface> asInterfaceMethod, IBinder binder) {
this(asInterface(asInterfaceMethod, binder));
}
public BinderInvocationStub(Class<?> stubClass, IBinder binder) {
this(asInterface(stubClass, binder));
}
public BinderInvocationStub(IInterface mBaseInterface) {
super(mBaseInterface);
mBaseBinder = getBaseInterface() != null ? getBaseInterface().asBinder() : null;
addMethodProxy(new AsBinder());
}
private static IInterface asInterface(RefStaticMethod<IInterface> asInterfaceMethod, IBinder binder) {
if (asInterfaceMethod == null || binder == null) {
return null;
}
return asInterfaceMethod.call(binder);
}
private static IInterface asInterface(Class<?> stubClass, IBinder binder) {
try {
if (stubClass == null || binder == null) {
return null;
}
Method asInterface = stubClass.getMethod("asInterface", IBinder.class);
return (IInterface) asInterface.invoke(null, binder);
} catch (Exception e) {
Log.d(TAG, "Could not create stub " + stubClass.getName() + ". Cause: " + e);
return null;
}
}
public void replaceService(String name) {
if (mBaseBinder != null) {
ServiceManager.sCache.get().put(name, this);
}
}
private final class AsBinder extends MethodProxy {
@Override
public String getMethodName() {
return "asBinder";
}
@Override
public Object call(Object who, Method method, Object... args) throws Throwable {
return BinderInvocationStub.this;
}
}
@Override
public String getInterfaceDescriptor() throws RemoteException {
return mBaseBinder.getInterfaceDescriptor();
}
public Context getContext() {
return VirtualCore.get().getContext();
}
@Override
public boolean pingBinder() {
return mBaseBinder.pingBinder();
}
@Override
public boolean isBinderAlive() {
return mBaseBinder.isBinderAlive();
}
@Override
public IInterface queryLocalInterface(String descriptor) {//IA.Stub.asInterface(b)内部会调用到queryLocalInterface方法
return getProxyInterface();
}
@Override
public void dump(FileDescriptor fd, String[] args) throws RemoteException {
mBaseBinder.dump(fd, args);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
@Override
public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
mBaseBinder.dumpAsync(fd, args);
}
@Override
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
return mBaseBinder.transact(code, data, reply, flags);
}
@Override
public void linkToDeath(DeathRecipient recipient, int flags) throws RemoteException {
mBaseBinder.linkToDeath(recipient, flags);
}
@Override
public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
return mBaseBinder.unlinkToDeath(recipient, flags);
}
public IBinder getBaseBinder() {
return mBaseBinder;
}
}
需要重点关注的是如下几点:
- BinderInvocationStub继承自MethodInvocationStub,更重要的是它实现了IBinder。
- 那么它为何要实现Binder呢?如上面所述,在replaceService方法中它获取到了一个代理Binder用于添加到缓存集合中,而这个代理binder正是BinderInvocationStub它自己。
- 如果BinderInvocationStub作为代理binder,但是却未实现ILocationManager接口,它如何能够代理掉ILocationManager接口中的那些方法呢?
我们回顾下获取binder之后做了啥,以LocationManager为例
return new LocationManager(ctx, ILocationManager.Stub.asInterface(b))
他需要将Binder通过ILocationManager.Stub.asInterface转换为IInterface类型的接口,那我们看看ILocationManager.Stub.asInterface做了啥
public static cc.abto.demo.UserManager asInterface(android.os.IBinder obj){
if ((obj == null))
{
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof cc.abto.demo.UserManager)))
{
return ((cc.abto.demo.UserManager) iin);
}
return new cc.abto.demo.UserManager.Stub.Proxy(obj);
}
先会调用queryLocalInterface方法询问该接口是本进程的接口,如果不为空就直接返回。而在BinderInvocationStub中我们重写了queryLocalInterface方法,并返回了getProxyInterface方法返回的代理对象。
至此我们明白了BinderInvocationStub作为代理binder是如何代理aidl接口中的方法的。