使用hook修改全局Toast

xiaoxiao2025-07-18  8

使用hook修改全局Toast

需求背景

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

技术背景

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

反射代码块速查

Hook(动态代理)

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

实现细节

流程简述

自定义一个代理类的调度类,它要实现InvocationHandler的invoke方法;通过反射,调用Toast的getService方法获得INotificationManager对象(Hook对象实例);用Hook对象实例为参数,生成一个代理类的实例;用代理类的实例,替换第一步拿到的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 } } }

自定义代理类

生成代理类,并且拦截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); } }

全局替换吐司。

反射调用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(); } }
转载请注明原文地址: https://www.6miu.com/read-5033283.html

最新回复(0)