I am trying to use the android AccountManager from qt c++ code. To add a account, I want to create an instance of android.accounts.Account, I am trying to do this with this code:
jstring jUsername = QAndroidJniObject::fromString(username).object<jstring>();
jstring jPassword = QAndroidJniObject::fromString(password).object<jstring>();
jstring jType = QAndroidJniObject::fromString(type).object<jstring>();
qDebug()<<"Creating";
QAndroidJniObject accountObject("android.accounts.Account","(Ljava/lang/String;Ljava/lang/String;)V",jUsername,jType);
qDebug()<<"Inserting";
The code segfaults at the line, where the accountObject is created ("Creating" is printed, "Inserting" not):
JNI ERROR (app bug): accessed deleted global reference 0x100e46
JNI ERROR (app bug): accessed deleted global reference 0xe46
I read this occurs, if I call a method with a wrong signature, but the signature is right (see here).
By the looks of it, the way you're creating your strings are causing your problem.
jstring jUsername = QAndroidJniObject::fromString(username).object<jstring>();
What this does is create an anonymous temporary QAndroidJniObject (returned by fromString), which you then extract the wrapped jobject from (and cast it to a jstring). By the time execution of that statement finishes the lifetime of that QAndroidJniObject is over, and the reference it held to the wrapped jobject will be released.
You could change your code to somethine like this:
auto qjUsername = QAndroidJniObject::fromString(username);
auto jUsername = qjUsername.object<jstring>();
Or to:
jstring jUsername = env->NewLocalRef(QAndroidJniObject::fromString(username).object<jstring>());
Assuming that you have a way of getting the JNIEnv*.
If you create a new reference you should probably also delete it with DeleteLocalRef when you don't need it anymore.
Related
We can write methods to get the key as below both Method 1 and Method 2 for the Android app
Are both Methods have the same security strength? or is there any security weakness in one of the below Methods while decompile/reverse engineering or other app cracking procedures?
Method 1
declare/assign key inside the method in the lib.cpp file
lib.cpp file
extern "C"
JNIEXPORT jstring JNICALL
Java_com_app_keytest_KeyHelper_getKey(JNIEnv *env, jobject) {
std::string API_KEY = "YOUR_API_KEY";
return env->NewStringUTF(API_KEY.c_str());
}
Method 2
declare/assign keys in separate keys.h file in the same directory with lib.cpp and import to lib.cpp file
keys.h file
std::string API_KEY = "YOUR_API_KEY";
lib.cpp file
#include "keys.h"
extern "C"
JNIEXPORT jstring JNICALL
Java_com_app_keytest_KeyHelper_getKey(JNIEnv *env, jobject) {
return env->NewStringUTF(API_KEY.c_str());
}
Your two snippets compile to nearly the exact same code. In both cases the API key is present in plain text in the compiled library. Even if you took pains to obfuscate the native code, an attacker can just attach a debugger and catch the return value of getKey.
You need to rethink your approach and decide if it is actually worth the effort on your part.
You can achieve a good result in another way:
you have to obfuscate JNI function name because "getKey()" is too much self-explained and easy to understand during reverse engineering
add unused parameters to "getKey()" to make it more complex when scrolling while viewing reversed code
getKey() haven't to return the Key but have to call a Java method or set a specific Java variable
About #3: I'm using a dedicated Thread on JNI to receive a "command" and a "callback" for its results:
Java calls JNI's "getKey(fakeArg1, fakeArg2, fn_callback, fakeArg3)"
getKey() sends a request to JNI dedicated Thread and pass even "fn_callback" as part of that request
the dedicated Thread process the request and then call Java Callback
In this way event during normal debugging it's very difficult to follow the Flow because debugging won't go inside dedicated Thread automatically using StepInto/Over key/button.
Update: callback is the first approach but create a link between caller and the result. To avoid this you could call a completly separated Java method from JNI to pass the variable to.
I am facing crash with
JNI WARNING : 0x44f81e80 is not a valid JNI reference, in Ldalvik/system/NativeStart;. run()v (GetObjectClass)
I found some post saying use NewWeakGlobalRef instead NewGlobalRef. But I am looking for validation in my code. All I want to know is is there any JNI api using which I can check whether given jobject is valid JNI reference or not? Something like unsigned
char isValid = (env)IsValidJNIRef(myjobject);
Using that isValid's value I can put some condition to handle system.
Thanks.
You can check the ref type of a jobject referred to by the obj argument using:
jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj);
Which returns one of the following enumerated values defined as a jobjectRefType:
JNIInvalidRefType = 0,
JNILocalRefType = 1,
JNIGlobalRefType = 2,
JNIWeakGlobalRefType = 3
Note that you can't use this on deleted refs - it is not specified what value the GetObjectRefType will return for a deleted ref.
See: http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#objreftype
My application is an Android app with some native code.
The Java code starts a thread via new Thread(new Runnable), in the native code, I attach that thread to the JVM via AttachCurrentThread.
The native function which is called in that thread is an endless loop which will listen for simple events(boolean variables) and call another native function which by using the proper JNIEnv pointer and jobject will fill 2 java variables on the Java side and call a void method to display the data. The reason for that loop is that the overhead to calling the function normally(has a lot of allocation and destruction to do) is most likely huge.
The only problem is that while I can renew a JNIEnv pointer, I have no idea how to do it for a jobject.
And by jobject I mean the jobject which is passed to the native code via the function call like so
jint Java_com_example_example_MainActivity_NativeFunc( JNIEnv* env,
jobject obj)
{
return;
}
The Java code starts a thread via new Thread(new Runnable), in the native code, I attach that thread to the JVM via AttachCurrentThread.
You don't have to attach the JVM to a thread that was created with Java code, it is already attached.
If I understood your question correctly you get a jobject once and want to access that object later in other native methods. To accomplish that, you can use NewGlobalRef:
jobject myGlobalReference = env->NewGlobalRef(obj);
You can store that jobject in a global C/C++ variable and use it later. It will stay valid until the JVM dies or you delete the reference with DeleteGlobalRef. Keep in mind that the garbage collection won't collect the referenced object if all Java references to it are gone as long as you don't delete your global reference!
When I run the code, I get an error "failed adding to JNI local ref table has 512 entries"
This is my code:
jstring pJNIData = pJNIEnv->NewStringUTF ( variables[0].GetStringValue() );
pJNIEnv->CallStaticVoidMethod ( pJNIActivityClass, pJNIMethodIDStartTime, pJNIData ) ;
pJNIEnv->DeleteLocalRef(pJNIData);
I have read several suggestions, but none of them work! In spite of the DeleteLocalRef, it fails to works. The function is used in a profiler that literally calls all the functions...
I have seen this when a JNI method called Java code (in my case, the method was not static). As I understand, unused local references are not automatically deleted when a Java method is called from JNI (I mean, until the top-level JNI function returns).
IIRC either there already was information about memory objects in the log, or I could add some logging; from that information I identified garbage items that I did not mention before. They were two arrays and a class, created in subsequent calls but not garbage-collected.
// in a function that calls a Java method from JNI
jbyteArray srcArray = env->NewByteArray(len);
jclass cls = env->FindClass("com/something/MyClass");
jmethodID mid = env->GetMethodID(cls, "mymethod", "([BI)[B");
jbyteArray resArray = (jbyteArray)env->CallObjectMethod(obj, mid, srcArray, XXXX);
...
env->DeleteLocalRef(cls);
env->DeleteLocalRef(resArray);
env->DeleteLocalRef(srcArray);
// no need to do anything with mid
Note that although these three local references were obtained differently, all of them were hanging around.
Useful link:
http://www.netmite.com/android/mydroid/dalvik/docs/jni-tips.html#local_vs_global_references
(or find the Dalvik VM docs dalvik/docs/jni-tips.html and locate the section "Local vs. Global References")
Every object that JNI returns is a "local reference". This means that it's valid for the duration of the current native method in the current thread. Even if the object itself continues to live on after the native method returns, the reference is not valid. This applies to all sub-classes of jobject, including jclass and jarray. [...] Note: method and field IDs are just 32-bit identifiers, not object references, and should not be passed to NewGlobalRef. The raw data pointers returned by functions like GetStringUTFChars and GetByteArrayElements are also not objects.
I thought I would chip in just in case anyone else runs into this issue. This is a weird case that kept me confused for hours!
Ok so I have an NDK app and the Java code being called is inside an apk that is loaded at runtime. I have no idea if the runtime loading effects this in any way but I thought I should mention it.
Now in a c++ method I use find class and getmethodid to get the constuctor to a HashMap and call it to get a new HashMap instance. I then populate the HashMap from the c++ side using jni calls. So far so good.
I then pass the HashMap to java code and, again, all is working as expected. Once the java code has returned I call DeleteLocalRef on the HashMap. No errors are thrown but the reference is not deleted.
This only came up when I finally ran over 512 local references (from multiple calls to this function) and the error dump showed that the last 10 items in the localref store were nearly all HashMaps. I would understand that the GC would not collect these references at the end of the method as I am make a multithreaded ndk app. However the DeleteLocalRef should have worked.
The Fix:
In the end I found that creating the HashMap from a jni call to a java method I wrote was fine, and the reference was then free'able. It seems crazy to have a java function that literally just returns a new HashMap but it worked so for now I am living with it :)
This question has been asked and answered in many posts like this!
But how can I call from c++ directly ? For this how can I get JNIEnv* and jobject ?
Is this possible ?
To get JNIEnv you can write global JNI_OnLoad function that will get called during loading of shared library. This function will get JavaVM pointer as argument. Using it you can get JNIEnv for current thread (or create new one if there was no JNIEnv previously).
As to where get jobject - if that is new object you are instantiating, then you use JNIEnv::NewObject method. Otherwise you need to pass object on which you want to call method from java side to C/C++.
You need to read the Invocation section of the JNI Specification.