java反编译工具有哪些(java反编译class命令)

您所在的位置:网站首页 paths怎么读音 java反编译工具有哪些(java反编译class命令)

java反编译工具有哪些(java反编译class命令)

2023-03-30 21:56| 来源: 网络整理| 查看: 265

前言

java 反编译,一听可能觉得高深莫测,其实反编译并不是什么特别高级的操作,java 对于 class 字节码文件的生成有着严格的要求,如果你非常熟悉 java 虚拟机规范,了解 class 字节码文件中一些字节的作用,那么理解反编译的原理并不是什么问题。甚至像下面这样的 class 文件你都能看懂一二。

java 反编译工具的使用与对比分析

一般在逆向研究和代码分析中,反编译用到的比较多。不过在日常开发中,有时候只是简单的看一下所用依赖类的反编译,也是十分重要的。

恰好最近工作中也需要用到 java 反编译,所以这篇文章介绍目前常见的的几种 java 反编译工具的使用,在文章的最后也会通过编译速度、语法支持以及代码可读性三个维度,对它们进行测试,分析几款工具的优缺点。

procyon

github 链接:https://github.com/mstrobel/procyon

procyon 不仅仅是反编译工具,它其实是专注于 java 代码的生成和分析的一整套的 java 元编程工具。主要包括下面几个部分:

core frameworkreflection frameworkexpressions frameworkcompiler toolset (experimental)java decompiler (experimental)

可以看到反编译只是 procyon 的其中一个模块,procyon 原来托管于 bitbucket,后来迁移到了 github,根据 github 的提交记录来看,也有将近两年没有更新了。不过也有依赖 procyon 的其他的开源反编译工具如** decompiler-procyon**,更新频率还是很高的,下面也会选择这个工具进行反编译测试。

使用 procyon     org.jboss.windup.decompiler     decompiler-procyon     5.1.4.final

写一个简单的反编译测试。

package com.wdbyte.decompiler; import java.io.ioexception; import java.nio.file.path; import java.nio.file.paths; import java.util.iterator; import java.util.list; 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; /**  * procyon 反编译测试  *  *  @author https://github.com/niumoo  * @date 2021/05/15  */ public class procyontest {     public static void main(string[] args) throws ioexception {         long time = procyon("decompiler.jar", "procyon_output_jar");         system.out.println(string.format("decompiler time: %dms", time));     }     public static long procyon(string source,string targetpath) throws ioexception {         long start = system.currenttimemillis();         path outdir = paths.get(targetpath);         path archive = paths.get(source);         decompiler dec = new procyondecompiler();         decompilationresult res = dec.decompilearchive(archive, outdir, new decompilationlistener() {             public void decompilationprocesscomplete() {                 system.out.println("decompilationprocesscomplete");             }             public void decompilationfailed(list inputpath, string message) {                 system.out.println("decompilationfailed");             }             public void filedecompiled(list 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.tostring());         }         system.out.println("compilation results: " + res.getdecompiledfiles().size() + " succeeded, " + res.getfailures().size() + " failed.");         dec.close();         long end = system.currenttimemillis();         return end - start;     } }

procyon 在反编译时会实时输出反编译文件数量的进度情况,最后还会统计反编译成功和失败的 class 文件数量。

.... 五月 15, 2021 10:58:28 下午 org.jboss.windup.decompiler.procyon.procyondecompiler$3 call 信息: decompiling 650 / 783 五月 15, 2021 10:58:30 下午 org.jboss.windup.decompiler.procyon.procyondecompiler$3 call 信息: decompiling 700 / 783 五月 15, 2021 10:58:37 下午 org.jboss.windup.decompiler.procyon.procyondecompiler$3 call 信息: decompiling 750 / 783 decompilationprocesscomplete compilation results: 783 succeeded, 0 failed. decompiler time: 40599ms procyon gui

对于 procyon 反编译来说,在 github 上也有基于此实现的开源 gui 界面,感兴趣的可以下载尝试。github 地址:https://github.com/deathmarine/luyten

cfr

github 地址:https://github.com/leibnitz27/cfrcfr 官方网站:http://www.benf.org/other/cfr/(可能需要fq)maven 仓库:https://mvnrepository.com/artifact/org.benf/cfr

cfr(class file reader) 可以支持 java 9、java 12、java 14 以及其他的最新版 java 代码的反编译工作。而且 cfr 本身的代码是由 java 6 编写,所以基本可以使用 cfr 在任何版本的 java 程序中。值得一提的是,使用 cfr 甚至可以将使用其他语言编写的的 jvm 类文件反编译回 java 文件。

cfr 命令行使用

使用 cfr 反编译时,你可以下载已经发布的 jar 包,进行命令行反编译,也可以使用 maven 引入的方式,在代码中使用。下面先说命令行运行的方式。

直接在 github tags 下载已发布的最新版 jar. 可以直接运行查看帮助。

# 查看帮助 java -jar cfr-0.151.jar --help

如果只是反编译某个 class.

# 反编译 class 文件,结果输出到控制台 java -jar cfr-0.151.jar windupclasspathtypeloader.class # 反编译 class 文件,结果输出到 out 文件夹 java -jar cfr-0.151.jar windupclasspathtypeloader.class --outputpath ./out

反编译某个 jar.

# 反编译 jar 文件,结果输出到 output_jar 文件夹 ➜  desktop java -jar cfr-0.151.jar decompiler.jar --outputdir ./output_jar processing decompiler.jar (use silent to silence) processing com.strobel.assembler.metadata.arraytypeloader processing com.strobel.assembler.metadata.parameterdefinition processing com.strobel.assembler.metadata.methodhandle processing com.strobel.assembler.metadata.signatures.floatsignature .....

反编译结果会按照 class 的包路径写入到指定文件夹中。

java 反编译工具的使用与对比分析cfr 代码中使用

添加依赖这里不提。

    org.benf     cfr     0.151

实际上我在官方网站和 github 上都没有看到具体的单元测试示例。不过没有关系,既然能在命令行运行,那么直接在 idea 中查看反编译后的 main 方法入口,看下命令行是怎么执行的,就可以写出自己的单元测试了。

package com.wdbyte.decompiler; import java.io.ioexception; import java.util.arraylist; import java.util.hashmap; import java.util.list; import org.benf.cfr.reader.api.cfrdriver; import org.benf.cfr.reader.util.getopt.optionsimpl; /**  * cfr test  *  * @author https://github.com/niumoo  * @date 2021/05/15  */ public class cfrtest {     public static void main(string[] args) throws ioexception {         long time = cfr("decompiler.jar", "./cfr_output_jar");         system.out.println(string.format("decompiler time: %dms", time));         // decompiler time: 11655ms     }     public static long cfr(string source, string targetpath) throws ioexception {         long start = system.currenttimemillis();         // source jar         list files = new arraylist();         files.add(source);         // target dir         hashmap 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

gihub 地址:https://github.com/java-decompiler/jd-corejd-core 官方网址:https://java-decompiler.github.io/jd-core 是一个的独立的 java 库,可以用于 java 的反编译,支持从 java 1 至 java 12 的字节码反编译,包括 lambda 表达式、方式引用、默认方法等。知名的 jd-gui 和 eclipse 无缝集成反编译引擎就是 jd-core。jd-core 提供了一些反编译的核心功能,也提供了单独的 class 反编译方法,但是如果你想在自己的代码中去直接反编译整个 jar 包,还是需要一些改造的,如果是代码中有匿名函数,lambda 等,虽然可以直接反编译,不过也需要额外考虑。

使用 jd-core                              org.jd             jd-core             1.1.3         

为了可以反编译整个 jar 包,使用的代码我做了一些简单改造,以便于最后一部分的对比测试,但是这个示例中没有考虑内部类,lambda 等会编译出多个 class 文件的情况,所以不能直接使用在生产中。

package com.wdbyte.decompiler; 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; import org.apache.commons.io.ioutils; import org.apache.commons.lang3.stringutils; import org.jd.core.v1.classfiletojavasourcedecompiler; import org.jd.core.v1.api.loader.loader; import org.jd.core.v1.api.printer.printer; /**  * @author https://github.com/niumoo  * @date 2021/05/15  */ public class jdcoretest {     public static void main(string[] args) throws exception {         jdcoredecompiler jdcoredecompiler = new jdcoredecompiler();         long time = jdcoredecompiler.decompiler("decompiler.jar","jd_output_jar");         system.out.println(string.format("decompiler time: %dms", time));     } } class jdcoredecompiler{     private classfiletojavasourcedecompiler decompiler = new classfiletojavasourcedecompiler();     // 存放字节码     private hashmap 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();         // 解压         archive(source);         for (string classname : classbytemap.keyset()) {             string path = stringutils.substringbeforelast(classname, "/");             string name = stringutils.substringafterlast(classname, "/");             if (stringutils.contains(name, "$")) {                 name = stringutils.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;     }     private void archive(string path) throws ioexception {         try (zipfile archive = new jarfile(new file(path))) {             enumeration


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3