首先 Legend项目地址
https://github.com/asLody/legend
- 通过注解方式调用hook
使用方法
1.先需要在类的入口处
HookManager.getDefault().applyHooks(YourClass.class);
2.再使用形如 @Hook("完整类名::方法名@参数1#参数2#参数3....")
写在你的类里
@Hook("android.app.Activity::startActivity@android.content.Intent")
public static void Activity_startActivity(Activity thiz, Intent intent) {
if (!ALLOW_LAUNCH_ACTIVITY) {
Toast.makeText(thiz, "I am sorry to turn your Activity down :)", Toast.LENGTH_SHORT).show();
} else {
HookManager.getDefault().callSuper(thiz, intent);//调用原始方法
}
}
这个方法的实际实现是这样的
public void applyHooks(Class<?> holdClass) {
for (Method hookMethod : holdClass.getDeclaredMethods()) {//遍历该类下所有的方法
Hook hook = hookMethod.getAnnotation(Hook.class);//找到有@Hook注解的方法
if (hook != null) {
String statement = hook.value();//这里可以查看Hook类的内容,里面只有一个value方法
String[] splitValues = statement.split("::");//按照规则分割取被hook的类名,方法名
if (splitValues.length == 2) {
String className = splitValues[0];//取得类名
String[] methodNameWithSignature = splitValues[1].split("@");//分割方法名
if (methodNameWithSignature.length <= 2) {
String methodName = methodNameWithSignature[0];//取得方法名
String signature = methodNameWithSignature.length == 2 ? methodNameWithSignature[1] : "";//取得方法签名,方法签名 = 方法名 + 参数类型(argument types)
String[] paramList = signature.split("#");//取参数类型
if (paramList[0].equals("")) {
paramList = new String[0];//这就是代表无参函数,直接给一个0长度的string 数组
}
try {
Class<?> clazz = Class.forName(className);//根据类名找类,所以使用时需要传入完整类名
boolean isResolve = false;
for (Method method : clazz.getDeclaredMethods()) {//遍历被hook类的所有方法
if (method.getName().equals(methodName)) {//如果某个方法的名字一样
Class<?>[] types = method.getParameterTypes();//通过方法参数类型去判断
if (paramList.length == types.length) {//方法参数长度一样,进入下一步判断
boolean isMatch = true;//先置true
for (int N = 0; N < types.length; N++) {
if (!types[N].getName().equals(paramList[N])) {
isMatch = false;
break;
}
}//如果每一个参数类型都一样,说明匹配到了要被hook的方法,否则不匹配
if (isMatch) {
hookMethod(method, hookMethod);//匹配就hook
isResolve = true;//处理完毕
Logger.d("[+++] %s have hooked.", method.getName());
}
}
}
if (isResolve) {
break;
}
}
if (!isResolve) {
Logger.e("[---] Cannot resolve Method : %s.", Arrays.toString(methodNameWithSignature));
}
} catch (Throwable e) {
Logger.e("[---] Error to Load Hook Method From : %s." , hookMethod.getName());
e.printStackTrace();
}
}else {
Logger.e("[---] Can't split method and signature : %s.", Arrays.toString(methodNameWithSignature));
}
}else {
Logger.e("[---] Can't understand your statement : [%s].", statement);
}
}
}
}
- 通过api方式调用hook
HookManager.getDefault().hookMethod(originMethod, hookMethod);
实际上该方法,就是通过注解方式hook的最终实现方法;
public void hookMethod(Method origin, Method hook) {
if (origin == null) {//需要被hook的原始方法
throw new IllegalArgumentException("Origin method cannot be null");
}
if (hook == null) {
throw new IllegalArgumentException("Hook method cannot be null");
}
if (!Modifier.isStatic(hook.getModifiers())) {//检验hook是不是静态的,否则出错,这是使用legend的必须
throw new IllegalStateException("Hook method must be a static method.");
}
origin.setAccessible(true);
hook.setAccessible(true);
String methodName = Runtime.isArt() ? hook.getName() : origin.getName();
Method backupMethod;
if (Runtime.isArt()) {//根据art或者dalvik去做处理,这里的方法看不太懂了
backupMethod = hookMethodArt(origin, hook);
}else {
backupMethod = hookMethodDalvik(origin, hook);
}
String className = hook.getDeclaringClass().getName();
Map<String,List<Method>> methodNameToBackupMethodsMap = classToBackupMethodsMapping.get(className);
//维护一份被hook类的原始方法列,方便以后使用原方法
if (methodNameToBackupMethodsMap == null) {
methodNameToBackupMethodsMap = new ConcurrentHashMap<String, List<Method>>();
classToBackupMethodsMapping.put(className, methodNameToBackupMethodsMap);
}
//维护一份被hook类的原始方法列,用ConcurrentHashMap的好处是,可能有多个被hook的类同时处理
List<Method> backupList = methodNameToBackupMethodsMap.get(methodName);
//被hook类的方法列,用LinkedList存储,方便添加
if (backupList == null) {
backupList = new LinkedList<Method>();
methodNameToBackupMethodsMap.put(methodName, backupList);
}
backupMethod.setAccessible(true);
backupList.add(backupMethod);//把被hook的方法备份后存入
}
根据art/dalvik处理的hookMethod方法解析参考了四哥的blog
https://blog.csdn.net/jiangwei0910410003/article/details/74435238
参考阅读
Android免Root环境下Hook框架Legend原理分析
https://zhuanlan.zhihu.com/p/25200724