使用hook修改全局Toast

使用hook修改全局Toast

需求背景

对android原生系统中的Settings模块所有的Toast进行统一替换

技术背景

  1. 反射基础(用于获取所需方法及字段,并替换使用我们的代理类)

    反射代码块速查

  2. Hook(动态代理)

    java动态代理实现与原理详细分析

实现细节

流程简述

  1. 自定义一个代理类的调度类,它要实现InvocationHandler的invoke方法;
  2. 通过反射,调用Toast的getService方法获得INotificationManager对象(Hook对象实例);
  3. 用Hook对象实例为参数,生成一个代理类的实例;
  4. 用代理类的实例,替换第一步拿到的INotificationManager对象。

细节

0.查看android原生Toast代码,内部的sService可以作为hook点

Toast代码代码如下:

public class Toast {
	...
	//HOOK替换的对象
    private static INotificationManager sService;
	
    static private INotificationManager getService() {
        if (sService != null) {
            return sService;
        }
        sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
        return sService;
    }

	...
    /**
     * Show the view for the specified duration.
     */
    public void show() {
        if (mNextView == null) {
            throw new RuntimeException("setView must have been called");
        }

        INotificationManager service = getService();
        String pkg = mContext.getOpPackageName();
        TN tn = mTN;
        tn.mNextView = mNextView;

        try {
            service.enqueueToast(pkg, tn, mDuration);
        } catch (RemoteException e) {
            // Empty
        }
    }
}
  1. 自定义代理类

    生成代理类,并且拦截enqueueToast方法,对入参进行修改后再传入目标的原生的方法。

     public class ToastProxy implements InvocationHandler {
     
         private static final String TAG = "ToastProxy";
         private Object mService;
         private Context mContext;
     
         public Object newProxyInstance(Context context, Object sService) {
             this.mService = sService;
             this.mContext = context;
             return Proxy.newProxyInstance(sService.getClass().getClassLoader(),
                     sService.getClass().getInterfaces(), this);
         }
     
         @Override
         public Object invoke(Object arg0, Method method, Object[] args)
                 throws Throwable {
             Log.i(TAG, "invoke: method == " + method.getName());
     
             if ("enqueueToast".equals(method.getName())) {
                 if (args != null && args.length > 0) {
                     Field mNextView = args[1].getClass().getDeclaredField("mNextView");
                     mNextView.setAccessible(true);
     				
                     View mNextView = (View) mNextViewClazz.get(args[1]);
                     if (mNextView != null && mNextView instanceof LinearLayout){
     					//获取原Toast的文字
                         String nextString = ((TextView) ((LinearLayout) mNextView).getChildAt(0)).getText().toString();
                         //构造所需变量
                         TextView value = new TextView(mContext);
                         value.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
                         value.setBackgroundColor(Color.RED);
                         value.setText(nextString);
                         mNextViewClazz.set(args[1], value);
                     }
                 }
             }
             return method.invoke(mService, args);
         }
     }
    
  2. 全局替换吐司。

    反射调用getService生成sService静态对象,用Hook对象实例为参数,生成一个代理类的实例,用代理类的实例,替换第一步拿到的sService对象。

     private void setToast() {
         try {
             // 获得HOOK点
             Toast toast = new Toast(this);
             Method getService = toast.getClass().getDeclaredMethod("getService");
             getService.setAccessible(true);
             final Object sService = getService.invoke(toast);
             // 生成代理对象
             ToastProxy toastProxy = new ToastProxy();
             Object proxyNotiMng = toastProxy.newProxyInstance(this,sService);
             // 替换 sService
             Field sServiceField = Toast.class.getDeclaredField("sService");
             sServiceField.setAccessible(true);
             sServiceField.set(sService, proxyNotiMng);
    
         } catch (NoSuchMethodException e) {
             e.printStackTrace();
         } catch (IllegalAccessException e) {
             e.printStackTrace();
         } catch (InvocationTargetException e) {
             e.printStackTrace();
         } catch (NoSuchFieldException e) {
             e.printStackTrace();
         }
     }
    

你可能感兴趣的:(Hook)