使用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();
         }
     }
    
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在C#中使用全局鼠标、键盘Hook需要使用Win32 API来实现,以下是一个简单的示例代码: ```csharp using System; using System.Diagnostics; using System.Runtime.InteropServices; namespace GlobalHookDemo { class Program { private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId); [DllImport("user32.dll", SetLastError = true)] private static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr GetModuleHandle(string lpModuleName); private const int WH_KEYBOARD_LL = 13; private static LowLevelKeyboardProc _proc = HookCallback; private static IntPtr _hookID = IntPtr.Zero; static void Main(string[] args) { _hookID = SetHook(_proc); Console.WriteLine("Global keyboard hook installed."); Console.WriteLine("Press any key to exit."); Console.ReadKey(); UnhookWindowsHookEx(_hookID); } private static IntPtr SetHook(LowLevelKeyboardProc proc) { using (Process curProcess = Process.GetCurrentProcess()) using (ProcessModule curModule = curProcess.MainModule) { return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0); } } private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0 && wParam == (IntPtr)0x100) { int vkCode = Marshal.ReadInt32(lParam); Console.WriteLine((Keys)vkCode); } return CallNextHookEx(_hookID, nCode, wParam, lParam); } } } ``` 这个示例代码可以监听全局键盘事件并输出键码。如果需要监听鼠标事件,可以使用`SetWindowsHookEx`函数的第一个参数`idHook`设置为`WH_MOUSE_LL`。需要注意的是,全局Hook会影响系统性能,使用时需要谨慎。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值