/* ** DroidPlugin Project ** ** Copyright(c) 2015 Andy Zhang <zhangyong232@gmail.com> ** ** This file is part of DroidPlugin. ** ** DroidPlugin is free software: you can redistribute it and/or ** modify it under the terms of the GNU Lesser General Public ** License as published by the Free Software Foundation, either ** version 3 of the License, or (at your option) any later version. ** ** DroidPlugin is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ** Lesser General Public License for more details. ** ** You should have received a copy of the GNU Lesser General Public ** License along with DroidPlugin. If not, see <http://www.gnu.org/licenses/lgpl.txt> ** **/ package com.morgoo.droidplugin.hook.binder; import android.content.Context; import android.os.IBinder; import android.text.TextUtils; import com.morgoo.droidplugin.hook.BaseHookHandle; import com.morgoo.droidplugin.hook.Hook; import com.morgoo.droidplugin.hook.HookedMethodHandler; import com.morgoo.droidplugin.reflect.FieldUtils; import com.morgoo.droidplugin.reflect.Utils; import com.morgoo.helper.MyProxy; import com.morgoo.helper.compat.ServiceManagerCompat; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.List; import java.util.Map; /** * Created by Andy Zhang(zhangyong232@gmail.com) on 2015/3/2. */ public class ServiceManagerCacheBinderHook extends Hook implements InvocationHandler { private String mServiceName; public ServiceManagerCacheBinderHook(Context hostContext, String servicename) { super(hostContext); mServiceName = servicename; setEnable(true); } @Override protected void onInstall(ClassLoader classLoader) throws Throwable { Object sCacheObj = FieldUtils.readStaticField(ServiceManagerCompat.Class(), "sCache"); if (sCacheObj instanceof Map) { Map sCache = (Map) sCacheObj; Object Obj = sCache.get(mServiceName); if (Obj != null && false) { //FIXME 已经有了怎么处理?这里我们只是把原来的给remove掉,再添加自己的。程序下次取用的时候就变成我们hook过的了。 //但是这样有缺陷。 throw new RuntimeException("Can not install binder hook for " + mServiceName); } else { sCache.remove(mServiceName); IBinder mServiceIBinder = ServiceManagerCompat.getService(mServiceName); if (mServiceIBinder == null) { if (Obj != null && Obj instanceof IBinder && !Proxy.isProxyClass(Obj.getClass())) { mServiceIBinder = ((IBinder) Obj); } } if (mServiceIBinder != null) { MyServiceManager.addOriginService(mServiceName, mServiceIBinder); Class clazz = mServiceIBinder.getClass(); List<Class<?>> interfaces = Utils.getAllInterfaces(clazz); Class[] ifs = interfaces != null && interfaces.size() > 0 ? interfaces.toArray(new Class[interfaces.size()]) : new Class[0]; IBinder mProxyServiceIBinder = (IBinder) MyProxy.newProxyInstance(clazz.getClassLoader(), ifs, this); sCache.put(mServiceName, mProxyServiceIBinder); MyServiceManager.addProxiedServiceCache(mServiceName, mProxyServiceIBinder); } } } } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { IBinder originService = MyServiceManager.getOriginService(mServiceName); if (!isEnable()) { return method.invoke(originService, args); } HookedMethodHandler hookedMethodHandler = mHookHandles.getHookedMethodHandler(method); if (hookedMethodHandler != null) { return hookedMethodHandler.doHookInner(originService, method, args); } else { return method.invoke(originService, args); } } catch (InvocationTargetException e) { Throwable cause = e.getTargetException(); if (cause != null && MyProxy.isMethodDeclaredThrowable(method, cause)) { throw cause; } else if (cause != null) { RuntimeException runtimeException = !TextUtils.isEmpty(cause.getMessage()) ? new RuntimeException(cause.getMessage()) : new RuntimeException(); runtimeException.initCause(cause); throw runtimeException; } else { RuntimeException runtimeException = !TextUtils.isEmpty(e.getMessage()) ? new RuntimeException(e.getMessage()) : new RuntimeException(); runtimeException.initCause(e); throw runtimeException; } } catch (IllegalArgumentException e) { try { StringBuilder sb = new StringBuilder(); sb.append(" DROIDPLUGIN{"); if (method != null) { sb.append("method[").append(method.toString()).append("]"); } else { sb.append("method[").append("NULL").append("]"); } if (args != null) { sb.append("args[").append(Arrays.toString(args)).append("]"); } else { sb.append("args[").append("NULL").append("]"); } sb.append("}"); String message = e.getMessage() + sb.toString(); throw new IllegalArgumentException(message, e); } catch (Throwable e1) { throw e; } } catch (Throwable e) { if (MyProxy.isMethodDeclaredThrowable(method, e)) { throw e; } else { RuntimeException runtimeException = !TextUtils.isEmpty(e.getMessage()) ? new RuntimeException(e.getMessage()) : new RuntimeException(); runtimeException.initCause(e); throw runtimeException; } } } private class ServiceManagerHookHandle extends BaseHookHandle { private ServiceManagerHookHandle(Context context) { super(context); } @Override protected void init() { sHookedMethodHandlers.put("queryLocalInterface", new queryLocalInterface(mHostContext)); } class queryLocalInterface extends HookedMethodHandler { public queryLocalInterface(Context context) { super(context); } @Override protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable { Object localInterface = invokeResult; Object proxiedObj = MyServiceManager.getProxiedObj(mServiceName); if (localInterface == null && proxiedObj != null) { setFakedResult(proxiedObj); } } } } @Override protected BaseHookHandle createHookHandle() { return new ServiceManagerHookHandle(mHostContext); } }