Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Load byte array of file into the memory

I'm trying to load a jar file directly into memory without dropping it to the HDD. I have tried using the ClassLoader, but i get an error.

This is my code:

Custom Classloader

public class CLS_ClassLoader extends ClassLoader {

    private byte[] bArrData;

    public CLS_ClassLoader(ClassLoader parent, byte[] bArrData) {
        super(parent);

        this.bArrData = bArrData;
    }

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return defineClass(name, bArrData, 0,
                bArrData.length);
    }
}

Main

ClassLoader tParentClsLoader = CLS_ClassLoader.class.getClassLoader();
CLS_ClassLoader tClsLoader = new CLS_ClassLoader(tParentClsLoader, fileToByteArray("D:/App.jar"));
Class<?> tClass = null;

try {
        tClass = tClsLoader.loadClass("pkg_main.CLS_Main");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

Output

Exception in thread "main" java.lang.ClassFormatError: Incompatible magic value 1347093252 in class file pkg_main/CLS_Main
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at pkg_main.CLS_ClassLoader.loadClass(CLS_ClassLoader.java:20)
    at pkg_main.CSL_Main.main(CSL_Main.java:27)

My idea is to take a encrypted jar file , decrypted it on runtime and load directly into memory.

Sorry for the typos, I did not speak English well. Thanks in advance!

like image 471
SamYan Avatar asked Aug 24 '13 17:08

SamYan


1 Answers

Your main mistake is that defineClass(...) is expecting class bytes and you're feeding it with the whole jar file. The actual exception is thrown if class bytes do not start with 0xCAFEBABE, typical Java class file header. So, you need an additional step to sort the classes out of the jar file. The following implementation demonstrates the idea:

 class CCLoader extends ClassLoader {
    private Map<String, byte[]> classes = new HashMap<String, byte[]>();

    public CCLoader(InputStream in) {
        super(CCLoader.class.getClassLoader());
        try {
            JarInputStream jis = new JarInputStream(in);
            JarEntry je = null;
            String entryName = null;
            while ((je = jis.getNextJarEntry()) != null) {
                entryName = je.getName();
                if (je.getName().endsWith(".class")) {
                    byte[] classBytes = readClass(jis);
                    String canonicalName = entryName.replaceAll("/", ".").replaceAll(".class", "");
                    classes.put(canonicalName, classBytes);
                }
            }
            jis.close();
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private byte[] readClass(InputStream stream) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        while(true){
            int qwe = stream.read();
            if(qwe == -1) break;
            baos.write(qwe);
        }
        return baos.toByteArray();
    }

    public Class loadClass(String name) throws ClassNotFoundException {
        try {
            return this.getParent().loadClass(name);
        } catch (ClassNotFoundException e) {
            return findClass(name);
        }
    }

    public Class findClass(String name) throws ClassNotFoundException {
        byte[] classBytes = classes.get(name);
        return defineClass(name, classBytes, 0, classBytes.length);
    }

}

Following your example you can try it like that:

ClassLoader tClsLoader = new CCLoader(new FileInputStream("C:/commons-io-2.0.1.jar"));
Class<?> tClass = tClsLoader.loadClass("org.apache.commons.io.FileExistsException");
like image 170
Jk1 Avatar answered Oct 12 '22 16:10

Jk1