当前位置: 首页 > 知识库问答 >
问题:

JNI:如何从字节数组加载类?

公西宏峻
2023-03-14

如何从JNI中JAR文件的字节数组加载所有类<我的代码

#include <jni.h>
#include <string>
using namespace std;

int main() {
    JavaVM* jvm;
    JNIEnv* env;
    long res = CreateJVM(&jvm, &env, "");

    jclass class_loader = env->FindClass("java/lang/ClassLoader");
    jobject system_loader = env->CallStaticObjectMethod(class_loader, env->GetStaticMethodID(class_loader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;"));
    jbyte* bytes = <bytes>


    // define all classes from this bytes
    env->DefineClass("Main", system_loader, bytes, sizeof(bytes));
    ...

    if (res == JNI_OK) {
        jclass cls = env->FindClass("Main");
        jmethodID mid = env->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V");
        env->CallStaticVoidMethod(cls, mid, NULL);
    }

    jvm->DestroyJavaVM();
}

我需要读取JAR文件的字节,将它们转换成jbytes并从这些字节加载所有java类

比如java中的代码

final var classes = new ArrayList<byte[]>();
try( final var inputStream = new JarInputStream( new ByteArrayInputStream( bytes ) ) )
{
  var entry = inputStream.getNextJarEntry();
  while( nonNull( entry ) )
  {
    var buffer = new bytes [entry.getSize()];
    inputStream.read( buffer, 0, entry.getSize()];
    classes.add( buffer );
    entry = inputStream.getNextJarEntry();
  }
}

共有1个答案

谷梁宜
2023-03-14

Java代码无法正确加载jar。至少它没有定义类或跟踪jar中条目的名称。

这适用于我过去测试过的所有罐子:

#include <jni.h>
#include <iostream>
#include <memory>
#include <vector>

struct JVM
{
    JavaVM* vm;
    JNIEnv* env;
};

JVM create_java_vm()
{
    const char* argv[] = {"-Djava.compiler=NONE",
                          "-Djava.class.path=."}; //"-verbose:jni"
    const int argc = static_cast<int>(sizeof(argv) / sizeof(argv[0]));
    
    JavaVMInitArgs jvm_args;
    JavaVMOption options[argc];

    for (int i = 0; i < argc; ++i)
    {
        options[i].optionString = const_cast<char*>(argv[i]);
    }

    JavaVM* vm = nullptr;
    JNIEnv* env = nullptr;
    JNI_GetDefaultJavaVMInitArgs(&jvm_args);

    jvm_args.version = JNI_VERSION_1_8;
    jvm_args.nOptions = argc;
    jvm_args.options = options;
    jvm_args.ignoreUnrecognized = false;

    if (JNI_CreateJavaVM(&vm, reinterpret_cast<void**>(&env), &jvm_args) != JNI_OK)
    {
        return {vm, env};
    }
    return {vm, env};
}

bool load_jar(JNIEnv* env, const std::vector<std::uint8_t> &jar_data, bool ignore_exceptions = false)
{
    auto string_replace_all = [&](std::string str, const std::string& from, const std::string& to) -> std::string {
        size_t start_pos = 0;
        while((start_pos = str.find(from, start_pos)) != std::string::npos)
        {
            str.replace(start_pos, from.length(), to);
        }
        return str;
    };

    auto byte_array_input_stream = [](JNIEnv* env, const std::vector<std::uint8_t> &buffer) -> jobject {
        jbyteArray arr = env->NewByteArray(static_cast<jsize>(buffer.size()));
        if (arr)
        {
            env->SetByteArrayRegion(arr, 0, static_cast<jsize>(buffer.size()), reinterpret_cast<const jbyte*>(&buffer[0]));
            jclass cls = env->FindClass("java/io/ByteArrayInputStream");
            if (cls)
            {
                jmethodID method = env->GetMethodID(cls, "<init>", "([B)V");
                if (method)
                {
                    jobject result = env->NewObject(cls, method, arr);
                    if (result)
                    {
                        env->DeleteLocalRef(std::exchange(result, env->NewGlobalRef(result)));
                        env->DeleteLocalRef(cls);
                        env->DeleteLocalRef(arr);
                        return result;
                    }
                }
                env->DeleteLocalRef(cls);
            }
            env->DeleteLocalRef(arr);
        }
        return nullptr;
    };

    auto jar_input_stream = [](JNIEnv* env, jobject input_stream) -> jobject {
        jclass cls = env->FindClass("java/util/jar/JarInputStream");
        if (cls)
        {
            jmethodID method = env->GetMethodID(cls, "<init>", "(Ljava/io/InputStream;)V");
            if (method)
            {
                jobject result = env->NewObject(cls, method, input_stream);
                if (result)
                {
                    env->DeleteLocalRef(std::exchange(result, env->NewGlobalRef(result)));
                    env->DeleteLocalRef(cls);
                    return result;
                }
            }
            env->DeleteLocalRef(cls);
        }
        return nullptr;
    };

    auto input_stream_read = [](JNIEnv* env, jobject input_stream) -> jint {
        jclass cls = env->GetObjectClass(input_stream);
        if (cls)
        {
            jmethodID method = env->GetMethodID(cls, "read", "()I");
            if (method)
            {
                jint result = env->CallIntMethod(input_stream, method);
                env->DeleteLocalRef(cls);
                return result;
            }
            
            env->DeleteLocalRef(cls);
        }
        return -1;
    };

    auto byte_array_output_stream = [](JNIEnv* env) -> jobject {
        jclass cls = env->FindClass("java/io/ByteArrayOutputStream");
        if (cls)
        {
            jmethodID method = env->GetMethodID(cls, "<init>", "()V");
            if (method)
            {
                jobject result = env->NewObject(cls, method);
                if (result)
                {
                    env->DeleteLocalRef(std::exchange(result, env->NewGlobalRef(result)));
                    env->DeleteLocalRef(cls);
                    return result;
                }
            }
            env->DeleteLocalRef(cls);
        }
        return nullptr;
    };

    auto output_stream_write = [](JNIEnv* env, jobject output_stream, jint value) {
        jclass cls = env->GetObjectClass(output_stream);
        if (cls)
        {
            jmethodID method = env->GetMethodID(cls, "write", "(I)V");
            if (method)
            {
                env->CallVoidMethod(output_stream, method, value);
            }
            env->DeleteLocalRef(cls);
        }
    };

    auto byte_array_output_stream_to_byte_array = [](JNIEnv* env, jobject output_stream) -> std::vector<std::uint8_t> {
        jclass cls = env->GetObjectClass(output_stream);
        if (cls)
        {
            jmethodID method = env->GetMethodID(cls, "toByteArray", "()[B");
            if (method)
            {
                jbyteArray bytes = reinterpret_cast<jbyteArray>(env->CallObjectMethod(output_stream, method));
                if (bytes)
                {
                    void* arr = env->GetPrimitiveArrayCritical(bytes, nullptr);
                    if (arr)
                    {
                        jsize size = env->GetArrayLength(bytes);
                        auto result = std::vector<std::uint8_t>(static_cast<std::uint8_t*>(arr),
                                                                static_cast<std::uint8_t*>(arr) + size);
                        env->ReleasePrimitiveArrayCritical(bytes, arr, 0);
                        env->DeleteLocalRef(bytes);
                        env->DeleteLocalRef(cls);
                        return result;
                    }
                }
            }
            env->DeleteLocalRef(cls);
        }
        return {};
    };

    auto get_next_jar_entry = [](JNIEnv* env, jobject jar_input_stream) -> jobject {
        jclass cls = env->GetObjectClass(jar_input_stream);
        if (cls)
        {
            jmethodID method = env->GetMethodID(cls, "getNextJarEntry", "()Ljava/util/jar/JarEntry;");
            if (method)
            {
                jobject result = env->CallObjectMethod(jar_input_stream, method);
                env->DeleteLocalRef(std::exchange(result, env->NewGlobalRef(result)));
                env->DeleteLocalRef(cls);
                return result;
            }
            
            env->DeleteLocalRef(cls);
        }
        return nullptr;
    };

    auto jar_entry_get_name = [](JNIEnv* env, jobject jar_entry) -> std::string {
        jclass cls = env->FindClass("java/util/jar/JarEntry");
        if (cls)
        {
            jmethodID method = env->GetMethodID(cls, "getName", "()Ljava/lang/String;");
            if (method)
            {
                jstring jstr = reinterpret_cast<jstring>(env->CallObjectMethod(jar_entry, method));
                const char *name_string = env->GetStringUTFChars(jstr, 0);
                std::string result = std::string(name_string);
                env->ReleaseStringUTFChars(jstr, name_string);
                env->DeleteLocalRef(jstr);
                env->DeleteLocalRef(cls);
                return result;
            }
            env->DeleteLocalRef(cls);
        }
        return std::string();
    };

    // Load the Jar
    jobject bais = byte_array_input_stream(env, jar_data);
    if (!bais)
    {
        std::cerr<<"Failed to open ByteArrayInputStream\n";
        return false;
    }

    jobject jis = jar_input_stream(env, bais);
    if (!jis)
    {
        env->DeleteGlobalRef(bais);
        std::cerr<<"Failed to open JarInputStream\n";
        return false;
    }

    // Read the jar entries
    jobject jar_entry = nullptr;
    const std::string extension = ".class";

    while((jar_entry = get_next_jar_entry(env, jis)))
    {
        std::string name = jar_entry_get_name(env, jar_entry);
        if ((name.length() > extension.length()) && (name.rfind(extension) == name.length() - extension.length()))
        {
            jobject baos = byte_array_output_stream(env);
            if (!baos)
            {
                std::cerr<<"Failed to open ByteArrayOutputStream for: "<<name<<"\n";
                if (!ignore_exceptions)
                {
                    env->DeleteGlobalRef(jar_entry);
                    env->DeleteGlobalRef(jis);
                    env->DeleteGlobalRef(bais);
                    return false;
                }
            }

            // Define the class
            jint value = -1;
            while((value = input_stream_read(env, jis)) != -1)
            {
                output_stream_write(env, baos, value);
            }
            
            std::vector<std::uint8_t> bytes = byte_array_output_stream_to_byte_array(env, baos);
            
            std::string canonicalName = string_replace_all(string_replace_all(name, "/", "."),
                                                            ".class",
                                                            std::string());
            
            jclass cls = env->DefineClass(string_replace_all(name, ".class", "").c_str(),
                                            nullptr, reinterpret_cast<jbyte*>(bytes.data()),
                                            static_cast<jint>(bytes.size()));
            
            if (cls)
            {
                std::cerr<<"Defined: "<<canonicalName<<" Size: "<<bytes.size()<<"\n";
                env->DeleteLocalRef(cls);
            }
            else
            {
                std::cerr<<"Failed to define: "<<canonicalName<<"  Size: "<<bytes.size()<<"\n";
                if (env->ExceptionCheck())
                {
                    env->ExceptionDescribe();
                    env->ExceptionClear();

                    if (!ignore_exceptions)
                    {
                        env->DeleteGlobalRef(jar_entry);
                        env->DeleteGlobalRef(jis);
                        env->DeleteGlobalRef(bais);
                        return false;
                    }
                }
            }
        }
        else
        {
            std::cerr<<"Skipping Resource: "<<name<<"\n";
        }
        
        env->DeleteGlobalRef(jar_entry);
    }
    env->DeleteGlobalRef(jis);
    env->DeleteGlobalRef(bais);
    return true;
}

int main(int argc, const char* argv[])
{
    JVM jvm = create_java_vm();
    if (!jvm.vm)
    {
        std::cerr<<"Failed to Create JavaVM\n";
        return 0;
    }

    if (load_jar(jvm.env, some_jar_as_bytes, true))
    {
        std::cout<<"Jar loaded successfully\n";
    }
    else
    {
        std::cerr<<"Failed to load Jar\n";
    }

    jvm.vm->DestroyJavaVM();
    return 0;
}
 类似资料:
  • 我试图将一个类加载到字节数组中。MainC、Loader、ClassByte和byteClassLoader都是用来加载类的。 主C是起点。 这里我们加载类并调用方法。 在这里,我们可以获得要加载的类和字节数组。 包含字节的类加载器 下面是我想执行的类 Startclass的startmethod()是起点 和 但也有例外。 线程“main”java中的StartMethod 异常。朗。反思。在太

  • 问题内容: 我想知道如何将字节数组加载到 内存 URLClassLoader中?字节数组是jar文件的解密字节(如下所示)! 大多数内存类加载器都使用ClassLoader而不是URLClassLoader!我需要它使用URLClassLoader。 谢谢! 问题答案: 我将在这里发布我过去做过的实现: 我的自定义ClassLoader:

  • 问题内容: 给定任意实例,包括运行时生成的实例(磁盘上没有文件),是否有任何方法获取类字节? 问题答案: 通常,这是不可能的。在加载类时,JVM会解析其字节码并将其转换为内部表示形式。此后,JVM可以随意忘记原始字节码,这就是HotSpot JVM真正发生的情况。 但是,对于某些黑客,可以检查内部类表示并将其转换回有效的类文件(尽管它将与原始字节码不同)。在HotSpot JVM中,该过程用于重新

  • 问题内容: 我正在尝试使用android NDK。 有没有办法将在JNI中创建的数组(以我的情况为例)返回给Java?如果是这样,请提供一个可以执行此操作的JNI函数的简单示例。 问题答案: 如果你已经阅读了文档,但仍然有一些问题应作为最初问题的一部分。在这种情况下,示例中的JNI函数将创建多个数组。外部数组由使用JNI函数创建的“对象”数组组成。从JNI的角度来看,这就是一个二维数组,即一个包含

  • 问题内容: 我正在寻找编写一个自定义类加载器,它将通过自定义网络加载文件。最后,我要做的只是文件的字节数组。 我无法将字节数组转储到文件系统上并使用。 我的第一个计划是从流或字节数组创建对象,但它仅支持对象。 我已经写了一些使用的东西: 这对于小型文件可能很好用,但是我尝试加载包含几乎所有类的jar文件,它只是为了遍历所有条目而已,更不用说加载找到的类了。 如果有人知道一个解决方案比每次加载类都遍

  • 我正在尝试从ODT模板文件生成一个PDF,其中包含我需要填充的字段。 我想获得这个ODT模板的字节数组,它存在于我的项目的根文件夹中。我的应用程序是以这样一种方式提供字节数组来生成PDF。