通过java代码,将jar或class反编译为java文件的四种方式

通过java代码,将jar或class反编译为java文件的四种方式本文介绍了如何在 SpringBoot 项目中通过 cfr jd core 和 procyon 库 利用 Maven 依赖管理 对 jar 或 class 文件进行反编译 并提供了对应的 Java 代码示例

大家好,欢迎来到IT知识分享网。

目标

在spring boot项目中,通过给定的文件地址,将*.jar或*.class反编译为*.java。

方式一:cfr

pom引用

 <!-- https://mvnrepository.com/artifact/org.benf/cfr --> <dependency> <groupId>org.benf</groupId> <artifactId>cfr</artifactId> <version>0.151</version> </dependency> 

java实现

package com.demo.decompile.cfr; import org.benf.cfr.reader.api.CfrDriver; import org.benf.cfr.reader.util.getopt.OptionsImpl; import org.springframework.util.ResourceUtils; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.List; / * @Author: huangzh * @Date: 2024/3/5 19:43 / public class CFRDemo { 
    public static void main(String[] args) throws IOException { 
    String sourceJar = "D:/xx/xx/xx.jar"; Path sourceJarPath = Paths.get(sourceJar); String sourceJarFileName = sourceJarPath.getFileName().toString().replaceFirst("[.][^.]+$", ""); File file = ResourceUtils.getFile("classpath:"); String relativePath = file.getPath(); String path = relativePath.substring(0, relativePath.lastIndexOf(File.separatorChar)); String outputPath = path + File.separator + "cfr" + File.separator + sourceJarFileName; Long time = cfr(sourceJar, outputPath); System.out.println(String.format("decompiler time: %dms, outputPath: %s", time, outputPath)); } public static Long cfr(String source, String targetPath) throws IOException { 
    Long start = System.currentTimeMillis(); // source jar List<String> files = new ArrayList<>(); files.add(source); // target dir HashMap<String, String> outputMap = new HashMap<>(); outputMap.put("outputdir", targetPath); OptionsImpl options = new OptionsImpl(outputMap); CfrDriver cfrDriver = new CfrDriver.Builder().withBuiltOptions(options).build(); cfrDriver.analyse(files); Long end = System.currentTimeMillis(); return (end - start); } } 

方式二:jd-core

pom引用

 <!-- https://mvnrepository.com/artifact/org.jd/jd-core --> <!-- 如果下载不下来,可使用华为镜像:https://repo.huaweicloud.com/repository/maven/org/jd/jd-core/1.1.3/jd-core-1.1.3.pom--> <dependency> <groupId>org.jd</groupId> <artifactId>jd-core</artifactId> <version>1.1.3</version> </dependency> 

java实现

package com.demo.decompile.jdcore; import org.jd.core.v1.ClassFileToJavaSourceDecompiler; import org.jd.core.v1.api.loader.Loader; import org.jd.core.v1.api.printer.Printer; import org.springframework.util.ResourceUtils; import org.springframework.util.StringUtils; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Enumeration; import java.util.HashMap; import java.util.jar.JarFile; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; / * @Author: huangzh * @Date: 2024/3/5 10:36 / public class JDCoreDemo { 
    public static void main(String[] args) throws Exception { 
    File file = ResourceUtils.getFile("classpath:"); String relativePath = file.getPath(); String path = JDCoreDecompiler.substringBeforeLast(relativePath, File.separator); System.out.println(path); JDCoreDecompiler jdCoreDecompiler = new JDCoreDecompiler(); Long time = jdCoreDecompiler.decompiler("D:\\xx\\xx\\xx.jar", path + File.separator + "JDCore"); System.out.println(String.format("decompiler time: %dms", time)); } } class JDCoreDecompiler{ 
    private ClassFileToJavaSourceDecompiler decompiler = new ClassFileToJavaSourceDecompiler(); // 存放字节码 private HashMap<String,byte[]> classByteMap = new HashMap<>(); / * 注意:没有考虑一个 Java 类编译出多个 Class 文件的情况。 * * @param source * @param target * @return * @throws Exception */ public Long decompiler(String source,String target) throws Exception { 
    long start = System.currentTimeMillis(); String sourceFileName = substringAfterLast(source, File.separator); String sourcePathName = substringBeforeLast(sourceFileName, "."); target = target + File.separator + sourcePathName; // 解压 archive(source); for (String className : classByteMap.keySet()) { 
    String path = substringBeforeLast(className, "/"); String name = substringAfterLast(className, "/"); if (contains(name, "$")) { 
    name = substringAfterLast(name, "$"); } name = StringUtils.replace(name, ".class", ".java"); decompiler.decompile(loader, printer, className); String context = printer.toString(); Path targetPath = Paths.get(target + "/" + path + "/" + name); if (!Files.exists(Paths.get(target + "/" + path))) { 
    Files.createDirectories(Paths.get(target + "/" + path)); } Files.deleteIfExists(targetPath); Files.createFile(targetPath); Files.write(targetPath, context.getBytes()); } return System.currentTimeMillis() - start; } / * 解压 * @param path * @throws IOException */ private void archive(String path) throws IOException { 
    try (ZipFile archive = new JarFile(new File(path))) { 
    Enumeration<? extends ZipEntry> entries = archive.entries(); while (entries.hasMoreElements()) { 
    ZipEntry entry = entries.nextElement(); if (!entry.isDirectory()) { 
    String name = entry.getName(); if (name.endsWith(".class")) { 
    byte[] bytes = null; try (InputStream stream = archive.getInputStream(entry)) { 
    bytes = toByteArray(stream); } classByteMap.put(name, bytes); } } } } } private Loader loader = new Loader() { 
    @Override public byte[] load(String internalName) { 
    return classByteMap.get(internalName); } @Override public boolean canLoad(String internalName) { 
    return classByteMap.containsKey(internalName); } }; private Printer printer = new Printer() { 
    protected static final String TAB = " "; protected static final String NEWLINE = "\n"; protected int indentationCount = 0; protected StringBuilder sb = new StringBuilder(); @Override public String toString() { 
    String toString = sb.toString(); sb = new StringBuilder(); return toString; } @Override public void start(int maxLineNumber, int majorVersion, int minorVersion) { 
   } @Override public void end() { 
   } @Override public void printText(String text) { 
    sb.append(text); } @Override public void printNumericConstant(String constant) { 
    sb.append(constant); } @Override public void printStringConstant(String constant, String ownerInternalName) { 
    sb.append(constant); } @Override public void printKeyword(String keyword) { 
    sb.append(keyword); } @Override public void printDeclaration(int type, String internalTypeName, String name, String descriptor) { 
    sb.append(name); } @Override public void printReference(int type, String internalTypeName, String name, String descriptor, String ownerInternalName) { 
    sb.append(name); } @Override public void indent() { 
    this.indentationCount++; } @Override public void unindent() { 
    this.indentationCount--; } @Override public void startLine(int lineNumber) { 
    for (int i=0; i<indentationCount; i++) sb.append(TAB); } @Override public void endLine() { 
    sb.append(NEWLINE); } @Override public void extraLine(int count) { 
    while (count-- > 0) sb.append(NEWLINE); } @Override public void startMarker(int type) { 
   } @Override public void endMarker(int type) { 
   } }; / * Represents a failed index search. */ public static final int INDEX_NOT_FOUND = -1; / * The empty String {@code ""}. */ public static final String EMPTY = ""; public static String substringBeforeLast(final String str, final String separator) { 
    if (isEmpty(str) || isEmpty(separator)) { 
    return str; } final int pos = str.lastIndexOf(separator); if (pos == INDEX_NOT_FOUND) { 
    return str; } return str.substring(0, pos); } / * Checks if a CharSequence is empty ("") or null. * @param cs * @return */ public static boolean isEmpty(final CharSequence cs) { 
    return cs == null || cs.length() == 0; } / * Checks if CharSequence contains a search CharSequence, handling {@code null}. * This method uses {@link String#indexOf(String)} if possible. * @param seq * @param searchSeq * @return */ public static boolean contains(final CharSequence seq, final CharSequence searchSeq) { 
    if (seq == null || searchSeq == null) { 
    return false; } return indexOf(seq, searchSeq, 0) >= 0; } / * Used by the indexOf(CharSequence methods) as a green implementation of indexOf. * @param cs * @param searchChar * @param start * @return */ static int indexOf(final CharSequence cs, final CharSequence searchChar, final int start) { 
    if (cs instanceof String) { 
    return ((String) cs).indexOf(searchChar.toString(), start); } if (cs instanceof StringBuilder) { 
    return ((StringBuilder) cs).indexOf(searchChar.toString(), start); } if (cs instanceof StringBuffer) { 
    return ((StringBuffer) cs).indexOf(searchChar.toString(), start); } return cs.toString().indexOf(searchChar.toString(), start); } / * Gets the substring after the last occurrence of a separator. * The separator is not returned. * @param str * @param separator * @return */ public static String substringAfterLast(final String str, final String separator) { 
    if (isEmpty(str)) { 
    return str; } if (isEmpty(separator)) { 
    return EMPTY; } final int pos = str.lastIndexOf(separator); if (pos == INDEX_NOT_FOUND || pos == str.length() - separator.length()) { 
    return EMPTY; } return str.substring(pos + separator.length()); } public static byte[] toByteArray(InputStream inputStream) throws IOException { 
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { 
    outputStream.write(buffer, 0, bytesRead); } return outputStream.toByteArray(); } } 

方式三:procyon.jar

jar下载

java实现

package com.demo.decompile.procyon; import org.springframework.util.ResourceUtils; import java.io.*; import java.nio.file.Path; import java.nio.file.Paths; / * @Author: huangzh * @Date: 2024/3/7 11:46 / public class ProcyonJarDemo { 
    public static void main(String[] args) throws FileNotFoundException { 
    String sourceJar = "D:/xx/xx/xx.jar"; Path sourceJarPath = Paths.get(sourceJar); String sourceJarFileName = sourceJarPath.getFileName().toString().replaceFirst("[.][^.]+$", ""); File file = ResourceUtils.getFile("classpath:"); String relativePath = file.getPath(); String path = relativePath.substring(0, relativePath.lastIndexOf(File.separatorChar)); String outputPath = path + File.separator + "procyon" + File.separator + sourceJarFileName; String[] command = { 
    "java", "-jar", "src/main/resources/procyon/procyon-decompiler-0.6.0.jar", "-jar", sourceJar, "-o", outputPath }; try { 
    Process process = new ProcessBuilder(command).start(); InputStream inputStream = process.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); String line; while ((line = reader.readLine()) != null) { 
    System.out.println("info: " + line); } InputStream inputStreamErr = process.getErrorStream(); BufferedReader readerErr = new BufferedReader(new InputStreamReader(inputStreamErr)); String lineErr; while ((lineErr = readerErr.readLine()) != null) { 
    System.out.println("error: " + lineErr); } // waiting for command execution to complete int exitCode = process.waitFor(); System.out.println("decompile completed,exit code: " + exitCode); System.out.println("output dir: " + outputPath); } catch (IOException | InterruptedException e) { 
    e.printStackTrace(); } } } 

方式四:procyon

pom引用

 <!-- https://mvnrepository.com/artifact/org.jboss.windup.decompiler/decompiler-procyon --> <dependency> <groupId>org.jboss.windup.decompiler</groupId> <artifactId>decompiler-procyon</artifactId> <version>5.1.4.Final</version> </dependency> 

java实现

package com.demo.decompile.procyon; import org.jboss.windup.decompiler.api.DecompilationFailure; import org.jboss.windup.decompiler.api.DecompilationListener; import org.jboss.windup.decompiler.api.DecompilationResult; import org.jboss.windup.decompiler.api.Decompiler; import org.jboss.windup.decompiler.procyon.ProcyonDecompiler; import org.springframework.util.ResourceUtils; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Iterator; import java.util.List; / * @Author: huangzh * @Date: 2024/3/5 18:42 / public class ProcyonDemo { 
    public static void main(String[] args) throws IOException { 
    String sourceJar = "D:/xx/xx/xx.jar"; Path sourceJarPath = Paths.get(sourceJar); String sourceJarFileName = sourceJarPath.getFileName().toString().replaceFirst("[.][^.]+$", ""); File file = ResourceUtils.getFile("classpath:"); String relativePath = file.getPath(); String path = relativePath.substring(0, relativePath.lastIndexOf(File.separatorChar)); String outputPath = path + File.separator + "procyon" + File.separator + sourceJarFileName; Long time = procyon(sourceJar, outputPath); System.out.println(String.format("decompiler time: %dms", time)); } / * 解析存在问题,不推荐 * * @param source * @param targetPath * @return * @throws IOException */ public static Long procyon(String source, String targetPath) throws IOException { 
    long start = System.currentTimeMillis(); Path archive = Paths.get(source); Path outDir = Paths.get(targetPath); Decompiler dec = new ProcyonDecompiler(); DecompilationResult res = dec.decompileArchive(archive, outDir, new DecompilationListener() { 
    public void decompilationProcessComplete() { 
    System.out.println("decompilationProcessComplete"); } public void decompilationFailed(List<String> inputPath, String message) { 
    System.out.println("decompilationFailed"); } public void fileDecompiled(List<String> inputPath, String outputPath) { 
    } public boolean isCancelled() { 
    return false; } }); if (!res.getFailures().isEmpty()) { 
    StringBuilder sb = new StringBuilder(); sb.append("Failed decompilation of " + res.getFailures().size() + " classes: "); Iterator failureIterator = res.getFailures().iterator(); while (failureIterator.hasNext()) { 
    DecompilationFailure dex = (DecompilationFailure) failureIterator.next(); sb.append(System.lineSeparator() + " ").append(dex.getMessage()); } System.out.println(sb); } System.out.println("Compilation results: " + res.getDecompiledFiles().size() + " succeeded, " + res.getFailures().size() + " failed."); dec.close(); Long end = System.currentTimeMillis(); return end - start; } } 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/109761.html

(0)
上一篇 2026-02-04 19:12
下一篇 2026-02-04 19:20

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信