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.Paths;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Enumeration;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
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.JFileChooser;
 | 
			
		||||
| 
						 | 
				
			
			@ -118,7 +114,7 @@ import the.bytecode.club.bytecodeviewer.util.*;
 | 
			
		|||
public class BytecodeViewer
 | 
			
		||||
{
 | 
			
		||||
    /*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 enjarifyVersion = "4";
 | 
			
		||||
    public static final boolean BLOCK_TAB_MENU = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -145,12 +141,13 @@ public class BytecodeViewer
 | 
			
		|||
    public static boolean currentlyDumping = false;
 | 
			
		||||
    public static boolean needsReDump = true;
 | 
			
		||||
    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;
 | 
			
		||||
    public static String fs = System.getProperty("file.separator");
 | 
			
		||||
    public static String nl = System.getProperty("line.separator");
 | 
			
		||||
    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 pluginsName = getBCVDirectory() + fs + "recentplugins.json";
 | 
			
		||||
    public static String settingsName = getBCVDirectory() + fs + "settings.bcv";
 | 
			
		||||
| 
						 | 
				
			
			@ -161,15 +158,15 @@ public class BytecodeViewer
 | 
			
		|||
    public static boolean runningObfuscation = false;
 | 
			
		||||
    private static long start = System.currentTimeMillis();
 | 
			
		||||
    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 boolean pingback = false;
 | 
			
		||||
    public static boolean deleteForeignLibraries = true;
 | 
			
		||||
    public static boolean canExit = false;
 | 
			
		||||
    public static Gson gson;
 | 
			
		||||
 | 
			
		||||
    private static ArrayList<String> recentPlugins;
 | 
			
		||||
    private static ArrayList<String> recentFiles;
 | 
			
		||||
    private static List<String> recentPlugins;
 | 
			
		||||
    private static List<String> recentFiles;
 | 
			
		||||
 | 
			
		||||
    static
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -669,6 +666,10 @@ public class BytecodeViewer
 | 
			
		|||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static List<FileContainer> getFiles() {
 | 
			
		||||
        return files;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static ClassNode getClassNode(FileContainer container, String name) {
 | 
			
		||||
        for (ClassNode c : container.classes)
 | 
			
		||||
            if (c.name.equals(name))
 | 
			
		||||
| 
						 | 
				
			
			@ -943,7 +944,7 @@ public class BytecodeViewer
 | 
			
		|||
 | 
			
		||||
                                        if (viewer.decodeAPKResources.isSelected()) {
 | 
			
		||||
                                            File decodedResources = new File(tempDirectory + fs + MiscUtils.randomString(32) + ".apk");
 | 
			
		||||
                                            APKTool.decodeResources(tempCopy, decodedResources);
 | 
			
		||||
                                            APKTool.decodeResources(tempCopy, decodedResources, container);
 | 
			
		||||
                                            container.files = JarUtils.loadResources(decodedResources);
 | 
			
		||||
                                        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1080,7 +1081,7 @@ public class BytecodeViewer
 | 
			
		|||
        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
 | 
			
		||||
| 
						 | 
				
			
			@ -1109,7 +1110,7 @@ public class BytecodeViewer
 | 
			
		|||
        resetRecentFilesMenu();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static ArrayList<String> killList2 = new ArrayList<String>();
 | 
			
		||||
    private static List<String> killList2 = new ArrayList<String>();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add to the recent plugin list
 | 
			
		||||
| 
						 | 
				
			
			@ -1187,7 +1188,7 @@ public class BytecodeViewer
 | 
			
		|||
            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
 | 
			
		||||
| 
						 | 
				
			
			@ -1254,7 +1255,7 @@ public class BytecodeViewer
 | 
			
		|||
     * @param a array
 | 
			
		||||
     * @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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1471,11 +1472,30 @@ public class BytecodeViewer
 | 
			
		|||
        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.File;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import javax.imageio.ImageIO;
 | 
			
		||||
import javax.swing.ImageIcon;
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +38,7 @@ import org.imgscalr.Scalr;
 | 
			
		|||
 | 
			
		||||
public class Resources {
 | 
			
		||||
 | 
			
		||||
    public static ArrayList<BufferedImage> iconList;
 | 
			
		||||
    public static List<BufferedImage> iconList;
 | 
			
		||||
    public static BufferedImage icon;
 | 
			
		||||
    public static ImageIcon nextIcon;
 | 
			
		||||
    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.compilers.KrakatauAssembler") ||
 | 
			
		||||
                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.compilers.JavaCompiler")) {
 | 
			
		||||
            blocking = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -60,7 +61,8 @@ public class SecurityMan extends SecurityManager {
 | 
			
		|||
                "attrib",
 | 
			
		||||
                "python",
 | 
			
		||||
                "pypy",
 | 
			
		||||
                "java"
 | 
			
		||||
                "java",
 | 
			
		||||
                "brut_util",
 | 
			
		||||
        };
 | 
			
		||||
        boolean allow = false;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +73,7 @@ public class SecurityMan extends SecurityManager {
 | 
			
		|||
 | 
			
		||||
        if (allow && !blocking) {
 | 
			
		||||
            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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,7 +44,7 @@ public class SmaliAssembler extends Compiler {
 | 
			
		|||
        tempSmaliFolder.mkdir();
 | 
			
		||||
 | 
			
		||||
        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 tempJarFolder = new File(fileStart + fileNumber + "-jar" + BytecodeViewer.fs);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -68,6 +68,7 @@ public class SmaliAssembler extends Compiler {
 | 
			
		|||
            Enjarify.apk2Jar(tempDex, tempJar);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            System.out.println("Unzipping to " + tempJarFolder.getAbsolutePath());
 | 
			
		||||
            ZipUtils.unzipFilesToPath(tempJar.getAbsolutePath(), tempJarFolder.getAbsolutePath());
 | 
			
		||||
 | 
			
		||||
            File outputClass = null;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,8 +41,7 @@ public class SmaliDisassembler extends Decompiler {
 | 
			
		|||
 | 
			
		||||
    public String decompileClassNode(FileContainer container, ClassNode cn, byte[] b) {
 | 
			
		||||
        String exception = "";
 | 
			
		||||
        String fileStart = BytecodeViewer.tempDirectory + BytecodeViewer.fs
 | 
			
		||||
                + "temp";
 | 
			
		||||
        String fileStart = BytecodeViewer.tempDirectory + BytecodeViewer.fs + "temp";
 | 
			
		||||
 | 
			
		||||
        String start = MiscUtils.getUniqueName(fileStart, ".class");
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -62,9 +61,7 @@ public class SmaliDisassembler extends Decompiler {
 | 
			
		|||
 | 
			
		||||
        //ZipUtils.zipFile(tempClass, tempZip);
 | 
			
		||||
 | 
			
		||||
        Dex2Jar.saveAsDex(container.file, tempDex, false);
 | 
			
		||||
 | 
			
		||||
        System.out.println("FOR SHOW: " + tempDex.getName().replaceFirst("\\.dex", "-out")); //tempSmali.getAbsolutePath()
 | 
			
		||||
        Dex2Jar.saveAsDex(tempClass, tempDex, true);
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -94,9 +91,6 @@ public class SmaliDisassembler extends Decompiler {
 | 
			
		|||
            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;
 | 
			
		||||
 | 
			
		||||
        boolean found = false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,6 +9,7 @@ import java.awt.KeyboardFocusManager;
 | 
			
		|||
import java.awt.event.*;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import javax.swing.BoxLayout;
 | 
			
		||||
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 JSeparator separator_7 = 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(mntmNewMenuItem_3);
 | 
			
		||||
        mntmSaveAsApk.addActionListener(new ActionListener()
 | 
			
		||||
        mntmSaveAsDEX.addActionListener(new ActionListener()
 | 
			
		||||
        {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void actionPerformed(ActionEvent arg0)
 | 
			
		||||
| 
						 | 
				
			
			@ -940,8 +942,151 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier
 | 
			
		|||
                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);
 | 
			
		||||
        mntmNewMenuItem.addActionListener(new ActionListener()
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -231,9 +231,7 @@ public class EZInjection extends Plugin {
 | 
			
		|||
                        MethodInsnNode mn = (MethodInsnNode) m.instructions
 | 
			
		||||
                                .get(1);
 | 
			
		||||
                        if (mn.owner
 | 
			
		||||
                                .equals("the/bytecode/club/bytecodeviewer/plugins/EZInjection")) // already
 | 
			
		||||
                            // been
 | 
			
		||||
                            // injected
 | 
			
		||||
                                .equals(EZInjection.class.getName().replace(".", "/")))//"the/bytecode/club/bytecodeviewer/plugins/EZInjection")) // already been injected
 | 
			
		||||
                            inject = false;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (inject) {
 | 
			
		||||
| 
						 | 
				
			
			@ -241,7 +239,7 @@ public class EZInjection extends Plugin {
 | 
			
		|||
                        m.instructions
 | 
			
		||||
                                .insert(new MethodInsnNode(
 | 
			
		||||
                                        Opcodes.INVOKESTATIC,
 | 
			
		||||
                                        "the/bytecode/club/bytecodeviewer/plugins/EZInjection",
 | 
			
		||||
                                        EZInjection.class.getName().replace(".", "/"),//"the/bytecode/club/bytecodeviewer/plugins/EZInjection",
 | 
			
		||||
                                        "hook", "(Ljava/lang/String;)V"));
 | 
			
		||||
                        m.instructions.insert(new LdcInsnNode(classNode.name
 | 
			
		||||
                                + "." + m.name + m.desc));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,17 @@
 | 
			
		|||
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.IOUtils;
 | 
			
		||||
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
 | 
			
		||||
import the.bytecode.club.bytecodeviewer.SecurityMan;
 | 
			
		||||
 | 
			
		||||
/***************************************************************************
 | 
			
		||||
 * 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/>. *
 | 
			
		||||
 ***************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author Konloch
 | 
			
		||||
 */
 | 
			
		||||
public class APKTool {
 | 
			
		||||
 | 
			
		||||
    public static synchronized void decodeResources(File input, File output) {
 | 
			
		||||
    public static synchronized void decodeResources(File input, File output, FileContainer container) {
 | 
			
		||||
        try {
 | 
			
		||||
            File dir = new File(BytecodeViewer.tempDirectory + BytecodeViewer.fs + "Decoded Resources");
 | 
			
		||||
            FileUtils.deleteDirectory(dir);
 | 
			
		||||
            File temp = new File(BytecodeViewer.tempDirectory + BytecodeViewer.fs + MiscUtils.randomString(12));
 | 
			
		||||
            temp.mkdirs();
 | 
			
		||||
            brut.apktool.Main.main(new String[]{"--frame-path", temp.getAbsolutePath(), "-s", "-f", "-o", dir.getAbsolutePath(), "decode", input.getAbsolutePath()});
 | 
			
		||||
            File original = new File(dir.getAbsolutePath() + BytecodeViewer.fs + "original");
 | 
			
		||||
            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 dir = new File(BytecodeViewer.tempDirectory + BytecodeViewer.fs + MiscUtils.randomString(32)+BytecodeViewer.fs+"Decoded Resources");
 | 
			
		||||
            dir.mkdirs();
 | 
			
		||||
 | 
			
		||||
            File tempAPKPath = new File(BytecodeViewer.tempDirectory + BytecodeViewer.fs + MiscUtils.randomString(12));
 | 
			
		||||
            tempAPKPath.mkdirs();
 | 
			
		||||
            brut.apktool.Main.main(new String[]{"r", "--frame-path", tempAPKPath.getAbsolutePath(), "d", input.getAbsolutePath(), "-o", dir.getAbsolutePath(), "-f"});
 | 
			
		||||
 | 
			
		||||
            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())
 | 
			
		||||
                zip.renameTo(output);
 | 
			
		||||
            FileUtils.deleteDirectory(dir);
 | 
			
		||||
 | 
			
		||||
            container.APKToolContents = dir;
 | 
			
		||||
            tempAPKPath.delete();
 | 
			
		||||
        } catch (Exception 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;
 | 
			
		||||
 | 
			
		||||
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 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        *
 | 
			
		||||
| 
						 | 
				
			
			@ -81,6 +94,10 @@ public class Dex2Jar {
 | 
			
		|||
            {
 | 
			
		||||
                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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,6 +44,7 @@ public class FileContainer {
 | 
			
		|||
 | 
			
		||||
    public File file;
 | 
			
		||||
    public String name;
 | 
			
		||||
    public File APKToolContents = null;
 | 
			
		||||
 | 
			
		||||
    public HashMap<String, byte[]> files = new HashMap<>();
 | 
			
		||||
    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,
 | 
			
		||||
        // 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();
 | 
			
		||||
 | 
			
		||||
            String fileName = destinationDir + File.separator + entry.getName();
 | 
			
		||||
| 
						 | 
				
			
			@ -53,7 +53,7 @@ public final class ZipUtils {
 | 
			
		|||
                f.mkdirs();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        }*/
 | 
			
		||||
 | 
			
		||||
        //now create all files
 | 
			
		||||
        for (Enumeration<JarEntry> enums = jar.entries(); enums.hasMoreElements(); ) {
 | 
			
		||||
| 
						 | 
				
			
			@ -62,6 +62,12 @@ public final class ZipUtils {
 | 
			
		|||
            String fileName = destinationDir + File.separator + entry.getName();
 | 
			
		||||
            File f = new File(fileName);
 | 
			
		||||
 | 
			
		||||
            File parent = f.getParentFile();
 | 
			
		||||
            if(!parent.exists())
 | 
			
		||||
            {
 | 
			
		||||
                parent.mkdirs();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!fileName.endsWith("/")) {
 | 
			
		||||
                InputStream is = jar.getInputStream(entry);
 | 
			
		||||
                FileOutputStream fos = new FileOutputStream(f);
 | 
			
		||||
| 
						 | 
				
			
			@ -119,6 +125,18 @@ public final class ZipUtils {
 | 
			
		|||
        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)
 | 
			
		||||
            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)
 | 
			
		||||
            throws Exception {
 | 
			
		||||
        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…
	
	Add table
		Add a link
		
	
		Reference in a new issue