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; }
ListenerInfoView的一个内部类.
如果我们想在点击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() } } }

源码地址

Android Hook之拦截Activity的启动Gradle读取配置文件
姜康
姜康
一个软件工程师
公告
type
status
date
slug
summary
tags
category
icon
password
🎉博客网站重新制作了🎉
👏欢迎更新体验👏