写在前面
hook(钩子),通常是指对一些方法进行拦截。这样当这些方法被调用时,也能够执行我们自己的代码,从而达到监控或更改代码逻辑的目的。
实现方式
方式一、Java 反射实现Hook:
这种方式是最常见的,也是各种框架常采用的一种方式,直接上例子:
/**
* 利用Hook的方式修复Toast在7.1系统上BadTokenException
* Created by conghongjie on 2018/3/28.
*/
public class ToastCompat extends Toast{
private static final String TAG = "ToastCompat";
public ToastCompat(Context context) {
super(context);
}
@Override
public void show() {
if(Build.VERSION.SDK_INT==Build.VERSION_CODES.N_MR1){
tryToHack();
}
super.show();
}
private void tryToHack(){
try {
Object mTN=getFieldValue(this,"mTN");
if(mTN!=null){
Object rawHandler=getFieldValue(mTN,"mHandler");
if(rawHandler!=null){
//替换为封装后的mHandler
setFieldValue(rawHandler,"mCallback",new InternalHandlerCallback((Handler)rawHandler));
}
}
}catch (Throwable e){
e.printStackTrace();
}
}
private class InternalHandlerCallback implements Handler.Callback{
private final Handler mHandler;
public InternalHandlerCallback(Handler mHandler) {
this.mHandler = mHandler;
}
@Override
public boolean handleMessage(Message msg) {
try {
// 捕获BadTokenException
mHandler.handleMessage(msg);
}catch (BadTokenException e) {
e.printStackTrace();
}
return true;
}
}
}
总结一下,此方法的实现思路:
找到需要Hook方法的系统类(最好是单例的情况)
利用java的动态代理这个系统类
使用反射的方法把这个系统类替换成你的动态代理类
方式二、更改方法体的内存指向: