type
status
date
slug
summary
tags
category
icon
password
Hook的本质就是就是利用Java反射机制,将源码中一些类的对象替换自己实现的对象,以实现一些特殊的操作.
基本上所有Hook的入门都会从Hook一个View的点击事件开始.
Hook之前,一般都得先看看源码,以找到如何反射,如何替换比较合适.
view.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; }
ListenerInfo
是View
的一个内部类.如果我们想在点击View的时候进行一些特殊的操作,其实只要将mOnClickListener替换成我们自己的OnClickListener就行了,
拿到 getListenerInfo()
方法
val getListenerInfoMethod = View::class.java.getDeclaredMethod("getListenerInfo") getListenerInfoMethod.isAccessible = true
执行getListenerInfo()
方法,获取View
对应的ListenerInfo
对象
val listenerInfo = getListenerInfoMethod.invoke(view)
拿到ListenerInfo
中的mOnClickListener
Field
val listenerInfoClz = Class.forName("android.view.View\\$ListenerInfo") val mOnClickListener = listenerInfoClz.getDeclaredField("mOnClickListener") mOnClickListener.isAccessible = true
拿到业务传入的OnClickListener对象
由于我们一般不会去显式的修改业务逻辑,所以需要保存一个原始的OnClickListener对象,以保证业务的正常执行.
val originOnClickListener = mOnClickListener[listenerInfo] as View.OnClickListener
设置一个新的OnClickListener对象,并持有之前的原始OnClickListener对象
// 新的listener val hookedOnClickListener: View.OnClickListener = HookOnClickListener(originOnClickListener, this) // 赋值新的listener mOnClickListener[listenerInfo] = hookedOnClickListener
class HookOnClickListener(private val originListener: View.OnClickListener?, private val context: Context) : View.OnClickListener { override fun onClick(v: View) { //点击之前 Log.d(TAG, "onClick: before") // 执行原始的点击逻辑 originListener?.onClick(v) //点击之后 Log.d(TAG, "onClick: after") } companion object { private const val TAG = "hook" } }
到这里基本就OK了,但是观察上面代码,其实需要传入一个View对象,并且需要获取View的原始点击事件,因此hook操作要放到设置点击事件之后,实用性并不是很高.
完整代码
class HackActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_hack) packageManager.getInstalledPackages(0) btn_hook_OnClick.setOnClickListener { ToastUtils.showShortToast("点击了Button") } hookOnClickListener(btn_hook_OnClick) } private fun hookOnClickListener(view:View) { try { //getListenerInfo() val getListenerInfoMethod = View::class.java.getDeclaredMethod("getListenerInfo") getListenerInfoMethod.isAccessible = true // 调用getListenerInfo()方法,得到ListenerInfo对象 val listenerInfo = getListenerInfoMethod.invoke(view) //得到View的mOnClickListener Field val listenerInfoClz = Class.forName("android.view.View\\$ListenerInfo") val mOnClickListener = listenerInfoClz.getDeclaredField("mOnClickListener") mOnClickListener.isAccessible = true // 获取到原来的listener值 val originOnClickListener = mOnClickListener[listenerInfo] as View.OnClickListener // 新的listener val hookedOnClickListener: View.OnClickListener = HookOnClickListener(originOnClickListener, this) // 赋值新的listener mOnClickListener[listenerInfo] = hookedOnClickListener } catch (t: Throwable) { t.printStackTrace() } } }
源码地址
- 作者:姜康
- 链接:https://jiangkang.tech/article/790ac297-a1bc-4fae-8421-13dda12e2619
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章