在计算机编程中,hook
函数是指在特定的事件发生时被调用的函数,用于在事件发生前或后进行一些特定的操作。
通常,hook
函数作为回调函数被注册到事件处理器中,当事件发生时,事件处理器会自动调用相应的hook
函数。
简单理解就是回调函数的触发。
那么我突然想到 我们在java开发中,自定义接口,做监听接口回调或者kotlin的高阶函数。
Java中回调函数的触发或kotlin 高阶函数的invoke。
这不就是hook函数么。
脑子开始发散了,记得以前做Android的热更新不是用的hook 技术么?hook技术是劫持,通过反射和代理实现‘偷梁换柱’。
react 的hook 函数 创建 useEfffect 对象,做一些逻辑处理。
???????这是不是不太对呀。
下面就开始整理分析一下。
先说结论:
概念:钩子函数的运作通常依赖于操作系统或框架的内部机制。操作系统为开发者提供了一组API,用于注册和管理钩子函数。当系统事件发生时,操作系统会调用对应的钩子函数,此时开发者可以在钩子函数中编写自己的代码,并对事件进行处理。
理解:钩子函数就是操作系统或者框架提供的一组api,我们理解为回调函数。
我们Android端用的hook技术,就是利用反射和代理 对 这类回调函数的调用,做劫持,改变目标函数的调用(就是替换原来的函数‘偷梁换柱’)。
react 中的 hooks ,例如useState, 首先函数执行这块根源都是到类,类都要extends Component 这个component 是from React 框架的,框架源码类有生命周期出发时的 特定的回调函数,这就是useState、useEffect 等。 使用这个特定的回调函数就会触发对应的生命周期的动作。
下面通过 Hook View 的 OnClickListener 来说明 Hook 的使用方法。
首先进入 View 的 setOnClickListener 方法,我们看到 OnClickListener 对象被保存在了一个叫做 ListenerInfo 的内部类里,其中 mListenerInfo 是 View 的成员变量。ListeneInfo 里面保存了 View 的各种监听事件,比如 OnClickListener、OnLongClickListener、OnKeyListener 等等。
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;
}
我们的目标是 Hook OnClickListener,所以就要在给 View 设置监听事件后,替换 OnClickListener 对象,注入自定义的操作。
private void hookOnClickListener(View view) {
try {
// 得到 View 的 ListenerInfo 对象
Method getListenerInfo = View.class.getDeclaredMethod("getListenerInfo");
getListenerInfo.setAccessible(true);
Object listenerInfo = getListenerInfo.invoke(view);
// 得到 原始的 OnClickListener 对象
Class> listenerInfoClz = Class.forName("android.view.View$ListenerInfo");
Field mOnClickListener = listenerInfoClz.getDeclaredField("mOnClickListener");
mOnClickListener.setAccessible(true);
View.OnClickListener originOnClickListener = (View.OnClickListener) mOnClickListener.get(listenerInfo);
// 用自定义的 OnClickListener 替换原始的 OnClickListener
View.OnClickListener hookedOnClickListener = new HookedOnClickListener(originOnClickListener);
mOnClickListener.set(listenerInfo, hookedOnClickListener);
} catch (Exception e) {
log.warn("hook clickListener failed!", e);
}
}
class HookedOnClickListener implements View.OnClickListener {
private View.OnClickListener origin;
HookedOnClickListener(View.OnClickListener origin) {
this.origin = origin;
}
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "hook click", Toast.LENGTH_SHORT).show();
log.info("Before click, do what you want to to.");
if (origin != null) {
origin.onClick(v);
}
log.info("After click, do what you want to to.");
}
}
到这里,我们成功 Hook 了 OnClickListener,在点击之前和点击之后可以执行某些操作,达到了我们的目的。下面是调用的部分,在给 Button 设置 OnClickListener 后,执行 Hook 操作。点击按钮后,日志的打印结果是:Before click → onClick → After click。
Button btnSend = (Button) findViewById(R.id.btn_send);
btnSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
log.info("onClick");
}
});
hookOnClickListener(btnSend);
这里是不是看到源码这块了,后续我会写点framwork的东西,至于什么时候就看缘分了。
native 模式
java 模式
主要通过反射和代理实现,应用与sdk开发环境中修改java代码。
应用与NDK 开发环境和系统开发修改Native 代码。
从实现的维度来看: 应用进程hook 和 全局hook 。
应用程序进程 hook 只是hook 当前应用程序进程。
如果对 zygote 进程(系统级别的进程孵化器)进行hook ,那就可以实现将所有应用程序进程进行修改,这就是全局hook。
1、找到hook点。
hook点必须满足以下条件:
2、将hook方法放到系统之外执行。
总结:
hook的原理就是劫持函数的调用,改变目标函数的调用。原理不复杂,但实现有难度。
tip: 热更新tinker 、Robust 都是用hook原理,找到应用的特定的回调函数处理。
React Hooks 是 React 16.8 引入的一项重要特性,它使函数组件能够拥有类组件的一些特性,例如状态管理和生命周期方法的使用。
通过 Hooks,可以更加简洁和灵活地编写 React 组件。
React Hooks 是一种函数式组件的增强机制,它允许你在不编写类组件的情况下使用 React 的特性。主要的 Hooks 包括 useState
, useEffect
, useContext
, useReducer
, useCallback
, useMemo
, useRef
, 和 useImperativeHandle
等。这些 Hooks 提供了访问 React 特性的方式,使得你可以更好地组织和重用你的代码。
useEffect
, useCallback
, useMemo
等 Hooks 可以更精确地控制副作用和性能消耗。Hooks 也使得组件逻辑的测试变得更简单,因为你可以单独测试每个 hook 的逻辑,而不需要包装在一个组件中。
此外,Hooks 还支持自定义,你可以编写自己的 Hooks 来封装复杂的逻辑,然后在多个组件中重用。
结束。
=============================================
参考:
Android hook 原理