I am writting an application for Android which passes a JAVA fd taken from ParcelFileDescriptor.getFd() which according to [1] states that the int I get back is a native fd.
Now, with this fd, I am trying to write it over a unix domain socket to the existing process which is listening. To do this, I am using JNI and I pass the int received above to the JNI function as an argument named fdToSend.
When my JNI code attempts to call sendmsg(), an error occurs stating "Bad file number".
With some help from google, It seems like the socket connection might be closed when I call sendmsg(), but I cannot see how this would be the case.
The sendfd() method is exactly as found in [2].
Below is my bridging JNI function:
JNIEXPORT jint JNICALL Java_com_example_myapp_AppManager_bridgeSendFd(JNIEnv *env, jint fdToSend) {
int fd;
struct sockaddr_un addr;
if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
__android_log_print(ANDROID_LOG_ERROR, APPNAME, "socket() failed: %s (socket fd = %d)\n", strerror(errno), fd);
return (jint)-1;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, "/data/data/com.example.myapp/sock_path", sizeof(addr.sun_path)-1);
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
__android_log_print(ANDROID_LOG_ERROR, APPNAME, "connect() failed: %s (fd = %d)\n", strerror(errno), fd);
return (jint)-1;
}
return (jint)sendfd(fd, (int)fdToSend);
}
[1] http://developer.android.com/reference/android/os/ParcelFileDescriptor.html#getFd()
[2] https://stackoverflow.com/a/4491203/2796346
Turns out the issue was the JNI function declaration.
The first arg is JNIEnv, the second is jclass THEN it's the values passed in from Java (i.e. for me, fdToSend).
So, my guess is that since I was using the second arg (supposed to be a jclass), then I'm probably getting a memory reference or something weird like that.
As seen here: https://stackoverflow.com/a/10743451/2796346
Related
Description of the intended goal
I'm trying to implement OpenSSL-generated public/private key pairs in Android/Kotlin using JNI, in order to implement user encryption on the information stored to the cloud server. I've compiled all OpenSSL source code and generated all .so files correcly.
The code (C++)
The C++ code to use OpenSSL is shown below. CmakeLists.txt and NDK configuration is working fine.
extern "C"
JNIEXPORT jobjectArray JNICALL
Java_eu_joober_ui_entry_SplashFragment_generateRSAKeyPair(JNIEnv *env, jobject thiz) {
int ret = 0;
RSA *r = nullptr;
BIGNUM *bne = nullptr;
BIO *bp_public = nullptr, *bp_private = nullptr;
int bits = 2048;
unsigned long e = RSA_F4;
jstring public_key_text;
jstring private_key_text;
jobjectArray returnPair = env->NewObjectArray(2, env->FindClass("java/lang/String"),nullptr);
// 1. generate rsa key
bne = BN_new();
ret = BN_set_word(bne,e);
if(ret != 1){
goto free_all;
}
r = RSA_new();
ret = RSA_generate_key_ex(r, bits, bne, nullptr);
if(ret != 1){
goto free_all;
}
// 2. get public key
bp_public = BIO_new(BIO_s_mem());
ret = PEM_write_bio_RSAPublicKey(bp_public, r);
BIO_get_mem_data(bp_public, &public_key_text);
if(ret != 1){
goto free_all;
}
// 3. get private key
bp_private = BIO_new(BIO_s_mem());
ret = PEM_write_bio_RSAPrivateKey(bp_private, r, nullptr, nullptr, 0, nullptr, nullptr);
BIO_get_mem_data(bp_private, &private_key_text);
// Check public and private keys were generated correctly
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "Public key is: \n%s",public_key_text);
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "Private key is: \n%s",private_key_text);
// 4. free
free_all:
BIO_free_all(bp_public);
BIO_free_all(bp_private);
RSA_free(r);
BN_free(bne);
// 5. Return strings using jobjectArray
if (ret == 1) {
env->SetObjectArrayElement(returnPair, 0, public_key_text);
env->SetObjectArrayElement(returnPair, 1, private_key_text);
return returnPair;
}
else {
return nullptr;
}
}
The error
If I check the Android Logcat, both public and private key seem to be generated correctly (as per __android_log_print output) but the app crashes with the following error when env->SetObjectArrayElement(returnPair, 0, public_key_text); is called:
JNI DETECTED ERROR IN APPLICATION: use of invalid jobject
The IDE (Android Studio) does not complain on any error, and the log suggests that key pair is being generated correctly, but I don't know why the keys are not being stored in the jobjectArray correctly. In fact, if I just simply put:
env->SetObjectArrayElement(returnPair, 0, env->NewStringUTF("Hello"));
env->SetObjectArrayElement(returnPair, 1, env->NewStringUTF("World"));
the code works completely fine, my Kotlin code gets the Strings correctly ("Hello" and "World"), and the app does not crash, which makes me think that problem is only on the C++ side.
The question
What I am doing wrong? I have checked other SO questions like JNI converting jstring to char * or jstring return in JNI program with slight modifications and combinations with no luck.
SIDE NOTE: I'm using OpenSSL implementation with C++ because Android/Kotlin code does not provide the private key generated using KeyPairGenerator.getInstance() and generatePair() (only public key can be retrieved from Keystore), which I need to be stored in a different place so that user information can be retrieved even if the app is uninstalled, as every subsequent call to generatePair() will lead to a different key pair. If you know a different approach to this problem I am more than welcome to any suggestions you may provide.
You never created a java string out of public_key_text
Try
char * public_key_text;
...
BIO_get_mem_data(bp_public, &public_key_text);
...
env->SetObjectArrayElement(returnPair, 0, env->NewStringUTF(public_key_text));
I am using JNI with Android Studio 1.5.1 targeting Android API 18 and my question is:
Q) Without using a tool or changing/modifying the Dalvik VM source code, how can I find the memory address of a Java local variable on the Dalvik Stack from native code?
For example, I try to use the following code (adapted from the Internet) to find the memory address of a Java local variable magicNumber = 0x23420023 but I am getting segmentation fault errors.
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("MyLibrary");
}
public native boolean findMagicNumber(int pid, int tid);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int magicNumber = 0x23420023 ;
int pid = android.os.Process.myPid();
int tid = android.os.Process.myTid();
findMagicNumber(pid, tid);
}
}
#include <jni.h>
#include <android/log.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include <stdlib.h>
#include "com_example_magicnumber2_MainActivity.h"
#include <unistd.h>
#include <memory.h>
#define ENOENT 2 /* No such file or directory */
#define ENOMEM 12 /* Out of memory */
#define EACCES 13 /* Permission denied */
#define EFAULT 14 /* Bad address */
#define EINVAL 22 /* Invalid argument */
jboolean validAddress(char* address)
{
if ((access(address, F_OK)==-1) && !(errno == ENOENT) && !(errno == ENAMETOOLONG))
return JNI_FALSE;
else if ((access(address, F_OK)==-1) && (errno == ENOMEM) ||
(access(address, F_OK)==-1) && (errno == EACCES) ||
(access(address, F_OK)==-1) && (errno == EFAULT) ||
(access(address, F_OK)==-1) && (errno == EINVAL))
return JNI_FALSE;
else if (address == NULL)
return JNI_FALSE;
else
return JNI_TRUE;
}
JNIEXPORT jboolean JNICALL Java_com_example_magicnumber2_MainActivity_findMagicNumber(JNIEnv *env, jobject obj, jint pid, jint tid) {
long long startaddr, endaddr, size, offset, inode;
char permissions[8], device[8], filename[200], line[250];
char *start, *end, *candidate;
int result, i = 0;
char filepath[100];
sprintf(filepath,"/proc/%d/task/%d", pid, tid);
FILE* file = fopen(filepath, "r");
jboolean found = JNI_FALSE;
while (fgets(line, sizeof(line), file) && !found) {
sscanf(line,"%llx-%llx %s %llx %s %llx", &startaddr, &endaddr, permissions, &offset, device, &inode);
start = startaddr;
end = endaddr;
mprotect( (void*)start , (end-start), PROT_READ);
candidate = memchr( start, 0x14, (end-start));
while( validAddress(candidate) && !found){
if ((validAddress(candidate[2]) && (candidate[2]== 0x23)) &&
(validAddress(candidate[3]) && (candidate[3] == 0x00)) &&
(validAddress(candidate[4]) && (candidate[4] == 0x42)) &&
(validAddress(candidate[5]) && (candidate[5] == 0x23))){
__android_log_print(ANDROID_LOG_DEBUG,"***","Location=%p WE FOUND IT!", candidate);
found = JNI_TRUE;
break;
return JNI_TRUE;
}
else if ((validAddress(candidate)) &&
validAddress(candidate=memchr(candidate+1, 0x14, (end-candidate)))){;
}
}
}
}
This is an update:
The previous code that I posted was not the latest one, here is the latest one:
The Java Code:
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("MyLibrary");
}
public native boolean findMagicNumber(int pid, int tid);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int magicNumber = 0x23420023 ;
int pid = android.os.Process.myPid();
int tid = android.os.Process.myTid();
findMagicNumber(pid, tid);
System.out.println("magicNumber = " + magicNumber );
}
}
The native Code:
JNIEXPORT jboolean JNICALL Java_com_example_magicnumber2_MainActivity_findMagicNumber(JNIEnv *env, jobject obj, jint pid, jint tid) {
long long startaddr, endaddr, size, offset, inode;
char permissions[8], device[8], filename[200], line[250];
char *start, *end, *candidate;
int result, i = 0;
char filepath[100];
sprintf(filepath,"/proc/%d/task/%d/maps", pid, tid);
FILE* file = fopen(filepath, "r");
jboolean found = JNI_FALSE;
while (fgets(line, sizeof(line), file) && !found) {
sscanf(line,"%llx-%llx %s %llx %s %llx %s", &startaddr, &endaddr, permissions, &offset, device, &inode, filename);
if (((strstr(filename, "apk#classes.dex")))==NULL) {
continue;
}
__android_log_print(ANDROID_LOG_DEBUG, "*****************", "%llx-%llx %s %llx %s %llx %s",
startaddr, endaddr, permissions, offset, device, inode, filename);
start = startaddr;
end = endaddr;
candidate = memchr( start, 0x14, (end-start));
while( candidate !=0 && !found){
if ((candidate[2]== 0x23) &&
(candidate[3] == 0x00) &&
(candidate[4] == 0x42) &&
(candidate[5] == 0x23)){
__android_log_print(ANDROID_LOG_DEBUG,"********************************************************************","WE FOUND IT at %p!!!", candidate);
found = JNI_TRUE;
break;
}
else
candidate=memchr(candidate+1, 0x14, (end-candidate));
}
}
}
This code is working and it can find the magic number but it finds it in the memory region mapped to /data/dalvik-cache/data#app#com.example.magicnumber2-1.apk#classes.dex which is not the Dalvik stack.
However, by running the above code and by looking at these two papers : paper1 (appendix B, only the egg-hunting code, I do not need to change any Dalvik code, so skip the code changing part) and paper2, we can notice the following (also to comments on fadden's remarks below):
(1) It seems that the int value magicNumber is stored in one Dalvik register. Also it seems that it is stored in the Dalvik stack and it is not on the native code stack because the int variable magicNumber is declared and assigned a value in the Java code section.
(2) According to the paper1, this answer and as evidence by running the attached latest code, we are not searching for 0x14 using the memchr function but we want to make sure we are at the beginning of a memory cell that store the int in ARM CPUs.
(3) I do not need to call the findMagicNumber function again. I just need to find the memory address of the magic number in the Dalvik stack
(4) I do not need to find nearby variables to the MagicNumber, so this is not a problem in my case.
(5) The project is targeting only Dalvik, so ART is not a problem
(6) I agree, using mprotect() is not a good idea and was not necessary in my case.
(7) If you refer to paper2, you can see that access() can be used for something that it is not designed for, checking if a virtual memory address is valid or not. I’m not using access() for any file related operations, although it was written for that purpose
(8) I do not need to change the variable. I just need the address of the variable magicNumber on the Dalvik stack programmatically without using any tool or changing the Dalvik source code
I want to know which of the memory regions of /proc/pid/task/tid/maps Dalvik uses to store its stack.
As you can see in paper1, the authors in section B.2, line #4, did not explain which memory region from procs/maps they used to assign the start and end variables their values.
It looks like you're trying to open /proc/[pid]/task/[tid]/maps, walk through the map, and manually scan through every address range for a magic number. Setting aside the fact that you're opening the task directory rather than the maps magic file in that directory, this approach has a few problems:
You're not guaranteed that the value is unique. If it appears somewhere else in memory -- perhaps because the value was stored in two different Dalvik registers -- you'll be in the wrong place. If the JIT compiler has compiled this stretch of code, you don't know if the "active" value will be on the managed stack or in a spilled register on the native stack.
You're searching for 0x14, which isn't part of your magic number.
You're scanning for the local variable on the thread that created it. Even if you find it and can change it, once the findMagicNumber method returns, the stack-allocated variable will disappear. It's not guaranteed to be in the same place if the method is called again.
If you were hoping to find related variables nearby, you will again have trouble if the JIT compiler rearranges things.
ART compiles almost everything ahead of time, so this is even less likely to be useful there.
I'm also not sure why you're calling mprotect(), rather than simply skipping the segments that aren't readable. You're forcing the permissions to read-only, disabling write and execute permission, which will cause you to crash when execute permission is disabled for the chunk of code that's executing, or when a thread tries to execute and touches its stack.
The access() system call takes a filename, not a memory address, and reports file permissions. I don't think there's an "is this address valid" call, but since you just sscanf'ed the map entry you shouldn't need one.
The only reliable way to find and change the value of a local variable is to use the JDWP debug interface. The compiler and debugger support work together to make reliable read-write access to local variables possible.
To sum up: the code is seriously broken, the approach is unsound. What problem are you trying to solve?
I've implemented a C native function with for android NDK, to send UDP packets.
I have a working receiver but i seem to get nothing when i use the sender.
How can i get more info from the return value of sendto? I'm having a hard time debugging native functions - no "debug step mode"
can anyone see anything wrong about the sender code? is there something im not doing right?
Thanks!
jstring
Java_com_example_adhocktest_SenderUDP_SendUdpJNI( JNIEnv* env,
jobject thiz, jstring ip, jstring message)
{
int PORT = 8888;
int i;
int sock_fd;
char *_ip = (*env)->GetStringUTFChars(env, ip, 0);
char *send_buf = (*env)->GetStringUTFChars(env, message, 0);
////////////////
//////// create socket
////////////////
if (( sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
return (*env)->NewStringUTF(env,"Cannot create socket");
}
struct sockaddr_in myaddr;
////////////////
//////// send
////////////////
struct sockaddr_in servaddr;
memset((char*)&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = 8888;
if ((inet_aton(_ip,&servaddr.sin_addr)) == 0) {
return (*env)->NewStringUTF(env,"Cannot decode IP address");
}
int retval = sendto(sock_fd, send_buf, strlen(send_buf), 0, (struct sockaddr*)&servaddr, sizeof(servaddr));
close(sock_fd);
char str[100];
if ( retval < 0) {
sprintf(str, "sendto failed with %d", retval);
} else {
sprintf(str, "sendto success with %d", retval);
}
return (*env)->NewStringUTF(env,str);
}
The problem in the code was this line
servaddr.sin_port = 8888;
the correct line would be
servaddr.sin_port = htons(8888);
the reason for this was explained here by
bornruffians:
" htons() stands for "host-to-network short". On a given platform
(called the host), it converts the endianness of a short (16-bit
generally) integer to the endianness required for sending on the
network (generally big endian).
sendto() returns the number of bytes sent. You should check that
retval is the string length of your 'send_buf' variable, not just a
positive value."
Thanks all for your help,
Ben.
If it's still not working, did you give your app the necessary permissions for manipulating sockets?
<uses-permission android:name="android.permission.INTERNET" />
htons() stands for "host-to-network short". On a given platform (called the host), it converts the endianness of a short (16-bit generally) integer to the endianness required for sending on the network (generally big endian).
sendto() returns the number of bytes sent. You should check that retval is the string length of your 'send_buf' variable, not just a positive value.
I write the Android application using libusb-1.0.9 and NDK (Android 4.0.4+) which has to read out audio-data from the USB-audiocard. The USB device from libusb opens successfully, and for it it is possible to receive all interfaces/EndPoint list. But when making ISOC transfer I faced with following unclear me error:
Debugging C code of making transfer:
static uint8_t buf[12];
static void cb_xfr(struct libusb_transfer *xfr)
{
LOGD("USB callback\n");
libusb_free_transfer(xfr);
}
JNIEXPORT jlong JNICALL
Java_com_usbtest_libusb_Libusb_makeISOCTransfer(JNIEnv *env, jobject this, jlong ms)
{
static struct libusb_transfer *xfr;
int num_iso_pack = 1;
unsigned char ep = 0x84;
xfr = libusb_alloc_transfer(num_iso_pack);
if (!xfr) {
LOGD("libusb_alloc_transfer: errno=%d\n", errno);
return -1000;
}
LOGD("libusb_fill_iso_transfer: ep=%x, buf=%d, num iso=%d\n", ep, sizeof(buf), num_iso_pack);
libusb_fill_iso_transfer(xfr, handle, ep, buf, sizeof(buf), num_iso_pack, cb_xfr, NULL, 0);
libusb_set_iso_packet_lengths(xfr, sizeof(buf)/num_iso_pack);
int res = libusb_submit_transfer(xfr);
LOGD("libusb_submit_transfer: %d, errno=%d\n", res, errno);
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
libusb_handle_events_timeout(NULL, &tv);
return res;
}
After a call of libusb_submit_transfer I received a mistake: libusb_submit_transfer: -1, errno -2
And text messages:
need 1 32k URBs for transfer
first URB failed, easy peasy
EndPoint 0x84 is true for audio-in. The buf[] size = 12 - corresponds to the field wMaxPacketSize of this EndPoint. I request 1 transfer. Field of xfr->status = 0, but callback isn't caused.
I thought that it is necessary to increase the buf buffer to 32K - I increased, but it didn't help.
I tried to increase quantity of transfers. Same error.
Prompt me please in what there can be a error?
I found the solution of this problem.
In this topic http://en.it-usenet.org/thread/14995/14985/ there was a similar question, and the decision appeared to make detach for device HID USB interfaces.
I've used libusb_detach_kernel to detach the 2 hid drivers and submit
transfer returned 0!!
I made libusb_detach_kernel_driver(handle, interface) for all interfaces of the device (0..3) after device opening, and as a result of libusb_submit_transfer return SUCCESS of transfer.
I am working on a jni-client-software, which should communicate with a server. I can establish the connection, can read out the information I need and give it back to my java programm. Now I want to to the connection infinite, that means the connection is established and the information should be read out in a infinite loop (I don't want to disconnect and reconnect with every jni-function call). Is it possible to pass a byte array from the working jni tread to a my java programm?
Thank you very much.
Kind regards
Thomas
"Is it possible to pass a byte array from the working jni tread to a my java programm?"
you can create static method in one of your java classes, and then call this method with parameters from within jni code. Here is some code:
java side:
package com.mysuper.game;
public class MyApp {
public static void callMeFromJNI(byte[] data) {
// ...
}
}
and c++ code run on worker thread :
JavaVM *vm;
// use vm->AttachCurrentThread(&env, 0); in thread function to get valid JNI interface pointer, on thread end use DetachCurrentThread().
JNIEnv *env;
void myFunc() {
// some test data to send
const int len = 32;
char data[len] = {0,1,2,3,4};
jclass app = env->FindClass("com/mysuper/game/MyApp");
jmethodID sendDataToJava = env->GetStaticMethodID(app, "callMeFromJNI", "([B)V");
jbyteArray bArray = env->NewByteArray(len);
char *bytes = (char *)env->GetByteArrayElements(bArray, 0);
memcpy( bytes, data, len);
env->ReleaseByteArrayElements(bArray, bytes, JNI_ABORT);
env->CallStaticVoidMethod(app, sendDataToJava, bArray);
}
for more on how this works look into:
Java Native Interface 6.0 Specification