美文网首页Android Gradle
Hook Android Gradle插件修改编译后的dex(转

Hook Android Gradle插件修改编译后的dex(转

作者: 天宝_4ab8 | 来源:发表于2020-03-18 11:05 被阅读0次

    Android Gradle学习(四):Project详解

    https://www.jianshu.com/p/179fff0dd7df

    本文着重介绍如何Hook Android Gradle插件的实现,涉及到的Gradle以及Groovy基础会稍微提下,具体可以参考文末给出的博客。

    本文分为两部分:

    1. Hook Android Gradle插件

    2. 使用dexlib2修改编译中的dex文件--修改dex中的函数名字

    1. Hook Android Gradle插件

    Android Gradle Plugin可以简单的理解为是由好多task组成的,这么多task组成一个task graph。在build apk的时候有的task负责编译每个class,有的task负责组成dex,有的task负责打包生成apk。这些task按照task graph上的顺序依次执行生成一个apk。

    Executing tasks: [:app:assembleDebug]
    :app:preBuild UP-TO-DATE
    :app:preDebugBuild UP-TO-DATE
    :app:compileDebugAidl UP-TO-DATE
    :app:compileDebugRenderscript UP-TO-DATE
    :app:checkDebugManifest UP-TO-DATE
    :app:generateDebugBuildConfig UP-TO-DATE
    :app:prepareLintJar UP-TO-DATE
    :app:mainApkListPersistenceDebug UP-TO-DATE
    :app:generateDebugResValues UP-TO-DATE
    :app:generateDebugResources UP-TO-DATE
    :app:mergeDebugResources UP-TO-DATE
    :app:createDebugCompatibleScreenManifests UP-TO-DATE
    :app:processDebugManifest UP-TO-DATE
    :app:splitsDiscoveryTaskDebug UP-TO-DATE
    :app:processDebugResources UP-TO-DATE
    :app:generateDebugSources UP-TO-DATE
    :app:javaPreCompileDebug
    :app:compileDebugJavaWithJavac
    :app:compileDebugNdk NO-SOURCE
    :app:compileDebugSources
    :app:mergeDebugShaders
    :app:compileDebugShaders
    :app:generateDebugAsset
    :app:mergeDebugAssets
    :app:transformClassesWithDexBuilderForDebug
    :app:transformDexArchiveWithExternalLibsDexMergerForDebug
    :app:transformDexArchiveWithDexMergerForDebug
    :app:mergeDebugJniLibFolders
    :app:transformNativeLibsWithMergeJniLibsForDebug
    :app:processDebugJavaRes NO-SOURCE
    :app:transformResourcesWithMergeJavaResForDebug
    :app:validateSigningDebug
    :app:packageDebug
    :app:assembleDebug
    BUILD SUCCESSFUL in 8s
    26 actionable tasks: 13 executed, 13 up-to-date
    
    

    我们的目的是在Android gradle plugin自己build apk的时候hook transformDexArchiveWithDexMergerForDebug这个task,这个task可以在上图中找到,Android Gradle Plugin会把各个class编译生成dex,transformDexArchiveWithDexMergerForDebug这个task的作用就是把生成的各个dex组合成一个dex。所以我们看中了这个task,只要hook它,可以在它生成整体的dex后使用dexlib2更改其中的class或者method。

    首先要先掌握如何自定义Android Gradle插件,这样把它打包上传后就可以在其他Android工程中直接使用了,很方便,这一步可以直接参考这个博客,写的很详细,一步一步按着做即可。在AndroidStudio中自定义Gradle插件 这个同学写的几篇关于Gradle插件的文章都可以看看,写的不错的。这个链接给出的教程只是开发一个独立的Android gradle插件,这个插件里面只有一个task,就是输出一句话而已,我们在直接工程中加入apply plugin:'XXXXX',再指定这个plugin的路径,build的时候就会输出这句话。这个插件的作用就是在Android gradle插件build project的时候加上了一个task。但是这个task是一个独立的task,并没有对Android gradle插件中的task作用,Android插件该怎么build怎么build,没有更改它的task graph,也谈不上hook。

    下面直接上代码了,上面链接中已经讲述了如何生成一个Android gradle plugin。直接在上面代码的基础上改:

    public class MyPlugin implements Plugin<Project> {
    
        void apply(Project project1) {
            System.out.println("========================");
            System.out.println("Hello gradle plugin!");
            System.out.println("========================");
            project1.afterEvaluate { project ->
                project.tasks.transformDexArchiveWithDexMergerForDebug << {
                    println 'add my own step from plugin'
                    //Get the inputs of this task
                    project.tasks.transformDexArchiveWithDexMergerForDebug.getInputs().getFiles().collect().each { element1 ->
                        println "inputs " + element1
                    }
                    //Get the outputs of this task
                    project.tasks.transformDexArchiveWithDexMergerForDebug.getOutputs().getFiles().collect().each() { element ->
                        def file = new File(element.toString())
                        def files = file.listFiles()
                        def files2 = files[0].listFiles()
                        String dexfilepath = files2[0]
                        println "Outputs Dex file's path: "+dexfilepath
                        //Modify the dex 可先注释掉
                        testRewrite(dexfilepath)
                    }
                }
            }
        }
    }
    
    

    上述代码的作用很简单,里面的变量project就是Android Gradle plugin的project,然后它执行到transformDexArchiveWithDexMergerForDebug这个task的时候就加上自己的一个action,这个action很简单就是打印这个task的输入和输出,然后testRewrite()函数就是用来更改这个task生成的dex的。看一下我们hook的结果。

    Executing tasks: [:app:assembleDebug]
    ========================
    Hello gradle plugin!
    ========================
    The Task.leftShift(Closure) method has been deprecated and is scheduled to be removed in Gradle 5.0. Please use Task.doLast(Action) instead.
    :app:preBuild UP-TO-DATE
    //省略了部分task
    :app:transformClassesWithDexBuilderForDebug
    :app:transformDexArchiveWithExternalLibsDexMergerForDebug
    //这个task就是被我们hook的task
    :app:transformDexArchiveWithDexMergerForDebug
    add my own step from plugin
    //很多inputs 这里只列出了一部分
    inputs /Users/xxx/AndroidStudioProjects/gradleTest/app/build/intermediates/transforms/dexBuilder/debug/7.jar
    inputs /Users/xxx/AndroidStudioProjects/gradleTest/app/build/intermediates/transforms/dexBuilder/debug/6.jar
    inputs /Users/xxx/AndroidStudioProjects/gradleTest/app/build/intermediates/transforms/dexBuilder/debug/19/com/example/dean/gradletest/R$bool.dex
    inputs /Users/xxx/AndroidStudioProjects/gradleTest/app/build/intermediates/transforms/dexBuilder/debug/19/com/example/dean/gradletest/R$integer.dex
    inputs /Users/xxx/AndroidStudioProjects/gradleTest/app/build/intermediates/transforms/externalLibsDexMerger/debug/0/classes.dex
    //output只是一个dex
    Outputs Dex file's path: /Users/xxxAndroidStudioProjects/gradleTest/app/build/intermediates/transforms/dexMerger/debug/0/classes.dex
    :app:mergeDebugJniLibFolders
    :app:transformNativeLibsWithMergeJniLibsForDebug
    :app:processDebugJavaRes NO-SOURCE
    :app:transformResourcesWithMergeJavaResForDebug
    :app:validateSigningDebug
    :app:packageDebug
    :app:assembleDebug
    BUILD SUCCESSFUL in 4s
    26 actionable tasks: 13 executed, 13 up-to-date
    
    

    通过这种方式我们可以hook Android Gradle Plugin中的任意一个task。

    2. 修改dex文件--修改dex中的函数名字

    这里就需要用到dexlib2这个工具了,直接在自定义插件的build.gradle中的dependency中加入compile group: 'org.smali', name: 'dexlib2', version: '2.2.4',如下所示。注:不要自行下载jar包加载,这样这个jar包无法打包到自己的插件里,会出现bug。

    dependencies {
        // https://mvnrepository.com/artifact/org.smali/dexlib2
        compile group: 'org.smali', name: 'dexlib2', version: '2.2.4'
        //gradle sdk
        compile gradleApi()
        //groovy sdk
        compile localGroovy()
    }
    
    

    在自定义插件类的统一文件中加入以下代码

     static void testRewrite(String dexfilepath){
            DexFile dexFile
            try {
                //把要修改的dex load进来
                dexFile = DexFileFactory.loadDexFile(dexfilepath, Opcodes.getDefault())
                println "dexFile: " + dexFile.getClass().getName()
                DexRewriter rewriter = new DexRewriter(new RewriterModule() {
                    @Override
                    Rewriter<org.jf.dexlib2.iface.Method> getMethodRewriter(
                            @Nonnull Rewriters rewriters) {
                        return new MyMethod()
                    }
                })
                //删除原dex
                DexFile rewrittenDexFile = rewriter.rewriteDexFile(dexFile);
                File olddex = new File(dexfilepath)
                if(olddex.exists()){
                    println "delete original dex"
                    olddex.delete()
                }
                //生成新dex
                DexFileFactory.writeDexFile(dexfilepath, rewrittenDexFile);
            } catch (IOException e) {
                println "failed"
                e.printStackTrace();
            }
        }
    }
    //修改dex中的method
    class MyMethod implements Rewriter<org.jf.dexlib2.iface.Method> {
        @Nonnull
        @Override
        org.jf.dexlib2.iface.Method rewrite(@Nonnull final org.jf.dexlib2.iface.Method value) {
           //找到 helloMethod 
            if (value.getName().contains("helloMethod")) {
                println "rewrite: "+value.getName()
                return new org.jf.dexlib2.iface.Method() {
                    @Override
                    public int compareTo(@Nonnull MethodReference o) {
                        return value.compareTo(o);
                    }
                    @Nonnull
                    @Override
                    public List<? extends CharSequence> getParameterTypes() {
                        return value.getParameters();
                    }
                    @Nonnull
                    @Override
                    public String getDefiningClass() {
                        return value.getDefiningClass();
                    }
                    @Nonnull
                    @Override
                    public List<? extends MethodParameter> getParameters() {
                        return value.getParameters();
                    }
                    @Nonnull
                    @Override
                    public String getReturnType() {
                        return value.getReturnType();
                    }
                    @Override
                    public int getAccessFlags() {
                        return value.getAccessFlags();
                    }
                    @javax.annotation.Nullable
                    @Override
                    public MethodImplementation getImplementation() {
                        return value.getImplementation();
                    }
                    @Nonnull
                    @Override
                    public Set<? extends org.jf.dexlib2.iface.Annotation> getAnnotations() {
                        return value.getAnnotations();
                    }
                    @Nonnull
                    @Override
                    //将helloMethod重命名为MyMethod
                    public String getName() {
                        return "MyMethod"
                    }
                };
            }
            return value;
        }
    }
    
    

    Bingo!!!
    最后, 同学点个赞吧!!! 加个关注好么
    Gradle for Android(一) 使用Gradle和Android Studio 这个人写了一系列,可以看看。
    晚点有时间我会上传到git上。

    源码:https://github.com/chuanweizhang2013/androidGradlePlugin

    相关文章

      网友评论

        本文标题:Hook Android Gradle插件修改编译后的dex(转

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