安卓应用分析:如何分析Jar内Class文件并反编译为Java文件
解决方案
1 Jar解压性能优化
现有代码速度慢的核心原因是采用了单字节读写模式,is.read()每次仅读取1字节,IO交互次数极高,在处理大体积Jar文件时性能会非常差。同时原代码仅调用mkdir(),会出现父目录不存在时创建子目录失败的问题。
优化后的代码如下,采用8KB缓冲区批量读写,同时补充目录层级创建逻辑:
import java.io.*; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; public class JarExtractor { private static final int BUFFER_SIZE = 8192; // 可根据硬件配置调整缓冲区大小 public void extractJar(String jarFilePath, String destDirPath) throws IOException { File destDir = new File(destDirPath); if (!destDir.exists()) { destDir.mkdirs(); } try (JarFile jar = new JarFile(jarFilePath)) { Enumeration<JarEntry> enumEntries = jar.entries(); byte[] buffer = new byte[BUFFER_SIZE]; while (enumEntries.hasMoreElements()) { JarEntry entry = enumEntries.nextElement(); File entryFile = new File(destDir, entry.getName()); if (entry.isDirectory()) { entryFile.mkdirs(); // 递归创建所有层级父目录 continue; } // 提前创建当前文件的父目录,避免写入失败 entryFile.getParentFile().mkdirs(); try (InputStream is = jar.getInputStream(entry); FileOutputStream fos = new FileOutputStream(entryFile)) { int readLen; while ((readLen = is.read(buffer)) != -1) { fos.write(buffer, 0, readLen); } } } } } }
优化后解压速度可以提升数十倍,同时解决了目录创建失败的异常问题。
2 Class文件反编译为Java文件实现
jd-core可以正常实现批量反编译,多数运行失败是因为依赖引入错误或者调用方式不对。可以直接在内存中处理Jar中的Class字节流,不需要先落地到本地磁盘,进一步提升处理效率。
jd-core的调用示例如下,需先引入对应版本的jd-core依赖:
import org.jd.core.v1.ClassFileToJavaSourceDecompiler; import org.jd.core.v1.api.loader.Loader; import org.jd.core.v1.api.loader.LoaderException; import org.jd.core.v1.api.printer.Printer; import org.jd.core.v1.service.printer.PlainTextPrinter; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.io.*; // 自定义Loader从Jar字节流加载Class public class JarEntryLoader implements Loader { private final JarFile jarFile; public JarEntryLoader(JarFile jarFile) { this.jarFile = jarFile; } @Override public byte[] load(String internalName) throws LoaderException { try { JarEntry entry = jarFile.getJarEntry(internalName + ".class"); if (entry == null) return null; try (InputStream is = jarFile.getInputStream(entry)) { return is.readAllBytes(); } } catch (IOException e) { throw new LoaderException(e); } } @Override public boolean canLoad(String internalName) { return jarFile.getJarEntry(internalName + ".class") != null; } } // 反编译调用逻辑 public void decompileJar(String jarFilePath, String outputJavaDir) throws Exception { File outputDir = new File(outputJavaDir); outputDir.mkdirs(); try (JarFile jarFile = new JarFile(jarFilePath)) { Loader loader = new JarEntryLoader(jarFile); ClassFileToJavaSourceDecompiler decompiler = new ClassFileToJavaSourceDecompiler(); Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); if (entry.isDirectory() || !entry.getName().endsWith(".class")) { continue; } // 去掉.class后缀得到内部类名 String internalName = entry.getName().substring(0, entry.getName().length() - 6); Printer printer = new PlainTextPrinter(); decompiler.decompile(loader, printer, internalName); String javaSource = printer.toString(); // 写入.java文件 File javaFile = new File(outputDir, internalName + ".java"); javaFile.getParentFile().mkdirs(); try (FileWriter writer = new FileWriter(javaFile)) { writer.write(javaSource); } } } }
如果对jd-core的反编译效果不满意,也可以替换为Fernflower反编译器,它是IntelliJ IDEA默认集成的反编译器,对复杂语法、Lambda表达式的反编译准确率更高,调用逻辑和上述流程类似。
内容的提问来源于stack exchange,提问作者LeeTi
相关产品推荐
相关产品推荐

