java反编译工具有哪些(java反编译class命令) |
您所在的位置:网站首页 › paths怎么读音 › java反编译工具有哪些(java反编译class命令) |
前言 java 反编译,一听可能觉得高深莫测,其实反编译并不是什么特别高级的操作,java 对于 class 字节码文件的生成有着严格的要求,如果你非常熟悉 java 虚拟机规范,了解 class 字节码文件中一些字节的作用,那么理解反编译的原理并不是什么问题。甚至像下面这样的 class 文件你都能看懂一二。 一般在逆向研究和代码分析中,反编译用到的比较多。不过在日常开发中,有时候只是简单的看一下所用依赖类的反编译,也是十分重要的。 恰好最近工作中也需要用到 java 反编译,所以这篇文章介绍目前常见的的几种 java 反编译工具的使用,在文章的最后也会通过编译速度、语法支持以及代码可读性三个维度,对它们进行测试,分析几款工具的优缺点。 procyongithub 链接: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 cfrgithub 地址: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 的包路径写入到指定文件夹中。 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-coregihub 地址: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 |