腾讯性能监控框架Matrix源码分析(十八)SQLiteLint耗电分析3之Hook系统服务

225 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情 上一篇文章说到了hook wife的方法,如何 Hook 系统服务的调用?

主流上一般有三种方案:字节码插桩,动态代理,Native Hook。

Matrix使用的动态代理来实现, 动态代理的流程分三步

  • 第一步肯定首先是要看源码流程了
  • 第二步找单例和接口切入点
  • 第三步就是设计实现类。

我们分析源码看看

// 调用一般都是通过 context 获取系统服务

WifiManager mWifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
mWifi.startScan();

对应找到 /frameworks/base/core/java/android/app/ContextImpl.java 中的 getSystemService 方法

    @Override
    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }

再找到 /frameworks/base/core/java/android/app/SystemServiceRegistry.java 中的 getSystemService 方法

    private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS = new HashMap<String, ServiceFetcher<?>>();
    
    static {
        registerService(Context.WIFI_SERVICE, WifiManager.class,
                new CachedServiceFetcher<WifiManager>() {
            @Override
            public WifiManager createService(ContextImpl ctx) throws ServiceNotFoundException {
                IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_SERVICE);
                IWifiManager service = IWifiManager.Stub.asInterface(b);
                return new WifiManager(ctx.getOuterContext(), service,
                        ConnectivityThread.getInstanceLooper());
            }});
    }

    /**
     * Gets a system service from a given context.
     */
    public static Object getSystemService(ContextImpl ctx, String name) {
        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
        return fetcher != null ? fetcher.getService(ctx) : null;
    }

    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);
    }

看到这里第二步的方案已经出来了,单例就是 WifiManager 而接口对象就是 WifiManager 中的 mService 对象,只要 Hook 住 mService 就可以了,这里我们再分析一个切入点,我们接着往 ServiceManager.getServiceOrThrow 中看:

    private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();

    public static IBinder getService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return Binder.allowBlocking(rawGetService(name));
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in getService", e);
        }
        return null;
    }

    public static IBinder getServiceOrThrow(String name) throws ServiceNotFoundException {
        final IBinder binder = getService(name);
        if (binder != null) {
            return binder;
        } else {
            throw new ServiceNotFoundException(name);
        }
    }

再往 IWifiManager.Stub.asInterface 中看:

public static IWifiManager asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof IWifiManager))) {
        return ((IWifiManager)iin);
      }
      return new IWifiManager.Stub.Proxy(obj);
    }

看到这里我们就有了第二种方案了,Hook 住 Binder 对象的 queryLocalInterface 方法返回一个代理对象即可。最后一步就是设计实现类了:

我们进入doHook方法

public class SystemServiceBinderHooker {
    private static final String TAG = "Matrix.battery.SystemServiceBinderHooker";

    public interface HookCallback {
        void onServiceMethodInvoke(Method method, Object[] args);
        @Nullable Object onServiceMethodIntercept(Object receiver, Method method, Object[] args) throws Throwable;
    }

    private final String mServiceName;
    private final String mServiceClass;
    private final HookCallback mHookCallback;

    @Nullable private IBinder mOriginServiceBinder;
    @Nullable private IBinder mDelegateServiceBinder;

    public SystemServiceBinderHooker(final String serviceName, final String serviceClass, final HookCallback hookCallback) {
        mServiceName = serviceName;
        mServiceClass = serviceClass;
        mHookCallback = hookCallback;
    }

    @SuppressWarnings({"PrivateApi", "unchecked", "rawtypes"})
    public boolean doHook() {
        MatrixLog.i(TAG, "doHook: serviceName:%s, serviceClsName:%s", mServiceName, mServiceClass);
        try {           
            //创建代理操作者,负责生成代理对象
            BinderProxyHandler binderProxyHandler = new BinderProxyHandler(mServiceName, mServiceClass, mHookCallback);
            //生成代理binder
            IBinder delegateBinder = binderProxyHandler.createProxyBinder();
            //将代理对象放到 ServiceManager 的 sCache 容器中,
            //这样当下次 getService 时如果有相应的服务则直接返回,这里为直接返回我们自己反射构造的对象
            Class<?> serviceManagerCls = Class.forName("android.os.ServiceManager");
            Field cacheField = serviceManagerCls.getDeclaredField("sCache");
            cacheField.setAccessible(true);
            Map<String, IBinder> cache = (Map) cacheField.get(null);
            cache.put(mServiceName, delegateBinder);
            
            mDelegateServiceBinder = delegateBinder;
            mOriginServiceBinder = binderProxyHandler.getOriginBinder();
            return true;

        } catch (Throwable e) {
            MatrixLog.e(TAG, "#doHook exp: " + e.getLocalizedMessage());
        }
        return false;
    }
    //解绑就是替换成原理的对象
    @SuppressWarnings({"PrivateApi", "unchecked", "rawtypes"})
    public boolean doUnHook() {
        if (mOriginServiceBinder == null) {
            MatrixLog.w(TAG, "#doUnHook mOriginServiceBinder null");
            return false;
        }
        if (mDelegateServiceBinder == null) {
            MatrixLog.w(TAG, "#doUnHook mDelegateServiceBinder null");
            return false;
        }

        try {
            IBinder currentBinder = BinderProxyHandler.getCurrentBinder(mServiceName);
            if (mDelegateServiceBinder != currentBinder) {
                MatrixLog.w(TAG, "#doUnHook mDelegateServiceBinder != currentBinder");
                return false;
            }

            Class<?> serviceManagerCls = Class.forName("android.os.ServiceManager");
            Field cacheField = serviceManagerCls.getDeclaredField("sCache");
            cacheField.setAccessible(true);
            Map<String, IBinder> cache = (Map) cacheField.get(null);
            cache.put(mServiceName, mOriginServiceBinder);
            return true;
        } catch (Throwable e) {
            MatrixLog.e(TAG, "#doUnHook exp: " + e.getLocalizedMessage());
        }
        return false;
    }
}

通过分析我们知道生成了代理binder,通过反射替换掉sCache内真正的binder便达到了我们的目的 那么代理binder如何生成呢?正好利用接口实现的特性,我通过动态代理来实现,看看BinderProxyHandler

//实现了InvocationHandler那必然是动态代理对象
 static final class BinderProxyHandler implements InvocationHandler {
        private final IBinder mOriginBinder;
        private final Object mServiceManagerProxy;

        BinderProxyHandler(String serviceName, String serviceClass, HookCallback callback) throws Exception {
            //首先通过反射保留以前的binder
            mOriginBinder = getCurrentBinder(serviceName);
            //通过动态代理生成mServiceManagerProxy
            mServiceManagerProxy = createServiceManagerProxy(serviceClass, mOriginBinder, callback);
        }
        // Hook 住 Binder 对象的 queryLocalInterface 方法返回一个代理对mServiceManagerProxy
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if ("queryLocalInterface".equals(method.getName())) {
                return mServiceManagerProxy;
            }
            return method.invoke(mOriginBinder, args);
        }

        public IBinder getOriginBinder() {
            return mOriginBinder;
        }

        @SuppressWarnings({"PrivateApi"})
        public IBinder createProxyBinder() throws Exception  {
            Class<?> serviceManagerCls = Class.forName("android.os.ServiceManager");
            ClassLoader classLoader = serviceManagerCls.getClassLoader();
            if (classLoader == null) {
                throw new IllegalStateException("Can not get ClassLoader of " + serviceManagerCls.getName());
            }
            return (IBinder) Proxy.newProxyInstance(
                    classLoader,
                    new Class<?>[]{IBinder.class},
                    this
            );
        }

        @SuppressWarnings({"PrivateApi"})
        static IBinder getCurrentBinder(String serviceName) throws Exception {
            Class<?> serviceManagerCls = Class.forName("android.os.ServiceManager");
            Method getService = serviceManagerCls.getDeclaredMethod("getService", String.class);
            return  (IBinder) getService.invoke(null, serviceName);
        }

代理对象mServiceManagerProxy又是如何生成?动态代理!

    @SuppressWarnings({"PrivateApi"})
       private static Object createServiceManagerProxy(String serviceClassName, IBinder originBinder, final HookCallback callback) throws Exception  {
           Class<?> serviceManagerCls = Class.forName(serviceClassName);
           Class<?> serviceManagerStubCls = Class.forName(serviceClassName + "$Stub");
           ClassLoader classLoader = serviceManagerStubCls.getClassLoader();
           if (classLoader == null) {
               throw new IllegalStateException("get service manager ClassLoader fail!");
           }
           Method asInterfaceMethod = serviceManagerStubCls.getDeclaredMethod("asInterface", IBinder.class);
           final Object originManagerService = asInterfaceMethod.invoke(null, originBinder);
           return Proxy.newProxyInstance(classLoader,
                   new Class[]{IBinder.class, IInterface.class, serviceManagerCls},
                   new InvocationHandler() {
                       @Override
                       public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                           if (callback != null) {
                               callback.onServiceMethodInvoke(method, args);
                               Object result = callback.onServiceMethodIntercept(originManagerService, method, args);
                               if (result != null) {
                                   return result;
                               }
                           }
                           return method.invoke(originManagerService, args);
                       }
                   }
           );
       }
   }
  

代码量不大,但是有点绕,通过反射,动态代理嵌套动态代理我们最终hook了系统服务,通过onServiceMethodInvoke onServiceMethodIntercept两个回调实现了我们hook各种系统服务的目的,除了wife还通过相同方法监控了

  • 1.WakeLockMonitorFeature

    监控的是IPowerManageracquireWakeLockreleaseWakeLock方法

  • 2.LocationMonitorFeature

    监控的是ILocationManagerrequestLocationUpdates方法

  • 3.BlueToothMonitorFeatur

    监控的是IBluetoothManagerregisterAdaptergetBluetoothGattregisterScanner startScan``startScanForIntent)方法

  • 4.NotificationMonitorFeature

    监控的是INotificationManagercreateNotificationChannelsenqueueNotificationWithTag方法

  • 5.AlarmMonitorFeature

    监控的是IAramManagerset setRepeating setInexactReating remove方法

其他代码可以举一反三看一下,这里就不列举了。