1

Java Code:

public class MainClass 
{       
    static
    {
        System.load("/home/chronic/workspace/ramRead/src/libRamRead.so");
    }
    private native String readRam(int len, long addr, int pid);
    private native int readSize();

    public static void main(String[] args) 
    {
        MainClass app = new MainClass();


        String x = app.readRam(4096, 0x55c5520b5000L, 4435);

        //System.out.println("HEY THERE");
        //  System.out.println("I BEGIN HERE");
        System.out.println(x.length());

        //int test = app.readSize();
        //System.out.println(test);
    }
}

This is my native function (C++)

JNIEXPORT jstring JNICALL Java_ramRead_MainClass_readRam
  (JNIEnv *env, jobject, jint len, jlong addr, jint pid)
{
        struct iovec local[1];
        struct iovec remote[1];
        char buf[len];
        jstring result;
        //jcharArray buf = (*env).NewCharArray(len);
        //jcharArray buf1 = (*env).NewCharArray(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);

        //printf("len %d, pid %d, addr %li, buf %c, size of buf %d ", len, pid, addr, buf, sizeof(buf));
        printf("\nTHIS MUCH: %d\n", nread);

        for(int i=0; i<4096; i++){
                printf("%c", buf[i]);
        }

        //(*env).SetCharArrayRegion(buf1, 0, len, buf);
 result = (*env).NewStringUTF(buf);
        //printf("RESULT: %s", (*env).GetStringUTFLength(result));

        return result;
}

My goal:

Have the native function in C++ read the contents of ram and then return it as a string or as a char array to java for further processing.

Problem:

The return value of the jstring result is not what I expected it to be in java, there are several problems, first of all it has a length of 7 instead of 4096 as was intended. Antoher problem is the output is nonsensical. I have another program written in C++ which basically reads the contents of the RAM and then just prints them to a console. Here is the code bellow it works like a charm.I know from which address to read and I know what result to expect, and it does work without any problems so long as I am using C++ but as soon as I use JNI with the same methods before mentioned problems occur.

    #include <jni.h>
#include "ramRead_MainClass.h"
#include <sys/uio.h>

static ssize_t nread;

JNIEXPORT jbyteArray JNICALL Java_ramRead_MainClass_readRam
  (JNIEnv *env, jobject, jint len, jlong addr, jint pid)
//JNIEXPORT jbyteArray JNICALL Java_ramRead_MainClass_readRam
  //(JNIEnv *env, jobject, jobject len, jobject addr, jint pid)
{
        struct iovec local[1];
        struct iovec remote[1];
        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;
}

JNIEXPORT jint JNICALL Java_ramRead_MainClass_readSize
  (JNIEnv *, jobject)
{
        return nread;
}

What I have tried thus far:

Check list 1. Not a permission problem 2. Ram address is correct and I know what is stored there. How do I know? I am reading RAM of a calculator, and I am looking for a specific set of numbers that I type in. With C++ I find them, but with jni not really. 3. Read more about UTF_8 and UTF_16 (there is a good chance the problem is here) 4. Seen posts here tried to reproduce the solutions, but to no avail.

OS: Linux 64 bit Fedora

Question:

How to convert a C++ string to a jstring and then return it in java and print it out so that the output would match the output of the C++ program listed above has.

Anyway this is how I intended to go about solving it, but if anyone has any better solutions or just functional solutions, I will more then gladly accept them.

Then you all for your time and effort.

ChronicUser
  • 67
  • 2
  • 12
  • Possible duplicate of [jstring return in JNI program](https://stackoverflow.com/questions/13796786/jstring-return-in-jni-program) – Michael Jun 27 '17 at 14:21
  • 2
    Is this actually a string in modified UTF-8 encoding, or are you trying to retrieve a block of data? In the latter case, using a string is not a good idea. It's much simpler to use a direct `ByteBuffer`. – Jorn Vernee Jun 27 '17 at 14:34
  • Your printf from Java_ramRead_MainClass_readRam - does it show you the expected characters? Are all 4096 characters printable? – Alex Cohn Jun 27 '17 at 15:45
  • I am trying to retrieve a block of data. printf("%c", buf[i]); does not print all the chars only 7 in this particular case. When I run the same code from my C++ program it gets all the expected chars no problems. – ChronicUser Jun 28 '17 at 06:33
  • I have take a look at [link]https://stackoverflow.com/questions/13796786/jstring-return-in-jni-program[/link] did not quite manage to make it work. I ll try a few more things today with the byte array as suggested. – ChronicUser Jun 28 '17 at 06:50

2 Answers2

0

JNI stores strings in modified utf-8 encoding (see bottom of the page).

If you want to keep using String, then you have to convert your bytes into this modified utf-8 encoding (so byte values >127 and zero have to be converted into 2 bytes).

But as Jorn suggested in the comments, you'll want to use ByteBuffer or a simple byte array (byte[]) instead, as it is more optimal: with String, you waste memory, as it contains 16-bit elements.

geza
  • 28,403
  • 6
  • 61
  • 135
  • Okay so what you are suggesting is to do away with String and go for a byte array instead, okay I am fine with that I ll try it, but then I am guessing when I return that byte array back to Java I ll need to convert it to a string or do something similar as the data needs to be printed out in a human readable format. Am I correct in stating this? – ChronicUser Jun 28 '17 at 06:45
  • Okay so I do believe that I have managed to get the data in a form of a byteArray back to java, and I print out the byte Array and get the size of it. So that is great seams to be okay, but I still need to convert it to a String in java or a char Array, to something human readable. However when I try to convert the byte Array to String with String s = new String(x, "UTF-8"); I do not get what I want, I know that the conversion is not working, because when I print the byte Array in java and try converting it on paper with ASCII I am getting different results. – ChronicUser Jun 28 '17 at 08:20
  • @ChronicUser: first, print out byte array as integers at java and JNI side (use "%d" instead of "%c") to check that the byte array is the same. Then, if you want characters, then you should know the character encoding of your data. But I guess that `process_vm_readv` returns a memory dump without any specific encoding. How would you want to print out a non-ASCII character? Or a control-char (char codes below 32)? – geza Jun 28 '17 at 09:09
  • This is the return of the C++ program so no JNI just of the C++ code listed above ELF> @�@8 @@@@�888 Table__gmon_start___Jv_RegisterClasseskdemainlibKF5Notifications.so. We ll this is just the part of it, and that is what I would like to get in java via JNI as well – ChronicUser Jun 28 '17 at 09:39
  • How to know which encoding is used? I really have no idea, I specified in the original question that this was the most likely cause of the problem. As I do not know the initial encoding and plus on top of that it is different for java and C++, if you have any idea how to achieve this I would be very grateful. – ChronicUser Jun 28 '17 at 09:41
  • Try `System.out.write(byteArray, 0, byteArray.length)` – geza Jun 28 '17 at 09:46
  • thank you for your help I posted an answer down bellow with the solution. Hopefully others will find it useful. I will up vote your answer as well. – ChronicUser Jun 28 '17 at 10:28
-1

I managed to get it to work but it took some tinkering. Here is the code bellow which can be combined with the original code I have posted Many thx to all, I will try to up vote the answers.

In the main inside of Java code simply create this to convert the byte array into human readable chars. Keynote, not all chars will be properly converted but as far as my tests go all special chars such as !"£$%^&*()_+=-{}[]@~:;'#<>?,./ will be printed out no problems plus letters and numbers A-Z a-z 0-9 anything else if you have some letters outside of English alphabet or something like that, you're likely to have problems.

public static void main(String[] args){
        MainClass app = new MainClass();

            byte[] x = app.readRam(4096, 0x556cbdb2a000L, 22444);

            System.out.println(x.length);
            System.out.write(x, 0, x.length);

            for(int i=0; i<x.length; i++)
            {   
                if((int)x[i] >= 32 && (int)x[i] < 127)
                {
                    System.out.print((char)x[i]);
                }
            }

            char c = 120;
            System.out.println("\n" + c);

    }
ChronicUser
  • 67
  • 2
  • 12
  • It owuld have been a million times simpler to return a `byte[]` or a `ByteBuffer`. `String` is not a container for binary data. – user207421 Jun 28 '17 at 10:32
  • @EJP In the final code it does return a byte just need to edit the original post my apologies. – ChronicUser Jun 28 '17 at 12:53