bcv-vf/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/CFRDecompiler.java

361 lines
14 KiB
Java

package the.bytecode.club.bytecodeviewer.decompilers.impl;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Random;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import me.konloch.kontainer.io.DiskReader;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.api.ExceptionUI;
import the.bytecode.club.bytecodeviewer.decompilers.InternalDecompiler;
import the.bytecode.club.bytecodeviewer.translation.TranslatedStrings;
import the.bytecode.club.bytecodeviewer.util.MiscUtils;
import static the.bytecode.club.bytecodeviewer.Constants.fs;
import static the.bytecode.club.bytecodeviewer.Constants.nl;
import static the.bytecode.club.bytecodeviewer.Constants.tempDirectory;
import static the.bytecode.club.bytecodeviewer.translation.TranslatedStrings.CFR;
import static the.bytecode.club.bytecodeviewer.translation.TranslatedStrings.ERROR;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
* Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
/**
* CFR Java Wrapper
*
* @author Konloch
*/
public class CFRDecompiler extends InternalDecompiler
{
private static final String[] WINDOWS_IS_GREAT = new String[]
{
"CON",
"PRN",
"AUX",
"NUL",
"COM1",
"COM2",
"COM3",
"COM4",
"COM5",
"COM6",
"COM7",
"COM8",
"COM9",
"LPT1",
"LPT2",
"LPT3",
"LPT4",
"LPT5",
"LPT6",
"LPT7",
"LPT8",
"LPT9"
};
public static String windowsFun(String base) {
for (String s : WINDOWS_IS_GREAT) {
if (base.contains(s.toLowerCase())) {
base = base.replace(s.toLowerCase(), "BCV");
}
}
return base;
}
@Override
public String decompileClassNode(ClassNode cn, byte[] b) {
String fileStart = tempDirectory + fs.toLowerCase();
String exception = "";
//final File tempClass = new File(windowsFun(MiscUtils.getUniqueName(fileStart, ".class") + ".class"));
final File tempClass = new File(MiscUtils.getUniqueName(fileStart, ".class") + ".class");
try (FileOutputStream fos = new FileOutputStream(tempClass)) {
fos.write(b);
} catch (final IOException e) {
BytecodeViewer.handleException(e);
}
String fuckery = fuckery(fileStart);
/*if (!BytecodeViewer.fatJar) {
try {
ProcessBuilder pb = new ProcessBuilder(ArrayUtils.addAll(
new String[]{BytecodeViewer.getJavaCommand(), "-jar", Resources.findLibrary("cfr")},
generateMainMethod(tempClass.getAbsolutePath(), fuckery)
));
BytecodeViewer.sm.stopBlocking();
Process p = pb.start();
BytecodeViewer.createdProcesses.add(p);
p.waitFor();
} catch (Exception e) {
BytecodeViewer.handleException(e);
} finally {
BytecodeViewer.sm.setBlocking();
}
} else {
org.benf.cfr.reader.Main.main(generateMainMethod(tempClass.getAbsolutePath(), fuckery));
}*/
try {
org.benf.cfr.reader.Main.main(generateMainMethod(tempClass.getAbsolutePath(), fuckery));
} catch (StackOverflowError | Exception e) {
StringWriter exceptionWriter = new StringWriter();
e.printStackTrace(new PrintWriter(exceptionWriter));
e.printStackTrace();
exception = exceptionWriter.toString();
}
tempClass.delete();
File file = new File(fuckery);
if (file.exists())
return findFile(MiscUtils.listFiles(file));
return CFR + " " + ERROR + "! " + ExceptionUI.SEND_STACKTRACE_TO +
nl + nl + TranslatedStrings.SUGGESTED_FIX_DECOMPILER_ERROR +
nl + nl + exception;
}
Random r = new Random();
File f;
public String fuckery(String start) {
while (true) {
f = new File(start + r.nextInt(Integer.MAX_VALUE));
if (!f.exists())
return f.toString();
}
}
public String findFile(File[] fA) {
for (File f : fA) {
if (f.isDirectory())
return findFile(MiscUtils.listFiles(f));
else {
String s;
try {
s = DiskReader.loadAsString(f.getAbsolutePath());
} catch (Exception e) {
StringWriter exceptionWriter = new StringWriter();
e.printStackTrace(new PrintWriter(exceptionWriter));
e.printStackTrace();
return CFR + " " + ERROR + "! " + ExceptionUI.SEND_STACKTRACE_TO +
nl + nl + TranslatedStrings.SUGGESTED_FIX_DECOMPILER_ERROR +
nl + nl + exceptionWriter;
}
return s;
}
}
return "CFR error!" +
nl + nl + TranslatedStrings.SUGGESTED_FIX_DECOMPILER_ERROR;
}
public String[] generateMainMethod(String filePath, String outputPath) {
return new String[]{
filePath,
"--outputdir",
outputPath,
"--decodeenumswitch",
String.valueOf(BytecodeViewer.viewer.decodeEnumSwitch
.isSelected()),
"--sugarenums",
String.valueOf(BytecodeViewer.viewer.sugarEnums.isSelected()),
"--decodestringswitch",
String.valueOf(BytecodeViewer.viewer.decodeStringSwitch
.isSelected()),
"--arrayiter",
String.valueOf(BytecodeViewer.viewer.arrayiter.isSelected()),
"--collectioniter",
String.valueOf(BytecodeViewer.viewer.collectioniter
.isSelected()),
"--innerclasses",
String.valueOf(BytecodeViewer.viewer.innerClasses.isSelected()),
"--removeboilerplate",
String.valueOf(BytecodeViewer.viewer.removeBoilerPlate
.isSelected()),
"--removeinnerclasssynthetics",
String.valueOf(BytecodeViewer.viewer.removeInnerClassSynthetics
.isSelected()),
"--decodelambdas",
String.valueOf(BytecodeViewer.viewer.decodeLambdas.isSelected()),
"--hidebridgemethods",
String.valueOf(BytecodeViewer.viewer.hideBridgeMethods
.isSelected()),
"--liftconstructorinit",
String.valueOf(BytecodeViewer.viewer.liftConstructorInit
.isSelected()),
"--removedeadmethods",
String.valueOf(BytecodeViewer.viewer.removeDeadMethods
.isSelected()),
"--removebadgenerics",
String.valueOf(BytecodeViewer.viewer.removeBadGenerics
.isSelected()),
"--sugarasserts",
String.valueOf(BytecodeViewer.viewer.sugarAsserts.isSelected()),
"--sugarboxing",
String.valueOf(BytecodeViewer.viewer.sugarBoxing.isSelected()),
"--showversion",
String.valueOf(BytecodeViewer.viewer.showVersion.isSelected()),
"--decodefinally",
String.valueOf(BytecodeViewer.viewer.decodeFinally.isSelected()),
"--tidymonitors",
String.valueOf(BytecodeViewer.viewer.tidyMonitors.isSelected()),
"--lenient",
String.valueOf(BytecodeViewer.viewer.lenient.isSelected()),
"--dumpclasspath",
String.valueOf(BytecodeViewer.viewer.dumpClassPath.isSelected()),
"--comments",
String.valueOf(BytecodeViewer.viewer.comments.isSelected()),
"--forcetopsort",
String.valueOf(BytecodeViewer.viewer.forceTopSort.isSelected()),
"--forcetopsortaggress",
String.valueOf(BytecodeViewer.viewer.forceTopSortAggress
.isSelected()),
"--stringbuffer",
String.valueOf(BytecodeViewer.viewer.stringBuffer.isSelected()),
"--stringbuilder",
String.valueOf(BytecodeViewer.viewer.stringBuilder.isSelected()),
"--silent",
String.valueOf(BytecodeViewer.viewer.silent.isSelected()),
"--recover",
String.valueOf(BytecodeViewer.viewer.recover.isSelected()),
"--eclipse",
String.valueOf(BytecodeViewer.viewer.eclipse.isSelected()),
"--override",
String.valueOf(BytecodeViewer.viewer.override.isSelected()),
"--showinferrable",
String.valueOf(BytecodeViewer.viewer.showInferrable
.isSelected()),
"--aexagg",
String.valueOf(BytecodeViewer.viewer.aexagg.isSelected()),
"--forcecondpropagate",
String.valueOf(BytecodeViewer.viewer.forceCondPropagate
.isSelected()),
"--hideutf",
String.valueOf(BytecodeViewer.viewer.hideUTF.isSelected()),
"--hidelongstrings",
String.valueOf(BytecodeViewer.viewer.hideLongStrings
.isSelected()),
"--commentmonitors",
String.valueOf(BytecodeViewer.viewer.commentMonitor
.isSelected()),
"--allowcorrecting",
String.valueOf(BytecodeViewer.viewer.allowCorrecting
.isSelected()),
"--labelledblocks",
String.valueOf(BytecodeViewer.viewer.labelledBlocks
.isSelected()),
"--j14classobj",
String.valueOf(BytecodeViewer.viewer.j14ClassOBJ.isSelected()),
"--hidelangimports",
String.valueOf(BytecodeViewer.viewer.hideLangImports
.isSelected()),
"--recovertypeclash",
String.valueOf(BytecodeViewer.viewer.recoveryTypeClash
.isSelected()),
"--recovertypehints",
String.valueOf(BytecodeViewer.viewer.recoveryTypehInts
.isSelected()),
"--forcereturningifs",
String.valueOf(BytecodeViewer.viewer.forceTurningIFs
.isSelected()),
"--forloopaggcapture",
String.valueOf(BytecodeViewer.viewer.forLoopAGGCapture
.isSelected()),};
}
@Override
public void decompileToZip(String sourceJar, String zipName) {
File tempZip = new File(sourceJar);
String fileStart = tempDirectory + fs;
String fuckery = fuckery(fileStart);
org.benf.cfr.reader.Main.main(generateMainMethod(tempZip.getAbsolutePath(), fuckery));
File fuck = new File(fuckery);
try {
zip(fuck, new File(zipName));
} catch (IOException e) {
BytecodeViewer.handleException(e);
}
fuck.delete();
}
public void zip(File directory, File zipFile) throws IOException {
java.net.URI base = directory.toURI();
Deque<File> queue = new LinkedList<>();
queue.push(directory);
try (OutputStream out = new FileOutputStream(zipFile);
ZipOutputStream zout = new ZipOutputStream(out)) {
while (!queue.isEmpty()) {
directory = queue.pop();
for (File kid : MiscUtils.listFiles(directory)) {
String name = base.relativize(kid.toURI()).getPath();
if (kid.isDirectory()) {
queue.push(kid);
name = name.endsWith("/") ? name : name + "/";
zout.putNextEntry(new ZipEntry(name));
} else {
zout.putNextEntry(new ZipEntry(name));
copy(kid, zout);
zout.closeEntry();
}
}
}
}
}
private static void copy(InputStream in, OutputStream out)
throws IOException {
byte[] buffer = new byte[1024];
while (true) {
int readCount = in.read(buffer);
if (readCount < 0) {
break;
}
out.write(buffer, 0, readCount);
}
}
private static void copy(File file, OutputStream out) throws IOException {
try (InputStream in = new FileInputStream(file)) {
copy(in, out);
}
}
}