hibeaver

Project Url: BryanSharp/hibeaver
Introduction: HiBeaver is an android plugin for AOP design by modifying project byte code during build of the package.
More: Author   ReportBugs   
Tags:

Build Status Download Gitter

Java 字节码修改神器 HiBeaver:黑掉你的 SDK

cute animals always busy in building their river dam

简介

HiBeaver 是一个用于进行 Java 字节码插桩的 Gradle 插件,可以用来:

1.修改 Jar 文件内部的代码实现,注入逻辑(同时还支持对 Android 的 aar 文件进行修改)。

2.实现 Java 轻量级 AOP 设计,支持 Android Gradle Plugin。

结合强大的 Java ASM 字节码修改工具和 Gradle Transform API,HiBeaver 可以实现在 Android 应用编译阶段,依据使用者的配置,对工程内所包含的 Java 字节码进行修改,从而支持使用者仅通过 Gradle 配置对字节码进行代码注入和 AOP 设计,或对项目依赖的 Jar 包内的代码增加 Hook 节点。

从 1.2.7 版本开始,HiBeaver 不再依赖于 Android 编译插件,可以在 Gradle 环境下独立运行,随心所欲地修改 Jar/Aar 文件内的代码逻辑。

Beaver,即河狸,是一种日日忙碌于在自己栖息河流上修建和装修大坝的可爱小动物。河狸的堤坝虽说不上像三峡那样“高峡出平湖”,却也为自然和生态做出了暖男般的贡献。

快速上手

该插件已经上传到 Jcenter,可直接引用最新版本如下:

classpath 'com.bryansharp:hibeaver:1.2.7'

在 1.2.7 及以上的版本中,hibeaverModifyFiles 任务不再依赖 Android 的 gradle 插件,也就是说只要你有 gradle 和 Java 运行环境,建一个 build.gradle 就可以指定 Jar/Aar 文件进行修改了。详见 testJarModify 目录下的示例。

Link to Jcenter

然后在工程的 build.gradle 里加入如下片段(或通过其他.gradle 引入):

    apply plugin: 'hiBeaver'
    import com.bryansharp.gradle.hibeaver.utils.MethodLogAdapter
    import org.objectweb.asm.ClassVisitor
    import org.objectweb.asm.MethodVisitor
    import org.objectweb.asm.Opcodes
    //or you can import like bellow:
    //import org.objectweb.asm.*
    hiBeaver {
        //下面这个参数仅仅影响 log 输出,为本次修改命名,无实际意义,不配置也可以
        hiBeaverModifyName = 'myHibeaverTest'
        //设置为 true 可以显示帮助内容,默认为 true
        showHelp = true
        //keepQuiet 默认为 false,为 true 时不会有字节码修改的 log 输出,建议为 false
        keepQuiet = false
        //下面的参数设置为 true 时会输出工程编译耗时信息
        watchTimeConsume = false

        //重头戏是配置下面的参数:modifyMatchMaps
        //基础配置结构形如: ['class':[[:],[:]],'class':[[:],[:]]], 类型是 Map<String, List<Map<String, Object>>>
        modifyMatchMaps = [
                //此处可以进行模糊匹配,!表示排除,!android*即表示排除掉 android 开头的全类名。
                //|符号不完全表示或,而仅仅是匹配的分隔符。*表示任意长度(>0)的任意字符
                '*Activity|*Receiver|!android*'             : [
                        //methodDesc 设置为空代表对 methodDesc 不进行限制
                        //方法名也可以用模糊匹配 用 javap -s 命令来查看类中方法的 description
                        ['methodName': 'on**', 'methodDesc': null, 'adapter': {
                            ClassVisitor cv, int access, String name, String desc, String signature, String[] exceptions ->
                                MethodVisitor methodVisitor = cv.visitMethod(access, name, desc, signature, exceptions);
                                MethodVisitor adapter = new MethodLogAdapter(methodVisitor) {
                                    @Override
                                    void visitCode() {
                                        super.visitCode();
                                        methodVisitor.visitLdcInsn(desc);
                                        methodVisitor.visitLdcInsn(name);
                                      //下面这行代码 为要调用的方法,请酌情修改
                                        methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, 
                                            "bruce/com/testhibeaver/MainActivity", 
                                                "hookXM", "(Ljava/lang/Object;Ljava/lang/Object;)V");
                                    }
                                }
                                return adapter;
                        }]
                ]
                ,
                //此处以 r:开头,代表正则表达式匹配模式
                'r:.*D[a-zA-Z]*Client'              : [
                        ['methodName': 'on**', 'methodDesc': null, 'adapter': {
                            ClassVisitor cv, int access, String name, String desc, String signature, String[] exceptions ->
                                MethodVisitor methodVisitor = cv.visitMethod(access, name, desc, signature, exceptions);
                                MethodVisitor adapter = new MethodLogAdapter(methodVisitor) {
                                    @Override
                                    void visitCode() {
                                        super.visitCode();
                                    }
                                }
                                return adapter;
                        }]
                ]
        ]
        //下面为对 Jar 或 Aar 进行单独修改的配置,执行 hibeaverModifyFiles 的 Gradle 任务来对路径所指向的文件进行修改,
        //产出物在 build/HiBeaver 目录下
        modifyTasks = ["${rootDir.absolutePath}/submodule/app/libs/MiPush_SDK_Client_3_2_2.jar": modifyMatchMaps]
    }

本 repo 项目中还包含一个 submodule,里面有本插件的 demo,可以使用 git submodule 来进行初始化,然后在项目根目录加入 settings.gradle 并编辑(include ':submodule:app')来包含这个子项目(是一个 app demo)。

玩的愉快!有任何问题和 bug 请提 issue,欢迎参与到本项目的完善中!

English Version

By applying the regular expression and wildcard features, HiBeaver now has been upgraded to a Java lightweight AOP design tool.

Beaver means 河狸 in Chinese, cute animals always busy in building their cute river dam.

Basically, HiBeaver is a Gradle plugin for modifying your java byte code.

This plugin has been uploaded to jcenter. You can use this by adding the following code to your buildScripts:

    classpath 'com.bryansharp:HiBeaver:1.2.7'

Link to Jcenter

and then add this to you app build scripts:

    import com.bryansharp.gradle.hibeaver.utils.MethodLogAdapter
    import org.objectweb.asm.ClassVisitor
    import org.objectweb.asm.MethodVisitor
    import org.objectweb.asm.Opcodes
    //or you can import like bellow:
    //import org.objectweb.asm.*
    hiBeaver {
        //this will determine the name of this hibeaver transform, no practical use.
        hiBeaverModifyName = 'myHibeaverTest'
        //turn this on to make it print help content, default value is true
        showHelp = true
        //this flag will decide whether the log of the modifying process be printed or not, default value is false
        keepQuiet = false
        //this is a kit feature of the plugin, set it true to see the time consume of this build
        watchTimeConsume = false

        //this is the most important part
        //basic structure is like ['class':[[:],[:]],'class':[[:],[:]]], type is Map<String, List<Map<String, Object>>>
        //advanced structure is like: ['classMatchPattern':['classMatchType':'wildcard','modifyMethods':[[:],[:]]],'classMatchPattern':['classMatchType':'regEx','modifyMethods':[[:],[:]]]]
        modifyMatchMaps = [
                //this is the basic version
                'classname of which to be modified': [
                        // you can use javap -s command to get the description of one method
                        // the adapter is a closure
                        ['methodName': 'the name of the method', 'methodDesc': 'javap -s to get the description', 'adapter': {
                            //the below args cannot be changed, to copy them entirely with nothing changed is recommended
                            ClassVisitor cv, int access, String name, String desc, String signature, String[] exceptions ->
                                //return null to modify nothing
                                return null;
                        }]
                        ,
                        ['methodName': 'the name of the method2', 'methodDesc': 'javap -s to get the description', 'adapter': {
                            ClassVisitor cv, int access, String name, String desc, String signature, String[] exceptions ->
                                return null;
                        }]
                ]
                ,
                //the latter ones are advanced cases
                '*Activity'                       : [
                        //the value of classMatchType can either be one of the three: all,regEx,wildcard
                        //default value is all
                        'classMatchType': 'wildcard',
                        'modifyMethods' : [
                                //methodMatchType 会同时对 methodName 和 methodDesc 的匹配生效
                                //methodDesc 设置为空代表对 methodDesc 不进行限制
                                ['methodName': 'on**', 'methodMatchType': 'wildcard', 'methodDesc': null, 'adapter': {
                                    ClassVisitor cv, int access, String name, String desc, String signature, String[] exceptions ->
                                        MethodVisitor methodVisitor = cv.visitMethod(access, name, desc, signature, exceptions);
                                        MethodVisitor adapter = new MethodLogAdapter(methodVisitor) {
                                            @Override
                                            void visitCode() {
                                                super.visitCode();
                                                methodVisitor.visitLdcInsn(desc);
                                                methodVisitor.visitLdcInsn(name);
                                                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "bruce/com/testhibeaver/MainActivity", "hookXM", "(Ljava/lang/Object;Ljava/lang/Object;)V");
                                            }
                                        }
                                        return adapter;
                                }]
                        ]
                ]
                ,
                '.*D[a-zA-Z]*Receiver'                       : [
                        'classMatchType': 'regEx',
                        'modifyMethods' : [
                                ['methodName': 'on**', 'methodMatchType': 'wildcard', 'methodDesc': null, 'adapter': {
                                    ClassVisitor cv, int access, String name, String desc, String signature, String[] exceptions ->
                                        MethodVisitor methodVisitor = cv.visitMethod(access, name, desc, signature, exceptions);
                                        MethodVisitor adapter = new MethodLogAdapter(methodVisitor) {
                                            @Override
                                            void visitCode() {
                                                super.visitCode();
                                                methodVisitor.visitLdcInsn(desc);
                                                methodVisitor.visitLdcInsn(name);
                                                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "bruce/com/testhibeaver/MainActivity", "hookXM", "(Ljava/lang/Object;Ljava/lang/Object;)V");
                                            }
                                        }
                                        return adapter;
                                }]
                        ]
                ]
        ]
    }

You can also see the content above in the build log outputs.

There is also a demo showing how to use it. You can either get it through git submodule and add a settings.gradle file to include the module, or get it by checking out hiBeaverDemo.

Hope you can enjoy it! Any comment and suggestion is welcomed.

Apps
About Me
GitHub: Trinea
Facebook: Dev Tools