Java tutorial
package the.bytecode.club.bytecodeviewer.plugin.strategies; import org.objectweb.asm.ClassReader; import org.objectweb.asm.tree.ClassNode; import the.bytecode.club.bytecodeviewer.JarUtils; import the.bytecode.club.bytecodeviewer.api.Plugin; import the.bytecode.club.bytecodeviewer.plugin.PluginLaunchStrategy; import java.io.File; import java.io.FileInputStream; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; /*************************************************************************** * Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite * * Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ /** * @author Konloch * @author Bibl (don't ban me pls) * @created 1 Jun 2015 */ public class CompiledJavaPluginLaunchStrategy implements PluginLaunchStrategy { private static final String PLUGIN_CLASS_NAME = Plugin.class.getCanonicalName().replace(".", "/"); private final Set<LoadedPluginData> loaded = new HashSet<LoadedPluginData>(); @Override public Plugin run(File file) throws Throwable { Set<LoadedNodeData> set = loadData(file); LoadedNodeData pdata = null; for (LoadedNodeData d : set) { ClassNode cn = d.node; if (cn.superName.equals(PLUGIN_CLASS_NAME)) { if (pdata == null) { pdata = d; } else { throw new RuntimeException("Multiple plugin subclasses."); } } } LoadingClassLoader cl = new LoadingClassLoader(pdata, set); Plugin p = cl.pluginKlass.newInstance(); LoadedPluginData npdata = new LoadedPluginData(pdata, cl, p); loaded.add(npdata); return p; } public Set<LoadedPluginData> getLoaded() { return loaded; } private static Set<LoadedNodeData> loadData(File jarFile) throws Throwable { ZipInputStream jis = new ZipInputStream(new FileInputStream(jarFile)); ZipEntry entry; Set<LoadedNodeData> set = new HashSet<LoadedNodeData>(); while ((entry = jis.getNextEntry()) != null) { try { String name = entry.getName(); if (name.endsWith(".class")) { byte[] bytes = JarUtils.getBytes(jis); String magic = String.format("%02X", bytes[0]) + String.format("%02X", bytes[1]) + String.format("%02X", bytes[2]) + String.format("%02X", bytes[3]); if (magic.toLowerCase().equals("cafebabe")) { try { ClassReader cr = new ClassReader(bytes); ClassNode cn = new ClassNode(); cr.accept(cn, 0); LoadedNodeData data = new LoadedNodeData(bytes, cn); set.add(data); } catch (Exception e) { e.printStackTrace(); } } else { System.out .println(jarFile + ">" + name + ": Header does not start with CAFEBABE, ignoring."); } } } catch (Exception e) { new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e); } finally { jis.closeEntry(); } } jis.close(); return set; } public static class LoadedNodeData { private final byte[] bytes; private final ClassNode node; public LoadedNodeData(byte[] bytes, ClassNode node) { this.bytes = bytes; this.node = node; } } public static class LoadedPluginData { private final LoadedNodeData data; private final LoadingClassLoader classLoader; private final Plugin plugin; public LoadedPluginData(LoadedNodeData data, LoadingClassLoader classLoader, Plugin plugin) { this.data = data; this.classLoader = classLoader; this.plugin = plugin; } public LoadedNodeData getData() { return data; } public LoadingClassLoader getClassLoader() { return classLoader; } public Plugin getPlugin() { return plugin; } } public static class LoadingClassLoader extends ClassLoader { private final LoadedNodeData data; private Map<String, LoadedNodeData> cache; private Map<String, Class<?>> ccache; private final Class<? extends Plugin> pluginKlass; public LoadingClassLoader(LoadedNodeData data, Set<LoadedNodeData> set) throws Throwable { this.data = data; cache = new HashMap<String, LoadedNodeData>(); ccache = new HashMap<String, Class<?>>(); for (LoadedNodeData d : set) { cache.put(d.node.name, d); } @SuppressWarnings("unchecked") Class<? extends Plugin> pluginKlass = (Class<? extends Plugin>) loadClass( data.node.name.replace("/", ".")); if (pluginKlass == null) throw new RuntimeException(); this.pluginKlass = pluginKlass; } @Override public Class<?> findClass(String name) throws ClassNotFoundException { name = name.replace(".", "/"); System.out.println("finding " + name); if (ccache.containsKey(name)) return ccache.get(name); LoadedNodeData data = cache.get(name); if (data != null) { byte[] bytes = data.bytes; Class<?> klass = defineClass(data.node.name.replace("/", "."), bytes, 0, bytes.length); ccache.put(name, klass); return klass; } return super.findClass(name); } public LoadedNodeData getPluginNode() { return data; } public Class<? extends Plugin> getPluginKlass() { return pluginKlass; } } }