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

news2024/12/23 7:24:22

目录

  • 目标
  • 方式一:cfr
    • pom引用
    • java实现
  • 方式二:jd-core
    • pom引用
    • java实现
  • 方式三:procyon.jar
    • jar下载
    • java实现
  • 方式四:procyon
    • pom引用
    • java实现

目标

在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下载

procyon-decompiler-0.6.0.jar下载地址:https://github.com/mstrobel/procyon/releases/tag/v0.6.0
下载后可存放于合适的位置,如:src/main/resources/procyon/procyon-decompiler-0.6.0.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;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1630534.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

通义千问(Qwen)AI大模型-系列_2

算力平台AutoDL pip install -r requirements.txt -i https://pypi.mirrors.ustc.edu.cn/simple -i https://pypi.mirrors.ustc.edu.cn/simple 一、通义千问系列模型 1、CodeQwen1.5-7B-Chat CodeQwen1.5是Qwen1.5的代码特定版本。它是一种基于变换器的纯解码器语言模型&…

【Unity学习笔记】第十四 Prefab 概念解惑

目录 1 prefab、prefab变体、prefab覆盖和prefab 嵌套2 connect 与unpack3 prefab到底是什么&#xff0c;它和gameobject又有什么区别&#xff1f;4 为什么要用prefab&#xff1f;5 代码动态加载prefab6 为什么我unity PrefabUtility.InstantiatePrefab() 得到的是null7 Prefab…

【LeetCode刷题记录】简单篇-70-爬楼梯

【题目描述】 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 【测试用例】 示例1&#xff1a; 输入&#xff1a;n 2 输出&#xff1a;2 解释&#xff1a;有两种方法可以爬到楼顶。 1.1阶 1阶…

EI会议、投稿优惠2024年计算机科学与神经网络国际会议(ICCSNN 2024)开始征稿啦!!

2024 International Conference on Computer Science and Neural Networks 一、大会信息 会议名称&#xff1a;2024年计算机科学与神经网络国际会议会议简称&#xff1a;ICCSNN 2024收录检索&#xff1a;提交Ei Compendex,CPCI,CNKI,Google Scholar等会议官网&#xff1a;http…

蓦然回首,追忆那些备战OCM的日子

蓦然回首 前段时间偶然在墨天轮群看到一位在墨天轮轮社区非常活跃的老兄发的《那些年&#xff0c;我们一起追过的OCP》的文章&#xff0c;获悉墨天轮在举办【我的备考经验】的有奖征文活动&#xff0c;打开那篇文章&#xff0c;一下子又把我的思绪拉回到了好几年前&#xff0c;…

ruoyi实现大文件上传

前端&#xff1a; 安装依赖 1.在package.json文件中添加"vue-simple-uploader": "^0.7.4","spark-md5": "^3.0.1"到dependencies中&#xff1b;devDependencies中"node-sass": "^4.9.0", "sass-loader&quo…

Redis面试题三(集群)

目录 1.Redis 集群搭建有几种模式 2.Redis 主从复制的实现 全量同步 增量同步 3.Redis 的主从同步策略 1. 全量同步&#xff08;Full Resynchronization&#xff09; 2. 增量同步&#xff08;Incremental Replication&#xff09; 4.Redis一致性hash 基本原理 节点动态…

BAPI_ACC_DOCUMENT_POST 凭证过账及增强

METHOD document_post.DATA ls_documentheader TYPE bapiache09. "表头DATA ls_accountgl TYPE bapiacgl09.DATA lt_accountgl TYPE STANDARD TABLE OF bapiacgl09. "总账项目DATA ls_accountreceivable TYPE bapiacar09.DATA lt_accountreceivable TYPE STANDARD TA…

LVS/NAT工作模式介绍及配置

1.1 LVS/NAT模式工作原理 LVS&#xff08;Linux Virtual Server&#xff09;的网络地址转换&#xff08;NAT&#xff09;模式是一种在网络层&#xff08;第四层&#xff09;实现负载均衡的方法。在NAT模式中&#xff0c;Director Server&#xff08;DS&#xff09;充当所有服务…

[Diffusion Model笔记] DDPM数学推导版 2024.04.23

本文是观看以下视频的笔记&#xff1a; https://www.bilibili.com/video/BV1CU4y1i7jn/?p4&spm_id_frompageDriver 其他参考 https://zhuanlan.zhihu.com/p/614147698 https://zhuanlan.zhihu.com/p/563661713 这个写的非常详细&#xff1a; https://www.zhihu.com/ques…

【新手必读】Airtest测试Android手机常见的设置问题

经常有新手同学在使用Airtest测试Android手机的时候&#xff0c;遇到各式各样的问题&#xff0c;其中很大一部分&#xff0c;都是因为Android手机的设置不当&#xff0c;比如&#xff1a; 因为没有登录华为/荣耀/小米账号&#xff0c;而无法开启USB调试功能 因为没有关闭防止恶…

06_Scala流程控制

文章目录 [toc] 1.流程控制**小结&#xff1a;** **2. Scala中流程控制没有三元运算符****2.1 Scala中如果逻辑代码只有一行可以省略花括号****小结&#xff1a;** **3. 循环控制****3.1 for控制****3.2循环守卫 --> 循环表达式添加逻辑判断****3.3 循环步长 --> 表示循环…

IntelliJ IDEA 如何启用 JDK 预览特性

IntelliJ IDEA 也可以启用 JDK 的预览特性。 针对项目&#xff0c;选择项目结构。 配置是在语言结构上。 单击语言结构上的 SDK 默认&#xff0c;往下拉&#xff0c;就可以看到针对新版本的选项。 同时还可以看到那些版本是支持新特性预览的&#xff0c;那些版本是不支持新特…

Python 使用相对路径读取文件失败

python open一个问及那时使用绝对路径可以&#xff0c;但是使用相对路径时报错&#xff0c;找不到指定文件 解决步骤如下&#xff1a; 添加Python配置 在新增的配置Json文件添加下图红框这一行

Linux——(关于权限常见的3个问题)

文章目录 1.修改文件或者目录的拥有者和所属组1.1chown指令1.2chgrp指令 2.常见的权限三个问题2.1对应一个目录&#xff0c;如果要进入&#xff0c;需要什么权限&#xff1f;2.2为什么我们创建的文件默认权限不是7772.2.1关于Linux下的权限掩码 2.3文件能否被删除取决于什么2.3…

与Apollo共创生态:Apollo7周年大会自动驾驶生态利剑出鞘

前言 4月22日&#xff0c;百度Apollo在北京车展前夕举办了以“破晓•拥抱智变时刻”为主题的智能汽车产品发布会&#xff0c;围绕汽车智能化&#xff0c;发布了智驾、智舱、智图等全新升级的“驾舱图”系列产品。 1、7周年大会 自2013年百度开始布局自动驾驶&#xff0c;201…

吴恩达2022机器学习专项课程(一) 6.2 逻辑回归第三周课后实验:Lab2逻辑回归

问题预览/关键词 逻辑回归预测分类创建逻辑回归算法Sigmoid函数Sigmoid函数的表示sigmoid输出的结果Numpy计算指数的方法实验python实现sigmoid函数打印输入的z值和sigmoid计算的值可视化z值和sigmoid的值添加更多数据&#xff0c;使用逻辑回归可以正常预测分类![在这里插入图片…

GMSSL编译iOS

一、GMSSL-2.x 国密SDK源码下载&#xff0c;对GMSSL库进行编译生成对应的静态库。执行如下命令&#xff1a; cd到SDK源码目录 cd /Users/xxxx/Downloads/GMSSLV2-master查看SDK适用环境 ./config上图中错误解决方法 使用文本编辑器打开SDK目录下Configure、test/build.info、…

第十五届蓝桥杯省赛第二场C/C++B组C题【传送阵】题解(AC)

解题思路 由于 a a a 数组是一个 1 1 1 到 n n n 的一个排列&#xff0c;那么形成的一定是如下形式&#xff1a; 一定会构成几个点的循环&#xff0c;或者是几个单独的点。 从任意点开始&#xff0c;如果能进入一个循环&#xff0c;一定可以将整个循环的宝藏都拿走&#x…

android room 数据库升级的原则

1.如果新加了一张数据表则什么都不用干直接database那里将数据库版本升1 就可以nichuang 在entities里增加新加的entity ProviderMeta.DB_VERSION 版本号增1 room会自动生成 一个ProviderMeta.DB_VERSION 版本号的json文件 比如实例中升级到70 就会生成一个70.json的文件这是r…