4

This is how i am keeping strings in JNI

extern "C" JNIEXPORT jstring JNICALL
Java_com_galaxy_mars_moon_MainActivity_getDefaultNegative(JNIEnv* env, jobject)
{
std::string defaultNegative = "Cancel";
return env->NewStringUTF(defaultNegative.c_str());
}

And i call this native function from java class.

My Questions are :

  1. Would it be able to decompiling person to identify?
  2. I uses Proguard too, Does proguard will make obfuscate code from it?
  3. Will it be converted to .so file and will it be readable to decompiling person?

Thank you in advance. I hope someone will throw more light on it

Rushikant Pawar
  • 428
  • 1
  • 5
  • 14
  • 2
    string in .so files can easily be read (use notepad++ with hex editor) – Hooman Jan 27 '20 at 09:25
  • @Hooman, any idea where to keep it so that decompiling persons should find it difficult? – Rushikant Pawar Jan 27 '20 at 09:54
  • I don't think there is a standard way of doing it but you can generate string on user device by some functions. it is better than plain text but generally don't count on it – Hooman Jan 27 '20 at 10:33

1 Answers1

4

You can't prevent reverse engineering but you can make it harder, involving more work and extra steps that not everyone is capable of.

Putting constants in a native library prevents them from appearing in the decompiled java code. Connecting which string in the compiled library matches which value in the final app isn't trivial. A common method involves just exporting all the strings from the native library and matching them by context - for example, looking for things that look like API keys. If you encrypt these constants and decrypt them in the app itself that adds another step to reverse engineering, although since the encryption keys are in the app an attacker can certainly do that themselves eventually.

To quickly and easily encrypt values in the NDK consider a basic salsa20 or chacha20 implementation - for obfuscation purposes no authentication is needed and the keys can be hard-coded.

As an example, using this simple Salsa20 C implementation:

// hard-coded 128-bit key, can be anything
uint8_t k[32] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };

// hard-coded nonce, can be anything
uint8_t n[8] = { 101, 102, 103, 104, 105, 106, 107, 108 };

// The s20_crypt() function encrypts/decrypts in place.

// During development, do this:
std::string myString = ...;
std::vector<uint8_t> vec(myString.begin(), myString.end());
s20_crypt(k, S20_KEYLEN_128, n, 0, vec.data(), vec.size());
// export the vector bytes somehow - you can run this on your computer and write then to a file or to stdout for example

// After you have the encrypted bytes:

Java_com_galaxy_mars_moon_MainActivity_getDefaultNegative(JNIEnv* env, jobject)
{
  uint8_t[] defaultNegative = [...]; // the encrypted values
  s20_crypt(k, S20_KEYLEN_128, n, 0, defaultNegative, sizeof(defaultNegative));
  std::string decrypted(defaultNegative, defaultNegative+sizeof(defaultNegative));
  return env->NewStringUTF(decrypted.c_str());
}
orip
  • 73,323
  • 21
  • 116
  • 148
  • Can i remove => std::string myString = ...; while releasing the app? – Rushikant Pawar Jan 27 '20 at 11:29
  • @RushikantPawar sure, you just need to calculate the encrypted value. You can do it with a command-line utility outside the android code too - I put up an example at https://gist.github.com/orip/0595f3bd3bfb959f25d03c4610d164f3 – orip Jan 27 '20 at 14:04
  • Aceepted as answer and Upvote for the efforts. It really helped. Thank you so much – Rushikant Pawar Jan 27 '20 at 16:34