发布时间:2025-12-09 13:43:36 浏览次数:4
相信很多搞机的朋友都玩过Xposed, 它实现了很多不可思议的功能。它是怎么实现的呢?这里就得提到我们的Hook技术了。
关于 Android 中的 Hook 机制,大致有两个方式:
时间所限,这里不展开了。
知识点:
今天主要讲的是利用hook技术,将点击事件劫持,做点啥呢?你可以随意,我这里是将双击给屏蔽了。免得点击过快打开多个界面。
这一步比较关键,也是个难点。Android中主要是依靠分析系统源码类来做到的,首先我们得找到被Hook的对象,我称之为Hook点;什么样的对象比较好Hook呢?自然是容易找到的对象。什么样的对象容易找到?静态变量和单例;在一个进程之内,静态变量和单例变量是相对不容易发生变化的,因此非常容易定位,而普通的对象则要么无法标志,要么容易改变。我们根据这个原则找到所谓的Hook。
去看setOnClickListener里面做了什么?
public void setOnClickListener(@Nullable OnClickListener l) { if (!isClickable()) { setClickable(true); } getListenerInfo().mOnClickListener = l; }
ListenerInfo getListenerInfo() { if (mListenerInfo != null) { return mListenerInfo; } mListenerInfo = new ListenerInfo(); return mListenerInfo; }看完了上面,就能猜到我们设置的Listener最终是被赋值给ListenerInfo的mOnClickListener成员了,ListenerInfo的实例可以说是信息的载体,那么很简单,只要把mOnClickListener,在ListenerInfo中还有mOnLongClickListener,mOnFocusChangeListener两个成员,分别对应了长按事件与焦点变化事件,所以处理长按事件与焦点变化事件与此类似。
public class HookViewClickUtil { public static HookViewClickUtil getInstance() { return UtilHolder.mHookViewClickUtil; } private static class UtilHolder { private static HookViewClickUtil mHookViewClickUtil = new HookViewClickUtil(); } public static void hookView(View view) { try { Class viewClazz = Class.forName("android.view.View"); //事件监听器都是这个实例保存的 Method listenerInfoMethod = viewClazz.getDeclaredMethod("getListenerInfo"); if (!listenerInfoMethod.isAccessible()) { listenerInfoMethod.setAccessible(true); } Object listenerInfoObj = listenerInfoMethod.invoke(view); Class listenerInfoClazz = Class.forName("android.view.View$ListenerInfo"); Field onClickListenerField = listenerInfoClazz.getDeclaredField("mOnClickListener"); if (!onClickListenerField.isAccessible()) { onClickListenerField.setAccessible(true); } View.OnClickListener mOnClickListener = (View.OnClickListener) onClickListenerField.get(listenerInfoObj); //自定义代理事件监听器 View.OnClickListener onClickListenerProxy = new OnClickListenerProxy(mOnClickListener); //更换 onClickListenerField.set(listenerInfoObj, onClickListenerProxy); } catch (Exception e) { e.printStackTrace(); } } //自定义的代理事件监听器 private static class OnClickListenerProxy implements View.OnClickListener { private View.OnClickListener object; private int MIN_CLICK_DELAY_TIME = 1000; private long lastClickTime = 0; private OnClickListenerProxy(View.OnClickListener object) { this.object = object; } @Override public void onClick(View v) { //点击时间控制 long currentTime = Calendar.getInstance().getTimeInMillis(); if (currentTime - lastClickTime > MIN_CLICK_DELAY_TIME) { lastClickTime = currentTime; Log.e("OnClickListenerProxy", "OnClickListenerProxy"); if (object != null) object.onClick(v); } } }}使用起来也是非常简单,首先在MainActivity的View渲染完毕的时候进行注入,即在 getWindow().getDecorView().post()中。
public class MainActivity extends Activity { private Button btn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final View btn = findViewById(R.id.btn); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.e("MainActivity","Button 被点击了"); } }); getWindow().getDecorView().post(new Runnable() { @Override public void run() { HookViewClickUtil.hookView(btn); } }); }}