This article is just a summary of the recent work for the convenience of future inquiries.

Disable log printing

Disable the printed logs to prevent debugging information in logs from being seen. This is especially true if logging is used in a network framework.

Code confusion

Obfuscating code is the most basic practice, and at the very least it makes it harder to read the source code when the App is decomcompiled.

Of course, even after the obfuscation of the code, as long as it takes a certain amount of time, it is still possible to clarify the logic between the codes.

Confuse the use of dictionaries

If you don’t like the idea of changing class and variable names to A, B, or C in your code, you can customize some characters to replace them. This is where the obfuscation dictionary comes in.

Recommend a lot on the library: https://github.com/ysrc/AndroidObfuseDictionary

Add obtrusion dictionary configuration in proguard-rules.pro

# Confound the dictionary
-obfuscationdictionary dic.txt
-classobfuscationdictionary dic.txt
-packageobfuscationdictionary dic.txt
Copy the code

Native layer calibration

In addition to being obfuscated, apps also need to be protected from being repackaged by others.

Due to the uniqueness of release signature, signature verification can be considered in native layer. If the signature is incorrect, the App will crash.

We override the JNI_OnLoad() function to check here.

jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) ! = JNI_OK) {return JNI_ERR;
    }
    if (verifySign(env) == JNI_OK) {
        return JNI_VERSION_1_4;
    }
    LOGE("Inconsistent signatures!");
    return JNI_ERR;
}
Copy the code

The verifySign() function performs the actual validation, comparing the signature string stored in the Native layer with the current App signature.

static int verifySign(JNIEnv *env) {
    // Application object
    jobject application = getApplication(env);
    if (application == NULL) {
        return JNI_ERR;
    }
    // Context(ContextWrapper) class
    jclass context_clz = env->GetObjectClass(application);
    // getPackageManager()
    jmethodID getPackageManager = env->GetMethodID(context_clz, "getPackageManager"."()Landroid/content/pm/PackageManager;");
    // android.content.pm.PackageManager object
    jobject package_manager = env->CallObjectMethod(application, getPackageManager);
    // PackageManager class
    jclass package_manager_clz = env->GetObjectClass(package_manager);
    // getPackageInfo()
    jmethodID getPackageInfo = env->GetMethodID(package_manager_clz, "getPackageInfo"."(Ljava/lang/String; I)Landroid/content/pm/PackageInfo;");
    // context.getPackageName()
    jmethodID getPackageName = env->GetMethodID(context_clz, "getPackageName"."()Ljava/lang/String;");
    // call getPackageName() and cast from jobject to jstring
    jstring package_name = (jstring) (env->CallObjectMethod(application, getPackageName));
    // PackageInfo object
    jobject package_info = env->CallObjectMethod(package_manager, getPackageInfo, package_name, 64);
    // class PackageInfo
    jclass package_info_clz = env->GetObjectClass(package_info);
    // field signatures
    jfieldID signatures_field = env->GetFieldID(package_info_clz, "signatures"."[Landroid/content/pm/Signature;");
    jobject signatures = env->GetObjectField(package_info, signatures_field);
    jobjectArray signatures_array = (jobjectArray) signatures;
    jobject signature0 = env->GetObjectArrayElement(signatures_array, 0);
    jclass signature_clz = env->GetObjectClass(signature0);

    jmethodID toCharsString = env->GetMethodID(signature_clz, "toCharsString"."()Ljava/lang/String;");
    // call toCharsString()
    jstring signature_str = (jstring) (env->CallObjectMethod(signature0, toCharsString));

    // release
    env->DeleteLocalRef(application);
    env->DeleteLocalRef(context_clz);
    env->DeleteLocalRef(package_manager);
    env->DeleteLocalRef(package_manager_clz);
    env->DeleteLocalRef(package_name);
    env->DeleteLocalRef(package_info);
    env->DeleteLocalRef(package_info_clz);
    env->DeleteLocalRef(signatures);
    env->DeleteLocalRef(signature0);
    env->DeleteLocalRef(signature_clz);

    const char *sign = env->GetStringUTFChars(signature_str, NULL);
    if (sign == NULL) {
        LOGE("Memory allocation failed");
        return JNI_ERR;
    }

    int result = strcmp(sign, RELEASE_SIGN);
    // Release the memory after use
    env->ReleaseStringUTFChars(signature_str, sign);
    env->DeleteLocalRef(signature_str);
    if (result == 0) { // The signature is consistent
        return JNI_OK;
    }

    return JNI_ERR;
}
Copy the code

The JNI_OnLoad() function is called only if JNI is used. To make sure the App can verify the signature as soon as it starts.

I also wrote a method:

jstring Java_io_merculet_core_utils_EncryptUtils_nativeCheck(JNIEnv *env, jclass type) {
    return env->NewStringUTF("Security str from native.");
}
Copy the code

It is used in onCreate() of the App’s MainActivity.

        EncryptUtils.nativeCheck()
Copy the code

EncryptUtils is a utility class that calls native layer methods.

/ * * *@versionV1.0 < Describes current version features > *@FileName: io.merculet.core.utils.EncryptUtils.java
 * @author: Tony Shen
 * @date: 2018-05-21 20:53 * /
public class EncryptUtils {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("codec");
    }

    public static native String nativeCheck(a); . }Copy the code

Do not transmit key passwords in plain text

For example, passwords related to login and payment should be encrypted rather than transmitted in plain text. If doing encryption at the Java layer is easy to decompile, consider using C++.

conclusion

These measures are just the tip of the iceberg, because safety is always a topic. We can also consider using shells, anti-dynamic debugging, and so on.

References:

  1. http://qbeenslee.com/article/about-wandoujia-proguard-config/
  2. https://github.com/Qrilee/AndroidObfuseDictionary
  3. https://www.jianshu.com/p/2576d064baf1

Java and Android technology stack: update and push original technical articles every week, welcome to scan the qr code of the public account below and pay attention to, looking forward to growing and progress with you together.