About modifying class bytecode in jar and aar

There is always the need to modify the class bytecode in jar and aar. Before that, I wanted to decompress directly with compression tools, write code modification directly with javasist, and then replace the classes.jar to AAR format. But it seems that this breaks the AAR structure and makes AAR unusable. So I wrote a tool.

Take a look at the renderings (aar that can be identified by modifying classes.jar)

With the help of https://github.com/BryanSharp/hibeaver to modify the class bytecode-related code in jar and aar (plagiarism), thanks to the author.

The GitHub address of the following tool is https://github.com/zjw-swun/AppMethodTime

Related code

 public static File unzipEntryToTemp(ZipEntry element, ZipFile zipFile, String parentDir) {
        def stream = zipFile.getInputStream(element);
        def array = IOUtils.toByteArray(stream);
        //String hex = DigestUtils.md5Hex(element.getName());
        File targetFile = new File(parentDir, "temp.jar");
        if (targetFile.exists()) {
            targetFile.delete()
        }
        def out = new FileOutputStream(targetFile)
        out.write(array)
        out.close()
        stream.close()
        return targetFile
    }

    public
    static File modifyJar(File jarFile) {
        ClassPath jarClassPath = pool.appendClassPath(jarFile.path)
        ClassPath androidClassPath = pool.insertClassPath(androidJarPath)
        /**
         * Read the original jar
         */
        def file = new JarFile(jarFile);
        /** Set the jar to output */
        def hexName = "";
        hexName = DigestUtils.md5Hex(jarFile.absolutePath).substring(0, 8);

        def outputJar = new File(jarFile.parent, "Target_" + jarFile.name)
        if (outputJar.exists()) {
            outputJar.delete()
        }
        JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(outputJar));
        Enumeration enumeration = file.entries();
        while (enumeration.hasMoreElements()) {
            JarEntry jarEntry = (JarEntry) enumeration.nextElement();
            InputStream inputStream = file.getInputStream(jarEntry);

            String entryName = jarEntry.getName();
            String className

            ZipEntry zipEntry = new ZipEntry(entryName);

            jarOutputStream.putNextEntry(zipEntry);

            byte[] modifiedClassBytes = null;
            byte[] sourceClassBytes = IOUtils.toByteArray(inputStream);
            if (filter(entryName)) {
                //println("entryName "+entryName)
                className = entryName.replace("/", ".").replace(".class", "")
                //println("modifyJar className "+className)
                CtClass c = modifyClass(className)
                if (c != null) {
                    modifiedClassBytes = c.toBytecode()
                    c.detach()
                }
            }
            if (modifiedClassBytes == null) {
                jarOutputStream.write(sourceClassBytes);
            } else {
                jarOutputStream.write(modifiedClassBytes);
            }
            jarOutputStream.closeEntry();
        }
//            Log.info("${hexName} is modified");
        jarOutputStream.close();
        file.close();
        pool.removeClassPath(jarClassPath)
        pool.removeClassPath(androidClassPath)
        return outputJar;
    }

    public static void modifyAar(File targetFile) {

        ZipFile zipFile = new ZipFile(targetFile);
        Enumeration<ZipEntry> entries = zipFile.entries();

        def outputAar = new File(targetFile.parent, "Target_" + targetFile.name)
        if (outputAar.exists()) {
            outputAar.delete()
        }

        ZipOutputStream outputAarStream = new ZipOutputStream(new FileOutputStream(outputAar))
        FileInputStream fileInputStream = null;
        File innerJar = null;
        File outJar = null;
        while (entries.hasMoreElements()) {
            ZipEntry element = entries.nextElement();
            def name = element.getName();
            ZipEntry zipEntry = new ZipEntry(name);

            outputAarStream.putNextEntry(zipEntry);
            if (name.endsWith(".jar")) {
                innerJar = unzipEntryToTemp(element, zipFile, targetFile.parent);
                outJar = modifyJar(innerJar);
                fileInputStream = new FileInputStream(outJar)
                outputAarStream.write(IOUtils.toByteArray(fileInputStream))
            } else {
                def stream = zipFile.getInputStream(element)
                byte[] array = IOUtils.toByteArray(stream)
                if (array != null) {
                    outputAarStream.write(array)
                }
                stream.close()
            }
            outputAarStream.closeEntry();
        }
        zipFile.close()
        if (fileInputStream!= null){
            fileInputStream.close()
        }
        outputAarStream.close()
        if (innerJar != null){
            innerJar.delete()
        }
        if (outJar != null){
            outJar.delete()
        }
    }

BuilSrc module in the project is a transform api plug-in

Instructions

Add the aarOrJarPath configuration field, fill in the target jar or aar path and execute the other directory appMethodJarOrAar task in the corresponding project of the gradle panel. The target jar or aar file with the Target_prefix can be generated in the same directory of the aarOrJarPath configuration.

Can refer to
https://github.com/zjw-swun/AppMethodTime/blob/master/mylibrary/build.gradle

Tags: github Gradle

Posted on Mon, 07 Oct 2019 05:14:41 -0700 by Janus13