美文网首页反编译 hook 破解
免root hook框架legend源码阅读

免root hook框架legend源码阅读

作者: 普通的程序员 | 来源:发表于2018-12-18 23:25 被阅读0次

首先 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

分析dalvik 最终用c++写入内存

参考阅读
Android免Root环境下Hook框架Legend原理分析
https://zhuanlan.zhihu.com/p/25200724

相关文章

网友评论

    本文标题:免root hook框架legend源码阅读

    本文链接:https://www.haomeiwen.com/subject/tcbrkqtx.html