大家好,又见面了,我是你们的朋友全栈君。
最近面试说到了这个hook技术,其实就是钩子函数,但是具体如何应用需要一探究竟,私下总结一下。
Hook简介
微软的MSDN中,对Hook的解释为: A hook is a point in the system message-handling mechanism where an application can install a subroutine to monitor the message traffic in the system and process certain types of messages before they reach the target window procedure.
微软只是简单的将Hook解释为一种过滤(或叫挂钩)消息的技术。
我们这里讲解的Hook,简单解释为:挂钩,挂钩一切事物。包含微软的解释。
挂钩的事物通常指的是函数。 Hook 目的:
过滤一些关键函数调用,在函数执行前,先执行自己的挂钩函数。达到监控函数调用,改变函数功能的目的。
可能前面讲的不是很透彻,通过后面的实例应该会更清晰。
Hook 技术实现的步骤分两步:
找Hook点
看到这,我们明白了,其实是 ActivityManager.getService() 最终调用了 startActivity() 方法,我们看 ActivityManager.getService() 方法。
package com.radish.android.hookframeworktest;
import android.content.Context;
import android.content.Intent;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class HookUtils {
private Context context;
public void hookStartActivity(Context context) {
this.context = context;
try {
/**
* 这里注意一下,用我们分析的源码运行不了,所以稍微改了一下,
* 思路什么都一样,只是源码的属性名做了修改
*/
// Class<?> activityManagerClass = Class.forName("android.app.ActivityManager");
Class<?> activityManagerClass = Class.forName("android.app.ActivityManagerNative");
//拿到 IActivityManagerSingleton 属性
// Field field = activityManagerClass.getDeclaredField("IActivityManagerSingleton");
Field field = activityManagerClass.getDeclaredField("gDefault");
field.setAccessible(true);
//获取到是 Singleton 对象,也就是 field 对应的类型
Object singletonObj = field.get(null);
//然后获取 Singletone 的 mInstance 属性
Class<?> singtonClass = Class.forName("android.util.Singleton");
Field mInstanceField = singtonClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
//真正的 hook 点
Object iActivityManagerObj = mInstanceField.get(singletonObj);
//hook 第二步,动态代理
Class<?> iActivityManagerIntercept = Class.forName("android.app.IActivityManager");
StartActivityHandler startActivityHandler = new StartActivityHandler(iActivityManagerObj);
Object proxyIActivityManager = Proxy.newProxyInstance(getClass().getClassLoader(),
new Class[]{iActivityManagerIntercept}, startActivityHandler);
//在这我们将系统的对象更换成我们生成的动态代理对象,为了是调用动态代理的 invoke 方法,不更换不执行
mInstanceField.set(singletonObj, proxyIActivityManager);
} catch (Exception e) {
e.printStackTrace();
}
}
class StartActivityHandler implements InvocationHandler {
//系统真正的对象
private Object trueIActivityManager;
public StartActivityHandler(Object trueIActivityManager) {
this.trueIActivityManager = trueIActivityManager;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("startActivity".equals(method.getName())) {
System.out.println("abc : --------------------- startActivity ---------------------");
Intent intent = null;
int index = -1;
for (int i = 0; i < args.length; i++) {
Object obj = args[i];
if (obj instanceof Intent) {
//找到 startActivity 传递进来的 Intent
intent = (Intent) obj;
index = i;
}
}
//瞒天过海,获取想要跳转的意图,进行篡改
Intent newIntent = new Intent(context, ProxyActivity.class);
//我们将真实的意图封装在假意图当中
newIntent.putExtra("oldIntent", intent);
args[index] = newIntent;
}
return method.invoke(trueIActivityManager, args);
}
}
}
接下来是使用:
总结下,目前我们实现的功能是,不管你跳转任何的 Activity,我们都跳转到 ProxyActivity,所以我们只需要在清单文件中注册一个 ProxyActivity 而不用注册其他的 Activity 也不会崩溃,是如何实现的呢?我们是通过使用 hook 技术篡改 Intent,并将你真正的意图存放到我们新的 Intent 中。这时候,应该有些人要打我了,我明明想去东京和巴黎,你却带我去了浪漫的土耳其~~~~~
参考文档:https://www.jianshu.com/p/3382cc765b39 https://blog.csdn.net/qq_36381855/article/details/79962673 https://blog.csdn.net/bruce135lee/article/details/80944504
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/141316.html原文链接:https://javaforall.cn