From f76b31d8e7585370db7b31eb1ecddbd82b5d345d Mon Sep 17 00:00:00 2001 From: Konloch Date: Fri, 31 May 2019 19:04:07 -0600 Subject: [PATCH] v2.9.20 --- .../club/bytecodeviewer/BytecodeViewer.java | 56 ++++--- .../club/bytecodeviewer/Resources.java | 3 +- .../club/bytecodeviewer/SecurityMan.java | 6 +- .../compilers/SmaliAssembler.java | 3 +- .../decompilers/SmaliDisassembler.java | 10 +- .../bytecodeviewer/gui/MainViewerGUI.java | 151 +++++++++++++++++- .../plugin/preinstalled/EZInjection.java | 6 +- .../club/bytecodeviewer/util/APKTool.java | 70 ++++++-- .../club/bytecodeviewer/util/Dex2Jar.java | 19 ++- .../bytecodeviewer/util/FileContainer.java | 1 + .../bytecodeviewer/util/JRTExtractor.java | 59 +++++++ .../club/bytecodeviewer/util/ZipUtils.java | 69 +++++++- 12 files changed, 398 insertions(+), 55 deletions(-) create mode 100644 src/the/bytecode/club/bytecodeviewer/util/JRTExtractor.java diff --git a/src/the/bytecode/club/bytecodeviewer/BytecodeViewer.java b/src/the/bytecode/club/bytecodeviewer/BytecodeViewer.java index b67afa953..e2941dd99 100644 --- a/src/the/bytecode/club/bytecodeviewer/BytecodeViewer.java +++ b/src/the/bytecode/club/bytecodeviewer/BytecodeViewer.java @@ -16,12 +16,8 @@ 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 @@ 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 files = new ArrayList(); //all of BCV's loaded files/classes/etc + public static List files = new ArrayList(); //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 createdProcesses = new ArrayList(); + public static List createdProcesses = new ArrayList(); 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 recentPlugins; - private static ArrayList recentFiles; + private static List recentPlugins; + private static List recentFiles; static { @@ -669,6 +666,10 @@ public static FileContainer getFileContainer(String name) { return null; } + public static List 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 void run() { 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 static void resetWorkSpace(boolean ask) the.bytecode.club.bytecodeviewer.api.BytecodeViewer.getClassNodeLoader().clear(); } - private static ArrayList killList = new ArrayList(); + private static List killList = new ArrayList(); /** * Add the recent file @@ -1109,7 +1110,7 @@ public static void addRecentFile(File f) { resetRecentFilesMenu(); } - private static ArrayList killList2 = new ArrayList(); + private static List killList2 = new ArrayList(); /** * Add to the recent plugin list @@ -1187,7 +1188,7 @@ public static void cleanup() { tempF.mkdir(); } - public static ArrayList createdRandomizedNames = new ArrayList(); + public static List createdRandomizedNames = new ArrayList(); /** * Ensures it will only return a uniquely generated names, contains a dupe checker to be sure @@ -1254,7 +1255,7 @@ private static void hideFile(File f) { * @param a array * @return string with newline per array object */ - private static String quickConvert(ArrayList a) { + private static String quickConvert(List a) { return gson.toJson(a); } @@ -1471,11 +1472,30 @@ else if (BytecodeViewer.viewer.panelGroup3.isSelected(BytecodeViewer.viewer.pane 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(); + } + } } } diff --git a/src/the/bytecode/club/bytecodeviewer/Resources.java b/src/the/bytecode/club/bytecodeviewer/Resources.java index 6d0df38d8..5224c42fb 100644 --- a/src/the/bytecode/club/bytecodeviewer/Resources.java +++ b/src/the/bytecode/club/bytecodeviewer/Resources.java @@ -4,6 +4,7 @@ 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 @@ public class Resources { - public static ArrayList iconList; + public static List iconList; public static BufferedImage icon; public static ImageIcon nextIcon; public static ImageIcon prevIcon; diff --git a/src/the/bytecode/club/bytecodeviewer/SecurityMan.java b/src/the/bytecode/club/bytecodeviewer/SecurityMan.java index dbb8c82fe..3c60bfae5 100644 --- a/src/the/bytecode/club/bytecodeviewer/SecurityMan.java +++ b/src/the/bytecode/club/bytecodeviewer/SecurityMan.java @@ -44,6 +44,7 @@ public void stopBlocking() { //slightly safer security system than just a public 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 void checkExec(String cmd) { "attrib", "python", "pypy", - "java" + "java", + "brut_util", }; boolean allow = false; @@ -71,7 +73,7 @@ public void checkExec(String cmd) { 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 diff --git a/src/the/bytecode/club/bytecodeviewer/compilers/SmaliAssembler.java b/src/the/bytecode/club/bytecodeviewer/compilers/SmaliAssembler.java index e420b8683..7cd8c0a04 100644 --- a/src/the/bytecode/club/bytecodeviewer/compilers/SmaliAssembler.java +++ b/src/the/bytecode/club/bytecodeviewer/compilers/SmaliAssembler.java @@ -44,7 +44,7 @@ public byte[] compile(String contents, String name) { 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 @@ else if (BytecodeViewer.viewer.apkConversionGroup.isSelected(BytecodeViewer.view Enjarify.apk2Jar(tempDex, tempJar); try { + System.out.println("Unzipping to " + tempJarFolder.getAbsolutePath()); ZipUtils.unzipFilesToPath(tempJar.getAbsolutePath(), tempJarFolder.getAbsolutePath()); File outputClass = null; diff --git a/src/the/bytecode/club/bytecodeviewer/decompilers/SmaliDisassembler.java b/src/the/bytecode/club/bytecodeviewer/decompilers/SmaliDisassembler.java index 16c088ce7..210574728 100644 --- a/src/the/bytecode/club/bytecodeviewer/decompilers/SmaliDisassembler.java +++ b/src/the/bytecode/club/bytecodeviewer/decompilers/SmaliDisassembler.java @@ -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 String decompileClassNode(FileContainer container, ClassNode cn, byte[] b //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 String decompileClassNode(FileContainer container, ClassNode cn, byte[] b 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; diff --git a/src/the/bytecode/club/bytecodeviewer/gui/MainViewerGUI.java b/src/the/bytecode/club/bytecodeviewer/gui/MainViewerGUI.java index bc1d7fe3a..f55ea82e5 100644 --- a/src/the/bytecode/club/bytecodeviewer/gui/MainViewerGUI.java +++ b/src/the/bytecode/club/bytecodeviewer/gui/MainViewerGUI.java @@ -9,6 +9,7 @@ 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 boolean dispatchKeyEvent(KeyEvent e) } } - 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 void actionPerformed(ActionEvent e) 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 void run() 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; + } + + //if theres only one file in the container don't bother asking + List containers = BytecodeViewer.getFiles(); + List validContainers = new ArrayList<>(); + List 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(mntmSaveAsAPK); + mnNewMenu.add(mntmSaveAsDEX); mnNewMenu.add(mntmSave); mntmNewMenuItem.addActionListener(new ActionListener() { diff --git a/src/the/bytecode/club/bytecodeviewer/plugin/preinstalled/EZInjection.java b/src/the/bytecode/club/bytecodeviewer/plugin/preinstalled/EZInjection.java index 928af3ea7..02cc4b37d 100644 --- a/src/the/bytecode/club/bytecodeviewer/plugin/preinstalled/EZInjection.java +++ b/src/the/bytecode/club/bytecodeviewer/plugin/preinstalled/EZInjection.java @@ -231,9 +231,7 @@ else if (injectHooks) 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 @@ else if (injectHooks) 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)); diff --git a/src/the/bytecode/club/bytecodeviewer/util/APKTool.java b/src/the/bytecode/club/bytecodeviewer/util/APKTool.java index 2f4c18983..c7528b9a9 100644 --- a/src/the/bytecode/club/bytecodeviewer/util/APKTool.java +++ b/src/the/bytecode/club/bytecodeviewer/util/APKTool.java @@ -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 @@ * along with this program. If not, see . * ***************************************************************************/ +/** + * @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); + } + } } diff --git a/src/the/bytecode/club/bytecodeviewer/util/Dex2Jar.java b/src/the/bytecode/club/bytecodeviewer/util/Dex2Jar.java index 8b2af8218..cc96b8616 100644 --- a/src/the/bytecode/club/bytecodeviewer/util/Dex2Jar.java +++ b/src/the/bytecode/club/bytecodeviewer/util/Dex2Jar.java @@ -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 @@ else if(currentDexLocation.getAbsolutePath().toLowerCase().endsWith(".zip")) { 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); diff --git a/src/the/bytecode/club/bytecodeviewer/util/FileContainer.java b/src/the/bytecode/club/bytecodeviewer/util/FileContainer.java index 023228cfe..096fb52de 100644 --- a/src/the/bytecode/club/bytecodeviewer/util/FileContainer.java +++ b/src/the/bytecode/club/bytecodeviewer/util/FileContainer.java @@ -44,6 +44,7 @@ public FileContainer(File f, String name) { public File file; public String name; + public File APKToolContents = null; public HashMap files = new HashMap<>(); public ArrayList classes = new ArrayList<>(); diff --git a/src/the/bytecode/club/bytecodeviewer/util/JRTExtractor.java b/src/the/bytecode/club/bytecodeviewer/util/JRTExtractor.java new file mode 100644 index 000000000..62ddd6fdf --- /dev/null +++ b/src/the/bytecode/club/bytecodeviewer/util/JRTExtractor.java @@ -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 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);} + }); + } + } +} \ No newline at end of file diff --git a/src/the/bytecode/club/bytecodeviewer/util/ZipUtils.java b/src/the/bytecode/club/bytecodeviewer/util/ZipUtils.java index e21ab4691..9dd99349e 100644 --- a/src/the/bytecode/club/bytecodeviewer/util/ZipUtils.java +++ b/src/the/bytecode/club/bytecodeviewer/util/ZipUtils.java @@ -43,7 +43,7 @@ public static void unzipFilesToPath(String jarPath, String destinationDir) throw // fist get all directories, // then make those directory on the destination Path - for (Enumeration enums = jar.entries(); enums.hasMoreElements(); ) { + /*for (Enumeration enums = jar.entries(); enums.hasMoreElements(); ) { JarEntry entry = (JarEntry) enums.nextElement(); String fileName = destinationDir + File.separator + entry.getName(); @@ -53,7 +53,7 @@ public static void unzipFilesToPath(String jarPath, String destinationDir) throw f.mkdirs(); } - } + }*/ //now create all files for (Enumeration enums = jar.entries(); enums.hasMoreElements(); ) { @@ -62,6 +62,12 @@ public static void unzipFilesToPath(String jarPath, String destinationDir) throw 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 static void zipFolder(String srcFolder, String destZipFile, String ignore 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 static void addFileToZip(String path, String srcFile, ZipOutputStream zip } } + 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 static void addFolderToZip(String path, String srcFolder, ZipOutputStream } } } + + 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); + } + } + } } \ No newline at end of file