v2.9.20
This commit is contained in:
parent
9f3302f7a9
commit
f76b31d8e7
12 changed files with 398 additions and 55 deletions
|
@ -16,12 +16,8 @@ import java.net.URL;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.jar.JarEntry;
|
|
||||||
import java.util.jar.JarFile;
|
|
||||||
import java.util.jar.JarOutputStream;
|
|
||||||
|
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JFileChooser;
|
import javax.swing.JFileChooser;
|
||||||
|
@ -118,7 +114,7 @@ import the.bytecode.club.bytecodeviewer.util.*;
|
||||||
public class BytecodeViewer
|
public class BytecodeViewer
|
||||||
{
|
{
|
||||||
/*per version*/
|
/*per version*/
|
||||||
public static final String VERSION = "2.9.19";
|
public static final String VERSION = "2.9.20";
|
||||||
public static String krakatauVersion = "12";
|
public static String krakatauVersion = "12";
|
||||||
public static String enjarifyVersion = "4";
|
public static String enjarifyVersion = "4";
|
||||||
public static final boolean BLOCK_TAB_MENU = true;
|
public static final boolean BLOCK_TAB_MENU = true;
|
||||||
|
@ -145,12 +141,13 @@ public class BytecodeViewer
|
||||||
public static boolean currentlyDumping = false;
|
public static boolean currentlyDumping = false;
|
||||||
public static boolean needsReDump = true;
|
public static boolean needsReDump = true;
|
||||||
public static boolean warnForEditing = false;
|
public static boolean warnForEditing = false;
|
||||||
public static ArrayList<FileContainer> files = new ArrayList<FileContainer>(); //all of BCV's loaded files/classes/etc
|
public static List<FileContainer> files = new ArrayList<FileContainer>(); //all of BCV's loaded files/classes/etc
|
||||||
private static int maxRecentFiles = 25;
|
private static int maxRecentFiles = 25;
|
||||||
public static String fs = System.getProperty("file.separator");
|
public static String fs = System.getProperty("file.separator");
|
||||||
public static String nl = System.getProperty("line.separator");
|
public static String nl = System.getProperty("line.separator");
|
||||||
private static File BCVDir = new File(System.getProperty("user.home") + fs + ".Bytecode-Viewer");
|
private static File BCVDir = new File(System.getProperty("user.home") + fs + ".Bytecode-Viewer");
|
||||||
public static File RJ_JAR = new File(System.getProperty("java.home") + fs + "lib" + fs + "rt.jar");
|
public static File RT_JAR = new File(System.getProperty("java.home") + fs + "lib" + fs + "rt.jar");
|
||||||
|
public static File RT_JAR_DUMPED = new File(getBCVDirectory() + fs + "rt.jar");
|
||||||
private static String filesName = getBCVDirectory() + fs + "recentfiles.json";
|
private static String filesName = getBCVDirectory() + fs + "recentfiles.json";
|
||||||
private static String pluginsName = getBCVDirectory() + fs + "recentplugins.json";
|
private static String pluginsName = getBCVDirectory() + fs + "recentplugins.json";
|
||||||
public static String settingsName = getBCVDirectory() + fs + "settings.bcv";
|
public static String settingsName = getBCVDirectory() + fs + "settings.bcv";
|
||||||
|
@ -161,15 +158,15 @@ public class BytecodeViewer
|
||||||
public static boolean runningObfuscation = false;
|
public static boolean runningObfuscation = false;
|
||||||
private static long start = System.currentTimeMillis();
|
private static long start = System.currentTimeMillis();
|
||||||
public static String lastDirectory = ".";
|
public static String lastDirectory = ".";
|
||||||
public static ArrayList<Process> createdProcesses = new ArrayList<Process>();
|
public static List<Process> createdProcesses = new ArrayList<Process>();
|
||||||
public static Refactorer refactorer = new Refactorer();
|
public static Refactorer refactorer = new Refactorer();
|
||||||
public static boolean pingback = false;
|
public static boolean pingback = false;
|
||||||
public static boolean deleteForeignLibraries = true;
|
public static boolean deleteForeignLibraries = true;
|
||||||
public static boolean canExit = false;
|
public static boolean canExit = false;
|
||||||
public static Gson gson;
|
public static Gson gson;
|
||||||
|
|
||||||
private static ArrayList<String> recentPlugins;
|
private static List<String> recentPlugins;
|
||||||
private static ArrayList<String> recentFiles;
|
private static List<String> recentFiles;
|
||||||
|
|
||||||
static
|
static
|
||||||
{
|
{
|
||||||
|
@ -669,6 +666,10 @@ public class BytecodeViewer
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<FileContainer> getFiles() {
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
public static ClassNode getClassNode(FileContainer container, String name) {
|
public static ClassNode getClassNode(FileContainer container, String name) {
|
||||||
for (ClassNode c : container.classes)
|
for (ClassNode c : container.classes)
|
||||||
if (c.name.equals(name))
|
if (c.name.equals(name))
|
||||||
|
@ -943,7 +944,7 @@ public class BytecodeViewer
|
||||||
|
|
||||||
if (viewer.decodeAPKResources.isSelected()) {
|
if (viewer.decodeAPKResources.isSelected()) {
|
||||||
File decodedResources = new File(tempDirectory + fs + MiscUtils.randomString(32) + ".apk");
|
File decodedResources = new File(tempDirectory + fs + MiscUtils.randomString(32) + ".apk");
|
||||||
APKTool.decodeResources(tempCopy, decodedResources);
|
APKTool.decodeResources(tempCopy, decodedResources, container);
|
||||||
container.files = JarUtils.loadResources(decodedResources);
|
container.files = JarUtils.loadResources(decodedResources);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1080,7 +1081,7 @@ public class BytecodeViewer
|
||||||
the.bytecode.club.bytecodeviewer.api.BytecodeViewer.getClassNodeLoader().clear();
|
the.bytecode.club.bytecodeviewer.api.BytecodeViewer.getClassNodeLoader().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ArrayList<String> killList = new ArrayList<String>();
|
private static List<String> killList = new ArrayList<String>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the recent file
|
* Add the recent file
|
||||||
|
@ -1109,7 +1110,7 @@ public class BytecodeViewer
|
||||||
resetRecentFilesMenu();
|
resetRecentFilesMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ArrayList<String> killList2 = new ArrayList<String>();
|
private static List<String> killList2 = new ArrayList<String>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add to the recent plugin list
|
* Add to the recent plugin list
|
||||||
|
@ -1187,7 +1188,7 @@ public class BytecodeViewer
|
||||||
tempF.mkdir();
|
tempF.mkdir();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ArrayList<String> createdRandomizedNames = new ArrayList<String>();
|
public static List<String> createdRandomizedNames = new ArrayList<String>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures it will only return a uniquely generated names, contains a dupe checker to be sure
|
* Ensures it will only return a uniquely generated names, contains a dupe checker to be sure
|
||||||
|
@ -1254,7 +1255,7 @@ public class BytecodeViewer
|
||||||
* @param a array
|
* @param a array
|
||||||
* @return string with newline per array object
|
* @return string with newline per array object
|
||||||
*/
|
*/
|
||||||
private static String quickConvert(ArrayList<String> a) {
|
private static String quickConvert(List<String> a) {
|
||||||
return gson.toJson(a);
|
return gson.toJson(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1471,11 +1472,30 @@ public class BytecodeViewer
|
||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void rtCheck()
|
public synchronized static void rtCheck()
|
||||||
{
|
{
|
||||||
if(rt.equals("") && RJ_JAR.exists())
|
if(rt.equals(""))
|
||||||
{
|
{
|
||||||
rt = RJ_JAR.getAbsolutePath();
|
if(RT_JAR.exists())
|
||||||
|
{
|
||||||
|
rt = RT_JAR.getAbsolutePath();
|
||||||
|
}
|
||||||
|
else if(RT_JAR_DUMPED.exists())
|
||||||
|
{
|
||||||
|
rt = RT_JAR_DUMPED.getAbsolutePath();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JRTExtractor.extractRT(RT_JAR_DUMPED.getAbsolutePath());
|
||||||
|
rt = RT_JAR_DUMPED.getAbsolutePath();
|
||||||
|
}
|
||||||
|
catch (Throwable t)
|
||||||
|
{
|
||||||
|
t.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
|
@ -37,7 +38,7 @@ import org.imgscalr.Scalr;
|
||||||
|
|
||||||
public class Resources {
|
public class Resources {
|
||||||
|
|
||||||
public static ArrayList<BufferedImage> iconList;
|
public static List<BufferedImage> iconList;
|
||||||
public static BufferedImage icon;
|
public static BufferedImage icon;
|
||||||
public static ImageIcon nextIcon;
|
public static ImageIcon nextIcon;
|
||||||
public static ImageIcon prevIcon;
|
public static ImageIcon prevIcon;
|
||||||
|
|
|
@ -44,6 +44,7 @@ public class SecurityMan extends SecurityManager {
|
||||||
executedClass.equals("the.bytecode.club.bytecodeviewer.decompilers.JDGUIDecompiler") ||
|
executedClass.equals("the.bytecode.club.bytecodeviewer.decompilers.JDGUIDecompiler") ||
|
||||||
executedClass.equals("the.bytecode.club.bytecodeviewer.compilers.KrakatauAssembler") ||
|
executedClass.equals("the.bytecode.club.bytecodeviewer.compilers.KrakatauAssembler") ||
|
||||||
executedClass.equals("the.bytecode.club.bytecodeviewer.util.Enjarify") ||
|
executedClass.equals("the.bytecode.club.bytecodeviewer.util.Enjarify") ||
|
||||||
|
executedClass.equals("the.bytecode.club.bytecodeviewer.util.APKTool") ||
|
||||||
executedClass.equals("the.bytecode.club.bytecodeviewer.BytecodeViewer") ||
|
executedClass.equals("the.bytecode.club.bytecodeviewer.BytecodeViewer") ||
|
||||||
executedClass.equals("the.bytecode.club.bytecodeviewer.compilers.JavaCompiler")) {
|
executedClass.equals("the.bytecode.club.bytecodeviewer.compilers.JavaCompiler")) {
|
||||||
blocking = false;
|
blocking = false;
|
||||||
|
@ -60,7 +61,8 @@ public class SecurityMan extends SecurityManager {
|
||||||
"attrib",
|
"attrib",
|
||||||
"python",
|
"python",
|
||||||
"pypy",
|
"pypy",
|
||||||
"java"
|
"java",
|
||||||
|
"brut_util",
|
||||||
};
|
};
|
||||||
boolean allow = false;
|
boolean allow = false;
|
||||||
|
|
||||||
|
@ -71,7 +73,7 @@ public class SecurityMan extends SecurityManager {
|
||||||
|
|
||||||
if (allow && !blocking) {
|
if (allow && !blocking) {
|
||||||
System.out.println("Allowing exec:" + cmd);
|
System.out.println("Allowing exec:" + cmd);
|
||||||
} else throw new SecurityException("BCV is awesome, blocking " + cmd);
|
} else throw new SecurityException("BCV is awesome, blocking("+blocking+") exec " + cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -44,7 +44,7 @@ public class SmaliAssembler extends Compiler {
|
||||||
tempSmaliFolder.mkdir();
|
tempSmaliFolder.mkdir();
|
||||||
|
|
||||||
File tempSmali = new File(tempSmaliFolder.getAbsolutePath() + BytecodeViewer.fs + fileNumber + ".smali");
|
File tempSmali = new File(tempSmaliFolder.getAbsolutePath() + BytecodeViewer.fs + fileNumber + ".smali");
|
||||||
File tempDex = new File(fileStart + fileNumber + ".dex");
|
File tempDex = new File("./out.dex");
|
||||||
File tempJar = new File(fileStart + fileNumber + ".jar");
|
File tempJar = new File(fileStart + fileNumber + ".jar");
|
||||||
File tempJarFolder = new File(fileStart + fileNumber + "-jar" + BytecodeViewer.fs);
|
File tempJarFolder = new File(fileStart + fileNumber + "-jar" + BytecodeViewer.fs);
|
||||||
|
|
||||||
|
@ -68,6 +68,7 @@ public class SmaliAssembler extends Compiler {
|
||||||
Enjarify.apk2Jar(tempDex, tempJar);
|
Enjarify.apk2Jar(tempDex, tempJar);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
System.out.println("Unzipping to " + tempJarFolder.getAbsolutePath());
|
||||||
ZipUtils.unzipFilesToPath(tempJar.getAbsolutePath(), tempJarFolder.getAbsolutePath());
|
ZipUtils.unzipFilesToPath(tempJar.getAbsolutePath(), tempJarFolder.getAbsolutePath());
|
||||||
|
|
||||||
File outputClass = null;
|
File outputClass = null;
|
||||||
|
|
|
@ -41,8 +41,7 @@ public class SmaliDisassembler extends Decompiler {
|
||||||
|
|
||||||
public String decompileClassNode(FileContainer container, ClassNode cn, byte[] b) {
|
public String decompileClassNode(FileContainer container, ClassNode cn, byte[] b) {
|
||||||
String exception = "";
|
String exception = "";
|
||||||
String fileStart = BytecodeViewer.tempDirectory + BytecodeViewer.fs
|
String fileStart = BytecodeViewer.tempDirectory + BytecodeViewer.fs + "temp";
|
||||||
+ "temp";
|
|
||||||
|
|
||||||
String start = MiscUtils.getUniqueName(fileStart, ".class");
|
String start = MiscUtils.getUniqueName(fileStart, ".class");
|
||||||
|
|
||||||
|
@ -62,9 +61,7 @@ public class SmaliDisassembler extends Decompiler {
|
||||||
|
|
||||||
//ZipUtils.zipFile(tempClass, tempZip);
|
//ZipUtils.zipFile(tempClass, tempZip);
|
||||||
|
|
||||||
Dex2Jar.saveAsDex(container.file, tempDex, false);
|
Dex2Jar.saveAsDex(tempClass, tempDex, true);
|
||||||
|
|
||||||
System.out.println("FOR SHOW: " + tempDex.getName().replaceFirst("\\.dex", "-out")); //tempSmali.getAbsolutePath()
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -94,9 +91,6 @@ public class SmaliDisassembler extends Decompiler {
|
||||||
exception += "Bytecode Viewer Version: " + BytecodeViewer.VERSION + BytecodeViewer.nl + BytecodeViewer.nl + sw.toString();
|
exception += "Bytecode Viewer Version: " + BytecodeViewer.VERSION + BytecodeViewer.nl + BytecodeViewer.nl + sw.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("FOR SHOW1: " + rename.getAbsolutePath());
|
|
||||||
System.out.println("FOR SHOW2: " + tempSmali.getAbsolutePath());
|
|
||||||
|
|
||||||
File outputSmali = null;
|
File outputSmali = null;
|
||||||
|
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
|
|
|
@ -9,6 +9,7 @@ import java.awt.KeyboardFocusManager;
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.ButtonGroup;
|
import javax.swing.ButtonGroup;
|
||||||
|
@ -261,7 +262,8 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final JMenuItem mntmSaveAsApk = new JMenuItem("Save As DEX..");
|
public final JMenuItem mntmSaveAsDEX = new JMenuItem("Save As DEX..");
|
||||||
|
public final JMenuItem mntmSaveAsAPK = new JMenuItem("Save As APK..");
|
||||||
public final JMenuItem mntmCodeSequenceDiagram = new JMenuItem("Code Sequence Diagram");
|
public final JMenuItem mntmCodeSequenceDiagram = new JMenuItem("Code Sequence Diagram");
|
||||||
public final JSeparator separator_7 = new JSeparator();
|
public final JSeparator separator_7 = new JSeparator();
|
||||||
public final JSeparator separator_8 = new JSeparator();
|
public final JSeparator separator_8 = new JSeparator();
|
||||||
|
@ -842,7 +844,7 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier
|
||||||
mnNewMenu.add(separator_18);
|
mnNewMenu.add(separator_18);
|
||||||
|
|
||||||
mnNewMenu.add(mntmNewMenuItem_3);
|
mnNewMenu.add(mntmNewMenuItem_3);
|
||||||
mntmSaveAsApk.addActionListener(new ActionListener()
|
mntmSaveAsDEX.addActionListener(new ActionListener()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent arg0)
|
public void actionPerformed(ActionEvent arg0)
|
||||||
|
@ -940,8 +942,151 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier
|
||||||
t.start();
|
t.start();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
mntmSaveAsAPK.addActionListener(new ActionListener()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent arg0)
|
||||||
|
{
|
||||||
|
if (BytecodeViewer.getLoadedClasses().isEmpty())
|
||||||
|
{
|
||||||
|
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mnNewMenu.add(mntmSaveAsApk);
|
//if theres only one file in the container don't bother asking
|
||||||
|
List<FileContainer> containers = BytecodeViewer.getFiles();
|
||||||
|
List<FileContainer> validContainers = new ArrayList<>();
|
||||||
|
List<String> validContainersNames = new ArrayList<>();
|
||||||
|
FileContainer container = null;
|
||||||
|
|
||||||
|
for(FileContainer fileContainer : containers)
|
||||||
|
{
|
||||||
|
if(fileContainer.APKToolContents != null && fileContainer.APKToolContents.exists())
|
||||||
|
{
|
||||||
|
validContainersNames.add(fileContainer.name);
|
||||||
|
validContainers.add(fileContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!validContainers.isEmpty())
|
||||||
|
{
|
||||||
|
container = validContainers.get(0);
|
||||||
|
|
||||||
|
if(validContainers.size() >= 2)
|
||||||
|
{
|
||||||
|
JOptionPane pane = new JOptionPane("Which file would you like to export as an APK?");
|
||||||
|
Object[] options = validContainersNames.toArray(new String[0]);
|
||||||
|
|
||||||
|
pane.setOptions(options);
|
||||||
|
JDialog dialog = pane.createDialog(BytecodeViewer.viewer, "Bytecode Viewer - Select APK");
|
||||||
|
dialog.setVisible(true);
|
||||||
|
Object obj = pane.getValue();
|
||||||
|
int result = -1;
|
||||||
|
for (int k = 0; k < options.length; k++)
|
||||||
|
if (options[k].equals(obj))
|
||||||
|
result = k;
|
||||||
|
|
||||||
|
container = containers.get(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BytecodeViewer.showMessage("You can only export as APK from a valid APK file. Make sure Settings>Decode Resources is ticked on.\n\nTip: Try exporting as DEX, it doesn't rely on decoded APK resources");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final FileContainer finalContainer = container;
|
||||||
|
|
||||||
|
Thread t = new Thread()
|
||||||
|
{
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
if (compileOnSave.isSelected() && !BytecodeViewer.compile(false))
|
||||||
|
return;
|
||||||
|
JFileChooser fc = new JFileChooser();
|
||||||
|
fc.setFileFilter(new FileFilter()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public boolean accept(File f)
|
||||||
|
{
|
||||||
|
return f.isDirectory() || MiscUtils.extension(f.getAbsolutePath()).equals("apk");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return "Android APK";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fc.setFileHidingEnabled(false);
|
||||||
|
fc.setAcceptAllFileFilterUsed(false);
|
||||||
|
int returnVal = fc.showSaveDialog(MainViewerGUI.this);
|
||||||
|
if (returnVal == JFileChooser.APPROVE_OPTION)
|
||||||
|
{
|
||||||
|
final File file = fc.getSelectedFile();
|
||||||
|
String output = file.getAbsolutePath();
|
||||||
|
if (!output.endsWith(".apk"))
|
||||||
|
output = output + ".apk";
|
||||||
|
|
||||||
|
final File file2 = new File(output);
|
||||||
|
|
||||||
|
if (file2.exists())
|
||||||
|
{
|
||||||
|
JOptionPane pane = new JOptionPane(
|
||||||
|
"Are you sure you wish to overwrite this existing file?");
|
||||||
|
Object[] options = new String[]{"Yes", "No"};
|
||||||
|
pane.setOptions(options);
|
||||||
|
JDialog dialog = pane.createDialog(BytecodeViewer.viewer,
|
||||||
|
"Bytecode Viewer - Overwrite File");
|
||||||
|
dialog.setVisible(true);
|
||||||
|
Object obj = pane.getValue();
|
||||||
|
int result = -1;
|
||||||
|
for (int k = 0; k < options.length; k++)
|
||||||
|
if (options[k].equals(obj))
|
||||||
|
result = k;
|
||||||
|
|
||||||
|
if (result == 0)
|
||||||
|
{
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread t = new Thread()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
BytecodeViewer.viewer.setIcon(true);
|
||||||
|
final String input = BytecodeViewer.tempDirectory + BytecodeViewer.fs + BytecodeViewer.getRandomizedName() + ".jar";
|
||||||
|
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), input);
|
||||||
|
|
||||||
|
Thread t = new Thread()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
APKTool.buildAPK(new File(input), file2, finalContainer);
|
||||||
|
|
||||||
|
BytecodeViewer.viewer.setIcon(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
t.start();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
t.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
t.start();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//mnNewMenu.add(mntmSaveAsAPK);
|
||||||
|
mnNewMenu.add(mntmSaveAsDEX);
|
||||||
mnNewMenu.add(mntmSave);
|
mnNewMenu.add(mntmSave);
|
||||||
mntmNewMenuItem.addActionListener(new ActionListener()
|
mntmNewMenuItem.addActionListener(new ActionListener()
|
||||||
{
|
{
|
||||||
|
|
|
@ -231,9 +231,7 @@ public class EZInjection extends Plugin {
|
||||||
MethodInsnNode mn = (MethodInsnNode) m.instructions
|
MethodInsnNode mn = (MethodInsnNode) m.instructions
|
||||||
.get(1);
|
.get(1);
|
||||||
if (mn.owner
|
if (mn.owner
|
||||||
.equals("the/bytecode/club/bytecodeviewer/plugins/EZInjection")) // already
|
.equals(EZInjection.class.getName().replace(".", "/")))//"the/bytecode/club/bytecodeviewer/plugins/EZInjection")) // already been injected
|
||||||
// been
|
|
||||||
// injected
|
|
||||||
inject = false;
|
inject = false;
|
||||||
}
|
}
|
||||||
if (inject) {
|
if (inject) {
|
||||||
|
@ -241,7 +239,7 @@ public class EZInjection extends Plugin {
|
||||||
m.instructions
|
m.instructions
|
||||||
.insert(new MethodInsnNode(
|
.insert(new MethodInsnNode(
|
||||||
Opcodes.INVOKESTATIC,
|
Opcodes.INVOKESTATIC,
|
||||||
"the/bytecode/club/bytecodeviewer/plugins/EZInjection",
|
EZInjection.class.getName().replace(".", "/"),//"the/bytecode/club/bytecodeviewer/plugins/EZInjection",
|
||||||
"hook", "(Ljava/lang/String;)V"));
|
"hook", "(Ljava/lang/String;)V"));
|
||||||
m.instructions.insert(new LdcInsnNode(classNode.name
|
m.instructions.insert(new LdcInsnNode(classNode.name
|
||||||
+ "." + m.name + m.desc));
|
+ "." + m.name + m.desc));
|
||||||
|
|
|
@ -1,9 +1,17 @@
|
||||||
package the.bytecode.club.bytecodeviewer.util;
|
package the.bytecode.club.bytecodeviewer.util;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.*;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
|
import me.konloch.kontainer.io.DiskWriter;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
||||||
|
import the.bytecode.club.bytecodeviewer.SecurityMan;
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
|
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
|
||||||
|
@ -23,28 +31,60 @@ import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Konloch
|
||||||
|
*/
|
||||||
public class APKTool {
|
public class APKTool {
|
||||||
|
|
||||||
public static synchronized void decodeResources(File input, File output) {
|
public static synchronized void decodeResources(File input, File output, FileContainer container) {
|
||||||
try {
|
try {
|
||||||
File dir = new File(BytecodeViewer.tempDirectory + BytecodeViewer.fs + "Decoded Resources");
|
File dir = new File(BytecodeViewer.tempDirectory + BytecodeViewer.fs + MiscUtils.randomString(32)+BytecodeViewer.fs+"Decoded Resources");
|
||||||
FileUtils.deleteDirectory(dir);
|
dir.mkdirs();
|
||||||
File temp = new File(BytecodeViewer.tempDirectory + BytecodeViewer.fs + MiscUtils.randomString(12));
|
|
||||||
temp.mkdirs();
|
File tempAPKPath = new File(BytecodeViewer.tempDirectory + BytecodeViewer.fs + MiscUtils.randomString(12));
|
||||||
brut.apktool.Main.main(new String[]{"--frame-path", temp.getAbsolutePath(), "-s", "-f", "-o", dir.getAbsolutePath(), "decode", input.getAbsolutePath()});
|
tempAPKPath.mkdirs();
|
||||||
File original = new File(dir.getAbsolutePath() + BytecodeViewer.fs + "original");
|
brut.apktool.Main.main(new String[]{"r", "--frame-path", tempAPKPath.getAbsolutePath(), "d", input.getAbsolutePath(), "-o", dir.getAbsolutePath(), "-f"});
|
||||||
FileUtils.deleteDirectory(original);
|
|
||||||
File classes = new File(dir.getAbsolutePath() + BytecodeViewer.fs + "classes.dex");
|
|
||||||
classes.delete();
|
|
||||||
File apktool = new File(dir.getAbsolutePath() + BytecodeViewer.fs + "apktool.yml");
|
|
||||||
apktool.delete();
|
|
||||||
File zip = new File(BytecodeViewer.tempDirectory + BytecodeViewer.fs + MiscUtils.randomString(12) + ".zip");
|
File zip = new File(BytecodeViewer.tempDirectory + BytecodeViewer.fs + MiscUtils.randomString(12) + ".zip");
|
||||||
ZipUtils.zipFolder(dir.getAbsolutePath(), zip.getAbsolutePath(), null);
|
ZipUtils.zipFolderAPKTool(dir.getAbsolutePath(), zip.getAbsolutePath());
|
||||||
|
|
||||||
if (zip.exists())
|
if (zip.exists())
|
||||||
zip.renameTo(output);
|
zip.renameTo(output);
|
||||||
FileUtils.deleteDirectory(dir);
|
|
||||||
|
container.APKToolContents = dir;
|
||||||
|
tempAPKPath.delete();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
|
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static synchronized void buildAPK(File input, File output, FileContainer container)
|
||||||
|
{
|
||||||
|
String name = container.file.getName().toLowerCase();
|
||||||
|
String temp = BytecodeViewer.tempDirectory + BytecodeViewer.fs;
|
||||||
|
File tempDir = new File(temp+BytecodeViewer.fs+BytecodeViewer.getRandomizedName()+BytecodeViewer.fs);
|
||||||
|
tempDir.mkdirs();
|
||||||
|
|
||||||
|
|
||||||
|
File tempAPKPath = new File(BytecodeViewer.tempDirectory + BytecodeViewer.fs + MiscUtils.randomString(12));
|
||||||
|
tempAPKPath.mkdirs();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File smaliFolder = new File(container.APKToolContents.getAbsolutePath()+BytecodeViewer.fs+"smali");
|
||||||
|
FileUtils.deleteDirectory(smaliFolder);
|
||||||
|
|
||||||
|
|
||||||
|
//save entire jar as smali files
|
||||||
|
System.out.println("Building!");
|
||||||
|
BytecodeViewer.sm.stopBlocking();
|
||||||
|
brut.apktool.Main.main(new String[]{"b", container.APKToolContents.getAbsolutePath(), "--frame-path", tempAPKPath.getAbsolutePath(), "-o", output.getAbsolutePath()});
|
||||||
|
BytecodeViewer.sm.setBlocking();
|
||||||
|
tempAPKPath.delete();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,21 @@
|
||||||
package the.bytecode.club.bytecodeviewer.util;
|
package the.bytecode.club.bytecodeviewer.util;
|
||||||
|
|
||||||
|
import me.konloch.kontainer.io.DiskReader;
|
||||||
|
import me.konloch.kontainer.io.DiskWriter;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.objectweb.asm.ClassWriter;
|
||||||
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.*;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
import java.util.jar.JarInputStream;
|
||||||
|
import java.util.jar.JarOutputStream;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
|
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
|
||||||
|
@ -81,6 +94,10 @@ public class Dex2Jar {
|
||||||
{
|
{
|
||||||
currentDexLocation = new File(currentDexLocation.getAbsolutePath().replaceFirst("\\.zip", "-jar2dex.dex"));
|
currentDexLocation = new File(currentDexLocation.getAbsolutePath().replaceFirst("\\.zip", "-jar2dex.dex"));
|
||||||
}
|
}
|
||||||
|
else if(currentDexLocation.getAbsolutePath().toLowerCase().endsWith(".class"))
|
||||||
|
{
|
||||||
|
currentDexLocation = new File(currentDexLocation.getAbsolutePath().replaceFirst("\\.class", "-jar2dex.dex"));
|
||||||
|
}
|
||||||
|
|
||||||
currentDexLocation.renameTo(output);
|
currentDexLocation.renameTo(output);
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ public class FileContainer {
|
||||||
|
|
||||||
public File file;
|
public File file;
|
||||||
public String name;
|
public String name;
|
||||||
|
public File APKToolContents = null;
|
||||||
|
|
||||||
public HashMap<String, byte[]> files = new HashMap<>();
|
public HashMap<String, byte[]> files = new HashMap<>();
|
||||||
public ArrayList<ClassNode> classes = new ArrayList<>();
|
public ArrayList<ClassNode> classes = new ArrayList<>();
|
||||||
|
|
59
src/the/bytecode/club/bytecodeviewer/util/JRTExtractor.java
Normal file
59
src/the/bytecode/club/bytecodeviewer/util/JRTExtractor.java
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
package the.bytecode.club.bytecodeviewer.util;
|
||||||
|
|
||||||
|
// Copyright 2017 Robert Grosse
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import java.net.*;
|
||||||
|
import java.nio.file.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.zip.*;
|
||||||
|
|
||||||
|
public class JRTExtractor
|
||||||
|
{
|
||||||
|
public static void extractRT(String path) throws Throwable
|
||||||
|
{
|
||||||
|
FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
|
||||||
|
|
||||||
|
try(ZipOutputStream zipStream = new ZipOutputStream(Files.newOutputStream(Paths.get(path))))
|
||||||
|
{
|
||||||
|
Files.walk(fs.getPath("/")).forEach(p -> {
|
||||||
|
if (!Files.isRegularFile(p)) {return;}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
byte[] data = Files.readAllBytes(p);
|
||||||
|
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
p.iterator().forEachRemaining(p2 -> list.add(p2.toString()));
|
||||||
|
assert list.remove(0).equals("modules");
|
||||||
|
|
||||||
|
if (!list.get(list.size()-1).equals("module-info.class"))
|
||||||
|
{
|
||||||
|
list.remove(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
list.remove(0);
|
||||||
|
String outPath = String.join("/", list);
|
||||||
|
|
||||||
|
if(!outPath.endsWith("module-info.class"))
|
||||||
|
{
|
||||||
|
ZipEntry ze = new ZipEntry(outPath);
|
||||||
|
zipStream.putNextEntry(ze);
|
||||||
|
zipStream.write(data);
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {throw new RuntimeException(t);}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,7 +43,7 @@ public final class ZipUtils {
|
||||||
|
|
||||||
// fist get all directories,
|
// fist get all directories,
|
||||||
// then make those directory on the destination Path
|
// then make those directory on the destination Path
|
||||||
for (Enumeration<JarEntry> enums = jar.entries(); enums.hasMoreElements(); ) {
|
/*for (Enumeration<JarEntry> enums = jar.entries(); enums.hasMoreElements(); ) {
|
||||||
JarEntry entry = (JarEntry) enums.nextElement();
|
JarEntry entry = (JarEntry) enums.nextElement();
|
||||||
|
|
||||||
String fileName = destinationDir + File.separator + entry.getName();
|
String fileName = destinationDir + File.separator + entry.getName();
|
||||||
|
@ -53,7 +53,7 @@ public final class ZipUtils {
|
||||||
f.mkdirs();
|
f.mkdirs();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}*/
|
||||||
|
|
||||||
//now create all files
|
//now create all files
|
||||||
for (Enumeration<JarEntry> enums = jar.entries(); enums.hasMoreElements(); ) {
|
for (Enumeration<JarEntry> enums = jar.entries(); enums.hasMoreElements(); ) {
|
||||||
|
@ -62,6 +62,12 @@ public final class ZipUtils {
|
||||||
String fileName = destinationDir + File.separator + entry.getName();
|
String fileName = destinationDir + File.separator + entry.getName();
|
||||||
File f = new File(fileName);
|
File f = new File(fileName);
|
||||||
|
|
||||||
|
File parent = f.getParentFile();
|
||||||
|
if(!parent.exists())
|
||||||
|
{
|
||||||
|
parent.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
if (!fileName.endsWith("/")) {
|
if (!fileName.endsWith("/")) {
|
||||||
InputStream is = jar.getInputStream(entry);
|
InputStream is = jar.getInputStream(entry);
|
||||||
FileOutputStream fos = new FileOutputStream(f);
|
FileOutputStream fos = new FileOutputStream(f);
|
||||||
|
@ -119,6 +125,18 @@ public final class ZipUtils {
|
||||||
zip.close();
|
zip.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void zipFolderAPKTool(String srcFolder, String destZipFile) throws Exception {
|
||||||
|
ZipOutputStream zip = null;
|
||||||
|
FileOutputStream fileWriter = null;
|
||||||
|
|
||||||
|
fileWriter = new FileOutputStream(destZipFile);
|
||||||
|
zip = new ZipOutputStream(fileWriter);
|
||||||
|
|
||||||
|
addFolderToZipAPKTool("", srcFolder, zip);
|
||||||
|
zip.flush();
|
||||||
|
zip.close();
|
||||||
|
}
|
||||||
|
|
||||||
public static void addFileToZip(String path, String srcFile, ZipOutputStream zip, String ignore)
|
public static void addFileToZip(String path, String srcFile, ZipOutputStream zip, String ignore)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
|
@ -142,6 +160,38 @@ public final class ZipUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void addFileToZipAPKTool(String path, String srcFile, ZipOutputStream zip) throws Exception
|
||||||
|
{
|
||||||
|
File folder = new File(srcFile);
|
||||||
|
|
||||||
|
String check = path.toLowerCase();
|
||||||
|
//if(check.startsWith("decoded resources/unknown") || check.startsWith("decoded resources/lib") || check.startsWith("decoded resources/assets") || check.startsWith("decoded resources/original") || check.startsWith("decoded resources/smali") || check.startsWith("decoded resources/apktool.yml"))
|
||||||
|
if(check.startsWith("decoded resources/original") || check.startsWith("decoded resources/smali") || check.startsWith("decoded resources/apktool.yml"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
//if(path.equals("original") || path.equals("classes.dex") || path.equals("apktool.yml"))
|
||||||
|
// continue;
|
||||||
|
|
||||||
|
if (folder.isDirectory())
|
||||||
|
{
|
||||||
|
addFolderToZipAPKTool(path, srcFile, zip);
|
||||||
|
} else {
|
||||||
|
byte[] buf = new byte[1024];
|
||||||
|
int len;
|
||||||
|
FileInputStream in = new FileInputStream(srcFile);
|
||||||
|
ZipEntry entry = null;
|
||||||
|
|
||||||
|
entry = new ZipEntry(path + "/" + folder.getName());
|
||||||
|
zip.putNextEntry(entry);
|
||||||
|
|
||||||
|
while ((len = in.read(buf)) > 0)
|
||||||
|
{
|
||||||
|
zip.write(buf, 0, len);
|
||||||
|
}
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void addFolderToZip(String path, String srcFolder, ZipOutputStream zip, String ignore)
|
public static void addFolderToZip(String path, String srcFolder, ZipOutputStream zip, String ignore)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
File folder = new File(srcFolder);
|
File folder = new File(srcFolder);
|
||||||
|
@ -154,4 +204,19 @@ public final class ZipUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void addFolderToZipAPKTool(String path, String srcFolder, ZipOutputStream zip) throws Exception
|
||||||
|
{
|
||||||
|
File folder = new File(srcFolder);
|
||||||
|
|
||||||
|
for (String fileName : folder.list())
|
||||||
|
{
|
||||||
|
if (path.equals(""))
|
||||||
|
{
|
||||||
|
addFileToZipAPKTool(folder.getName(), srcFolder + "/" + fileName, zip);
|
||||||
|
} else {
|
||||||
|
addFileToZipAPKTool(path + "/" + folder.getName(), srcFolder + "/" + fileName, zip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue