问题描述
我正在尝试从字节数组加载jar而不将其写入文件(将其加载到内存中)。我已经创建了一个自定义的ClassLoader但是当我尝试使用它并加载一个类时,它给了我ClassNotFoundException。
I'm trying to load a jar from a byte array without having it written to a file (loading it into memory). I have made a custom ClassLoader but when I try to use it and load a class it gives me ClassNotFoundException.
ClassLoader
ClassLoader
public class NetworkClassLoader extends ClassLoader
{
/*
* Default ClassLoader.
*/
private final ClassLoader startup;
/*
* Byte array used to load classes.
*/
private final byte[] bytes;
/*
* HashMap used to contain cached classes.
*/
private HashMap<String, byte[]> classes = new HashMap<>();
/*
* Initializes byte array used for loading classes.
* @param ClassLoader classLoader
* @param byte[] bytes
*/
public NetworkClassLoader(ClassLoader classLoader, byte[] bytes)
{
this.startup = classLoader;
this.bytes = bytes;
}
/*
* Loads class from name.
* (non-Javadoc)
* @see java.lang.ClassLoader#loadClass(java.lang.String, boolean)
* @param String name
* @param boolean resolve
* @throws ClassNotFoundException
* @returns clazz
*/
@Override
public Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
Class<?> clazz = findLoadedClass(name);
if (clazz == null)
{
try
{
InputStream in = getResourceAsStream(name.replace('.', '/') + ".class");
if (in == null) return null;
ByteArrayOutputStream out = new ByteArrayOutputStream();
IOUtils.writeStream(in, out);
in.close();
byte[] bytes = out.toByteArray();
out.close();
clazz = defineClass(name, bytes, 0, bytes.length);
if (resolve)
{
resolveClass(clazz);
}
}
catch (Exception e)
{
clazz = super.loadClass(name, resolve);
}
}
return clazz;
}
/*
* Returns resource.
* (non-Javadoc)
* @see java.lang.ClassLoader#getResource(java.lang.String)
* @param String name
*/
@Override
public URL getResource(String name)
{
return null;
}
/*
* Returns resource as stream.
* (non-Javadoc)
* @see java.lang.ClassLoader#getResourceAsStream(java.lang.String)
* @param String name
* @return ByteArrayInputStream
*/
@Override
public InputStream getResourceAsStream(String name)
{
InputStream jarRes = this.startup.getResourceAsStream(name);
if (jarRes != null)
{
return jarRes;
}
if (!this.classes.containsKey(name))
{
return null;
}
return new ByteArrayInputStream((byte[])this.classes.get(name));
}
/*
* Loads classes using byte array.
*/
public void inject()
{
if (bytes == null) return;
try
{
JarInputStream jis = new JarInputStream(new ByteArrayInputStream(bytes));
try
{
JarEntry entry;
while ((entry = jis.getNextJarEntry()) != null)
{
String entryName = entry.getName();
ByteArrayOutputStream out = new ByteArrayOutputStream();
IOUtils.writeStream(jis, out);
byte[] bytes = out.toByteArray();
this.classes.put(entryName, bytes);
this.loadClass(entryName, false);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
主要
byte[] array =
IOUtils.readFileBytes(new File("C:\\Users\\o_m_a\\Desktop\\HWID.jar"));
ByteClassLoader loader = new ByteClassLoader(Main.class.getClassLoader(), array);
loader.inject();
//System.out.println(Arrays.toString(array));
try {
Class<?> clazz = loader.loadClass("Main", true).newInstance();
Method m = clazz.getMethod(method, (Class<?>[]) null);
m.setAccessible(true);
m.invoke(clazz.newInstance(), (Object[]) null);
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
e.printStackTrace();
}
它正确加载我的类并运行它但我一直收到随机错误。
It loads my class correctly and runs it but I keep getting random errors.
推荐答案
这有点hackish但做他的作品,这段代码基本上创建了一个假的url scheme(myjarprotocol),当打开时返回 jarBytes
字段(您的真实jar数据的位置)。然后通过反射调用 SystemClassLoader.addURL
,它将URL作为他必须加载的jar的参数。总而言之,SystemClassLoader被欺骗从伪url方案加载一个jar,返回你想要的任何InputStream。
This is a bit hackish but do his works, this code basically create a fake url scheme (myjarprotocol) that when opened return the jarBytes
field (where your real jar data goes). Then via reflection it call SystemClassLoader.addURL
that take a URL as parameter of the jar he have to load. In conclusion the SystemClassLoader is tricked to load a jar from a fake url scheme that return any InputStream you want.
public class JarLoader {
private static final byte[] jarBytes = new byte[] { 0x00 /* .... etc*/ };
public static void main(String[] args) throws Exception {
URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() {
public URLStreamHandler createURLStreamHandler(String urlProtocol) {
System.out.println("Someone asked for protocol: " + urlProtocol);
if ("myjarprotocol".equalsIgnoreCase(urlProtocol)) {
return new URLStreamHandler() {
@Override
protected URLConnection openConnection(URL url) throws IOException {
return new URLConnection(url) {
public void connect() throws IOException {}
public InputStream getInputStream() throws IOException {
System.out.println("Someone is getting my jar!!");
return new ByteArrayInputStream(jarBytes);
}
};
}
};
}
return null;
}
});
System.out.println("Loading jar with fake protocol!");
loadJarFromURL(new URL("myjarprotocol:fakeparameter"));
}
public static final void loadJarFromURL(URL jarURL) throws Exception {
URLClassLoader systemClassloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Method systemClassloaderMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
systemClassloaderMethod.setAccessible(true);
systemClassloaderMethod.invoke(systemClassloader, jarURL);
// This make classloader open the connection
systemClassloader.findResource("/resource-404");
}
}
这篇关于如何将bytearray转换为Jar的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!