简体   繁体   中英

How to access values of a jobject in JNI

JNIEXPORT jbyteArray JNICALL Java_ramRead_MainClass_readRam
        (JNIEnv *env, jobject, jobject len, jobject addr, jint pid)
{
        jclass c = (*env).GetObjectClass(len);
        jfieldID fid = (*env).GetFieldID(c, "len", "Ljava/math/BigInteger;");
        std::cout << "Object Field: " << (*env).GetObjectField(len, fid);

        struct iovec local[1];
        struct iovec remote[1];
        unsigned long long len1 = (*env).GetObjectField(len, fid) //This did not work out. the conversion to unsigned long long
        jbyte buf[len];
        jbyteArray buf1 = (*env).NewByteArray(len);

        local[0].iov_base = buf;
        local[0].iov_len = len;

        remote[0].iov_base = (void *) addr;
        remote[0].iov_len = len;

        nread = process_vm_readv(pid, local, 1, remote, 1, 0);

        (*env).SetByteArrayRegion(buf1, 0, len, buf);
        return buf1;
}

This is my native method and as you can see, it receives 3 parameters as arguments two jobject and one int . The two jobjects are big integers, Originally it revived as parameters two int and one long but as I need to pass big integers now I have a problem or should I say several of them.

Goal:

I need to get the BigInteger value from jobject len and then I need to use it to initialize the size of jbyte buf[len] , jbyteArray buf1 = (*env).NewByteArray(len) and local[0].iov_len = len; I cannot of course just initialize them with len I need to get the big integer value from len and then initialize the size with that. And then I need to do the same, with jobject addr that is extract the big integer value from it.

Any help would be appreciated.

The problem with your approach is that you're trying to access a field of BigInteger call len :

GetFieldID(c, "len", "Ljava/math/BigInteger;");

But BigInteger doesn't have such a field.

There is no way to access the "value" of a BigInteger directly. The best you can do from JNI is convert the BigInteger to a jlong (by calling the Java method longValue , on the BigInteger ) and use that.

But, there is no point in using a BigInteger if you're converting it to an jlong any ways. You would lose any precision you'd gain from using a BigInteger . So might as well use a long in Java instead (which will still fit the value 0xffffffffff601000 that you mentioned).

Furthermore, a byte[] can only be indexed with an int , so even if you make buf bigger than that, it won't be accessible from Java.

I would recommand using a direct ByteBuffer instead of a byte[] , since you won't have to copy data across buffers.

You'd get something like this:

private static native void readRam(ByteBuffer buff, long address, int pid);

And then in C++:

JNIEXPORT void JNICALL Java_Main_readRam
    (JNIEnv *env, jclass, jobject byteBuffer, jlong addr jint pid) {

    struct iovec local[1];
    struct iovec remote[1];

    void* buf = env->GetDirectBufferAddress(byteBuffer);
    jlong len = env->GetDirectBufferCapacity(byteBuffer);

    local[0].iov_base = buf;
    local[0].iov_len = len;

    remote[0].iov_base = (void *) addr;
    remote[0].iov_len = len;

    process_vm_readv(pid, local, 1, remote, 1, 0);
}

And then calling:

int bufferLength = ...;
long address = 0xffffffffff601000L;
int pid = ...;

ByteBuffer bb = ByteBuffer.allocateDirect(bufferLength);

readRam(bb, address, pid);

// use bb...

In regards to your comment, you could do this:

StringBuilder sb = new StringBuilder();
while(bb.hasRemaining()) {
    byte b = bb.get();
    if((b >= 32 && b < 127) || b == 10) {
        sb.append((char) b);
    }
}
String result = sb.toString();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM