运行 内存中有文件的 jar 文件

Run jar file with file in memory

我创建了一个加密和解密 jar 文件的应用程序。我希望此应用程序解密文件,而不必将文件写入磁盘,运行 应用程序。有可能吗?

我用 C# 创建了这个应用程序,但它也可以用其他语言创建。

概念验证,主要归功于this overflower. You will need this class到运行它。当然,您的数组不是来自嵌入的 Base64 字符串,而是来自解密的 byte[]JARENC 一个jar文件:

package com.technojeeves.shm;

import com.technojeeves.io.IOUtils;
import java.util.Base64;
import java.util.zip.*;
import java.util.*;
import java.io.*;
import java.lang.reflect.Method;

public class Loader {
    public static void main(String[] args) {
         final String JARENC = "UEsDBBQACAgIABqp8FIAAAAAAAAAAAAAAAAlAAQAY29tL3RlY2hub2plZXZlcy9zaG0vSGVsbG9Xb3JsZC5jbGFzc/7KAABtUMtOwkAUPZdXaa2CIPgkwsIEXNiNO4wbE+OCqAkGF65KmcCQtmPKgPGzdKGJCz/AjzLeaUzQhFncx5nzSO7X98cngFM0HGSQtZBzkUeBUJ76C98L/Xjs3QynItCEwpmMpT4nZNudgY0ibAuOizW4/+j955kWEbPUnEW1XvojlXebyFj3dSL8qFvEBsG9EmGomk8qCUctB2VsWqi4qGKLUF2hIliPZgtjtm33/iRqhsfdzsBCnXAYqMjTIpjEairEQsy82STy0qx7E0XIXaiRIJR6MhbX82gokjt/GDKSi3zJ5vX2wyp3gtNX8yQQl9KQS0vLE8NGCzW+oXkZkLki123eGtyJe/74HfTCA2GHayEFs7Cxi71f6lEqBexXWJX1N5SWdCf1zvPZjXA/TTn4AVBLBwhqV5QAMQEAAL8BAABQSwECFAAUAAgICAAaqfBSaleUADEBAAC/AQAAJQAEAAAAAAAAAAAAAAAAAAAAY29tL3RlY2hub2plZXZlcy9zaG0vSGVsbG9Xb3JsZC5jbGFzc/7KAABQSwUGAAAAAAEAAQBXAAAAiAEAAAAA";


        try {
            Base64.Decoder dec = Base64.getDecoder();
            byte[] jarBytes = dec.decode(JARENC);
            RemoteClassLoader memJarLoader = new RemoteClassLoader(jarBytes);
            Class<?> hw = memJarLoader.loadClass("com.technojeeves.shm.HelloWorld", true);
            Method main = hw.getMethod("main", String[].class);
            String[] params = null;
            main.invoke(null, (Object)params);
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

    static class RemoteClassLoader extends ClassLoader {

        private final byte[] jarBytes;
        private final Set<String> names;

        public RemoteClassLoader(byte[] jarBytes) throws IOException {
            this.jarBytes = jarBytes;
            this.names = RemoteClassLoader.loadNames(jarBytes);
        }

        /**
         * This will put all the entries into a thread-safe Set
         */
        private static Set<String> loadNames(byte[] jarBytes) throws IOException {
            Set<String> set = new HashSet<>();
            try (ZipInputStream jis = new ZipInputStream(new ByteArrayInputStream(jarBytes))) {
                ZipEntry entry;
                while ((entry = jis.getNextEntry()) != null) {
                    set.add(entry.getName());
                }
            }
            return Collections.unmodifiableSet(set);
        }

        @Override
        public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
            Class<?> clazz = findLoadedClass(name);
            if (clazz == null) {
                try {
                    String toLoad = name.replace('.', '/') + ".class";
                    //System.out.printf("Attempting to load %s%n", toLoad);
                    InputStream in = getResourceAsStream(name.replace('.', '/') + ".class");
                    ByteArrayOutputStream out = new ByteArrayOutputStream();
                    //StreamUtils.writeTo(in, out);
                    IOUtils.copyStream(in, out);
                    byte[] bytes = out.toByteArray();
                    clazz = defineClass(name, bytes, 0, bytes.length);
                    if (resolve) {
                        resolveClass(clazz);
                    }
                } catch (Exception e) {
                    clazz = super.loadClass(name, resolve);
                }
            }
            return clazz;
        }

        @Override
        public InputStream getResourceAsStream(String name) {
            // Check first if the entry name is known
            if (!names.contains(name)) {
                return null;
            }
            // I moved the JarInputStream declaration outside the
            // try-with-resources statement as it must not be closed otherwise
            // the returned InputStream won't be readable as already closed
            boolean found = false;
            ZipInputStream jis = null;
            try {
                jis = new ZipInputStream(new ByteArrayInputStream(jarBytes));
                ZipEntry entry;
                while ((entry = jis.getNextEntry()) != null) {
                    if (entry.getName().equals(name)) {
                        found = true;
                        return jis;
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                // Only close the stream if the entry could not be found
                if (jis != null && !found) {
                    try {
                        jis.close();
                    } catch (IOException e) {
                        // ignore me
                    }
                }
            }
            return null;
        }
    }
}