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.
Related
I am using TextureView and Vulkan to draw 3D models. I get the surface from the TextureView and pass it to the native side to render.
I need to support multiple TextureView to render different 3D models simultaneously, but I find out that my native .so library is only loaded once per process, suitable for memory saving.
So I think I need a global std::map in jni to hold different c++ objects. Every TextureView has an id and could use its id to find its c++ objects.
I was planning to code like this.
#include <jni.h>
#include <string>
#include <map>
#include <android/native_window_jni.h>
#include <android/asset_manager_jni.h>
std::unique_ptr<std::map<jint, MiuiVkWidgetApp*>> mVkWidgetAppMapUniquePtr;
extern "C" JNIEXPORT jboolean JNICALL
Java_com_miui_vkwidget_MiuiVkTextureView_nativeInitVkWidgetApp(JNIEnv* env, jobject /* this */, jint id) {
...
if (!mVkWidgetAppMapUniquePtr) {
mVkWidgetAppMapUniquePtr.reset(new std::map<jint, MiuiVkWidgetApp*>());
}
...
return true;
}
extern "C" JNIEXPORT void JNICALL
Java_com_miui_vkwidget_MiuiVkTextureView_nativeDestroyVkWidgetApp(JNIEnv* /* env */,
jobject /* this */, jint id) {
mVkWidgetAppMapUniquePtr.reset(nullptr);
...
}
But in https://developer.android.com/training/articles/perf-jni#local-and-global-references "Local and global reference," it says that "The only way to get non-local references is via the functions NewGlobalRef and NewWeakGlobalRef." But NewGlobalRef seems only to support java objects.
I assume how I create the global map may have some potential risks, such as memory leak(I call DestroyVkWidgetApp when TextureView is onDestroy)? I am curious on how to create a proper global map in jni to hold multiple c++ objects? Thanks!
JNI GlobalRefs refers to Java VM objects: those objects will persists in memory until a "destroy" method is called and Java could refers to them (if needed) in that period. Without GlobalRefs that object's memory could be freed early (by JVM GarbageCollector) and Java will FC due to memory issue.
It's seems that your JVM spawns multiple Processes, so if second process is spawn from the first one, yes you could use a "Mapped Memory File" starting from JavaVersion.VERSION_1_4 (JNI part could be copied from here: https://www.codeproject.com/Articles/2460/Using-Memory-Mapped-Files-and-JNI-to-communicate-b even if you don't need the Java one).
If processes are spawned from different Apps, I'm not sure but I think my solution will work.
(A "global map" is GLOBAL inside the same JVM (one Process per JVM), so even if you use a "global map" it will not be "seen" from the second Process but this last one will create its own "global map")
From Scudo page:
Some parameters of the allocator can be defined on a per-process basis through several ways:
Statically: Define a __scudo_default_options function in the program that returns the options string to be parsed. This function must have the following prototype: extern "C" const char *__scudo_default_options().
Dynamically: Use the environment variable SCUDO_OPTIONS containing the options string to be parsed. Options defined this way override any definition made through __scudo_default_options.
Im fairly new to Android, could someone help me understand where to put functions like __scudo_default_options in cpp code, I have MainActivity.cpp and MidiManager.cpp
The function can be located anywhere within the application, as long as it's discoverable by the linker. Putting it in the vicinity of your main compilation unit is probably better.
You want to make sure that the symbol has a public visibility.
You can refer to the example in the tests here:
https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/scudo/standalone/tests/scudo_unit_test_main.cpp#L23
eg. in MainActivity.cpp, something to the extent of:
extern "C" __attribute__((visibility("default"))) const char *
__scudo_default_options() {
return "quarantine_size_kb=256:thread_local_quarantine_size_kb=128:"
"quarantine_max_chunk_size=512";
}
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.
I have written a shared library which is accessed via my linux-system and via a JNI-call from java.
That library should have a global ringbuffer which must be available in the JNI-method and in the other native methods.
I thought this won't be a problem, because when I access the SL from different programs, the global variables are always as they should.
But now, in my JNI-method, the global variables seem to be not initialized (they should as the program-flow forces it).
Here is my example:
ringbuf_t ringbuffer;
void internalMethod() {
// this method is first called from system-program
ringbuffer = ringbuf_new(5000);
}
JNIEXPORT jint JNICALL Java_example_read(JNIEnv *env, jobject This) {
// this method is later called via JNI
if (!ringbuffer) {
LOGI("uhhh, why is that buffer not set?!");
}
}
What do I have to do to make the ringbuffer-variable really global so every instance/call to the shared-library access one and the same instance of that variable?
From the comments it seems you want to allocate your memory in a process and use it in another.
For this purpose it might be a good idea to look at shared memory: 'man shmget' should be a good start.
Note that this is not specifically related to JNI and is a restriction from the OS.
EDIT:
I would suggest you:
read up on shared memory - you should understand the concepts of how this works.
first try sharing memory between 2 simple applications
only then implement in your JNI application
I am writing the C++/Native side of a text/VoIP program that utilizes JNI. Currently there are three functions on the C++ side that will likely be called by the Java code regularly. The frequency at which the functions are called will depend on how often the user sends/receives text messages and how active their friends are in changing their presence/status. Each of the three functions follow the pseudo code below and I am not sure if it would be wise (or even possible) to "globalize" anything within the functions.
JNIEnv *env;
if (jvm_->GetEnv((void**) &env, JNI_VERSION_1_6) < 0)
{
[print error and return]
}
jclass stringclass = env->FindClass("java/lang/String"); // Same for all 3
jstring passinfo = env->NewStringUTF([str-info-to-pass]); // Different for all 3
jclass cls = env->FindClass([directory to calling Java function class]); // Same for all 3
[check if cls found, print error if not found and return]
jmethodID methID = env->GetStatisMethodID([arguments for the function]); // Different for all 3
[check if methID found, print error if not found and return]
jobjectArray args = env->NewObjectArray([arguments to build argument structure being passed to function]);
[call Java function, passing right arguments]
I was thinking that I should/would be able to move the jclass stringclass, jclass cls and JNIEnv *env out of the functions and make them globals that were set during the JNI_OnLoad function call. Is this possible/advisable? Will these values be somehow different from the OnLoad function to these functions? Should I just instantiate them as globals and still set them each time in the function call? And, will doing any of these really increase performance in any noticeable amount?
Possible, yes. Wise, it depends. Define for yourself what is a "noticeable amount". What cadence of JNI calls you expect? I think you could see some measurable impact of caching starting at 10+ calls per second. Is that a case? Anyway, in my personal experience, nicely done caching cleans up the code, makes it shorter and more readable. So i will give some advice anyway.
Caching results of FindClass, yes absolutely. Generally anything reusable and "static", what you are looking up by a symbolic name - which introduces a penalty of symbolic lookup, contrary to simply using an already cached pointer. Liang JNI book talks also about field and method ids, so your GetStaticMethodID applies. jmethodID is simpler in the sense that the result of Get(Static)MethodID can be directly cached: it is valid for any further calls. Unlike FindClass which returns a local reference, so for valid caching (and to make jmethodID really persistent) you must create NewGlobalRef from it - and delete it when appropriate. A nice comprehensive example is found in the JNI_OnLoad tutorial. On the other hand, there is no point in caching JNIEnv, you get it again with every JNI call. Cached JNIEnv would be also invalid if you happen to call the JNI methods from another thread than the one from which you cached.
You should not worry about performance.
Currently I develop a voip client where i used native codec. This means that the JNI is called for every rtp packet (two times in every 20 msec because both encoding and decoding) also passing the JNIEnv with each call. This variable is just a pointer. Consumed CPU usage for JNI calls are below 1% on a low end device for me in these circumstances so you should not worry at all if your only need is texting.