Android App安全防範措施的小結

本文只是對最近工作的一些小結,方便以後的查詢。

關閉日誌的打印

關閉打印的日誌,防止日誌中的調試信息被看到。如果在網絡框架中使用了日誌,那就更加需要關閉了。

代碼混淆

代碼混淆是最基本的做法,至少能讓App在被反編譯之後不那麼順暢地閱讀源碼。

當然,即使是混淆之後的代碼,只要花費一定的時間,仍然是可以釐清代碼之間的邏輯。

混淆字典的使用

如果對代碼中的類名、變量名變成a、b、c還不爽,那可以自定義一些字符來替代它們。此時需要用到混淆字典。

推薦一個github上的庫:https://github.com/Qrilee/AndroidObfuseDictionary

使用混淆字典之後反編譯的效果.png

在proguard-rules.pro中添加混淆字典的配置

#混淆字典-obfuscationdictionary dic.txt-classobfuscationdictionary dic.txt-packageobfuscationdictionary dic.txt

native層校驗

除了混淆之外,App還需要防止被別人進行二次打包。

由於release簽名的唯一性,可以考慮在native層進行簽名的校驗。如果簽名不正確,直接讓App crash。

我們重寫JNI_OnLoad()函數,在此處進行校驗。

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("簽名不一致!"); return JNI_ERR;}

verifySign()函數會執行真正的校驗,將存放在native層的簽名字符串和當前App的簽名進行比對。

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("分配內存失敗"); return JNI_ERR; } int result = strcmp(sign, RELEASE_SIGN); // 使用之後要釋放這段內存 env->ReleaseStringUTFChars(signature_str, sign); env->DeleteLocalRef(signature_str); if (result == 0) { // 簽名一致 return JNI_OK; } return JNI_ERR;}

JNI_OnLoad()函數是隻有使用了JNI,才會被調用。為了確保App一啟動就能夠進行驗證簽名。

我還寫了一個方法:

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

在App的MainActivity的onCreate()中使用。

 EncryptUtils.nativeCheck()

EncryptUtils是一個工具類,用於調用native層的方法。

/** * @version V1.0  * @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(); ...}

關鍵的密碼不要明文傳輸

例如登錄、支付相關的密碼,最好不要明文傳輸,需要進行加密。如果在Java層來做加密容易被反編譯,那麼可以考慮使用C++來實現。

總結

這些措施也只是冰山一角,因為安全一直是永恆的話題。我們還可以考慮使用加殼、反動態調試等等。

  1. http://qbeenslee.com/article/about-wandoujia-proguard-config/
  2. https://github.com/Qrilee/AndroidObfuseDictionary
  3. https://www.jianshu.com/p/2576d064baf1
Android App安全防範措施的小結

Android App安全防範措施的小結


分享到:


相關文章: