Question

I am trying to port a C program to android using JNI. I have been able to set up the program and get java and c working fine together. The problem is I need to be able to use STDIN since the C program reads input from STDIN and returns a response through STDOUT (C program is a server-client application). I don't know if it is worth mentioning but the C program uses STDIN_FILENO file descriptor for reading input from STDIN. How do I read from STDOUT and WRITE to STDIN using Java?

I did some research and found some vague explanation at the following link: https://groups.google.com/forum/#!topic/android-ndk/Brm6jPr4C0Y that I don't understand.

Here's is the C code https://github.com/unekwu/kemi/blob/master/jni/dnscat/dnscat.c#L1270

More Details

The C program is usually run from command line like dnscat --dns <ip> <port> After which it starts listening for messages from the user. Normally entered from stdin.

Now in my Android app, I'm able to run it with JNI by calling main and different name and passing ann array of strings to it. I'v verify the program starts up correcting. The problem is how I'll send the messages to the program since there's no stdin on android.

Was it helpful?

Solution

I've created a project in github, which you can download from here.

It creates 2 named pipes (FIFO), one for input and other for output.

It opens up one end of the pipe in write only mode in native code and other end of the pipe in read only mode in Java code. The file descriptor in native code is mapped to STDOUT i.e. 1, thereafter any writes to STDOUT in native code, will be redirected to other end of pipe which can read in Java code.

It opens up one end of the pipe in read only mode in native code and other end of the pipe in write only mode in Java code. The file descriptor in native code is mapped to STDIN i.e. 0, thereafter any writes to other end of pipe in Java code, will be read by native code using STDIN.

To achieve STDOUT redirection:

Native Code:

    /*
     * Step 1: Make a named pipe
     * Step 2: Open the pipe in Write only mode. Java code will open it in Read only mode.
     * Step 3: Make STDOUT i.e. 1, a duplicate of opened pipe file descriptor.
     * Step 4: Any writes from now on to STDOUT will be redirected to the the pipe and can be read by Java code.
     */
    int out = mkfifo(outfile, 0664);
    int fdo = open(outfile, O_WRONLY);
    dup2(fdo, 1);
    setbuf(stdout, NULL);
    fprintf(stdout, "This string will be written to %s", outfile);
    fprintf(stdout, "\n");
    fflush(stdout);
    close(fdo);

Java Code:

/*
 * This thread is used for reading content which will be written by native code using STDOUT.
 */

new Thread(new Runnable() {

    @Override
    public void run() {
        BufferedReader in = null;
        try {
            in = new BufferedReader(new FileReader(mOutfile));
            while(in.ready()) {
                final String str = in.readLine();
                mHandler.post(new Runnable() {

                    @Override
                    public void run() {

                        Toast.makeText(RedirectionJni.this, str, Toast.LENGTH_LONG).show();
                    }

                });
            }
            in.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}).start();

To achieve STDIN redirection:

Native Code:

    /*
     * Step 1: Make a named pipe
     * Step 2: Open the pipe in Read only mode. Java code will open it in Write only mode.
     * Step 3: Make STDIN i.e. 0, a duplicate of opened pipe file descriptor.
     * Step 4: Any reads from STDIN, will be actually read from the pipe and JAVA code will perform write operations.
     */
    int in = mkfifo(infile, 0664);
    int fdi = open(infile, O_RDONLY);
    dup2(fdi, 0);
    char buf[256] = "";
    fscanf(stdin, "%*s %99[^\n]", buf); // Use this format to read white spaces.
    close(fdi);

Java Code:

/*
 * This thread is used for writing content which will be read by native code using STDIN.
 */
new Thread(new Runnable() {

    @Override
    public void run() {
        BufferedWriter out = null;
        try {
                out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(mInfile)));
            String content = "This content is written to " + mInfile;
            out.write(content.toCharArray(), 0, content.toCharArray().length);
            out.flush();
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}).start();

Let me know if you need any help.

OTHER TIPS

In the Java Code, get a command line by

Process p;
p = Runtime.getRuntime().exec("su"); // or execute something else, su is just to get root
DataOutputStream dos = new DataOutputStream(p.getOutputStream());
dos.writeBytes(ForExampleABinaryPath+" &\n");
dos.flush();

// to read (might has to run parallel in different thread)
InputStream is = p.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
is.readLine() // will get you a line o null

using write && flush and read in parallel, you might be able to achieve your goals. good luck

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top