本文只是對最近工作的一些小結,方便以後的查詢。
關閉日誌的列印
關閉列印的日誌,防止日誌中的除錯資訊被看到。如果在網路框架中使用了日誌,那就更加需要關閉了。
程式碼混淆
程式碼混淆是最基本的做法,至少能讓App在被反編譯之後不那麼順暢地閱讀原始碼。
當然,即使是混淆之後的程式碼,只要花費一定的時間,仍然是可以釐清程式碼之間的邏輯。
混淆字典的使用
如果對程式碼中的類名、變數名變成a、b、c還不爽,那可以自定義一些字元來替代它們。此時需要用到混淆字典。
推薦一個github上的庫:https://github.com/ysrc/AndroidObfuseDictionary
在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++來實現。
總結
這些措施也只是冰山一角,因為安全一直是永恆的話題。我們還可以考慮使用加殼、反動態除錯等等。
參考資料:
- http://qbeenslee.com/article/about-wandoujia-proguard-config/
- https://github.com/Qrilee/AndroidObfuseDictionary
- https://www.jianshu.com/p/2576d064baf1
Java與Android技術棧:每週更新推送原創技術文章,歡迎掃描下方的公眾號二維碼並關注,期待與您的共同成長和進步。