Android App安全防范措施的小结

本文只是对最近工作的一些小结,方便以后的查询。

Android App安全防范措施的小结_第1张图片

关闭日志的打印

关闭打印的日志,防止日志中的调试信息被看到。如果在网络框架中使用了日志,那就更加需要关闭了。

代码混淆

代码混淆是最基本的做法,至少能让App在被反编译之后不那么顺畅地阅读源码。

当然,即使是混淆之后的代码,只要花费一定的时间,仍然是可以厘清代码之间的逻辑。

混淆字典的使用

如果对代码中的类名、变量名变成a、b、c还不爽,那可以自定义一些字符来替代它们。此时需要用到混淆字典。

推荐一个github上的库:https://github.com/Qrilee/AndroidObfuseDictionary

Android App安全防范措施的小结_第2张图片

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

 
   
  1. #混淆字典

  2. -obfuscationdictionary dic.txt

  3. -classobfuscationdictionary dic.txt

  4. -packageobfuscationdictionary dic.txt

native层校验

除了混淆之外,App还需要防止被别人进行二次打包。

由于release签名的唯一性,可以考虑在native层进行签名的校验。如果签名不正确,直接让App crash。

我们重写JNI_OnLoad()函数,在此处进行校验。

 
   
  1. jint JNI_OnLoad(JavaVM *vm, void *reserved) {

  2.    JNIEnv *env = NULL;

  3.    if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {

  4.        return JNI_ERR;

  5.    }

  6.    if (verifySign(env) == JNI_OK) {

  7.        return JNI_VERSION_1_4;

  8.    }

  9.    LOGE("签名不一致!");

  10.    return JNI_ERR;

  11. }

verifySign()函数会执行真正的校验,将存放在native层的签名字符串和当前App的签名进行比对。

 
   
  1. static int verifySign(JNIEnv *env) {

  2.    // Application object

  3.    jobject application = getApplication(env);

  4.    if (application == NULL) {

  5.        return JNI_ERR;

  6.    }

  7.    // Context(ContextWrapper) class

  8.    jclass context_clz = env->GetObjectClass(application);

  9.    // getPackageManager()

  10.    jmethodID getPackageManager = env->GetMethodID(context_clz, "getPackageManager",

  11.                                                   "()Landroid/content/pm/PackageManager;");

  12.    // android.content.pm.PackageManager object

  13.    jobject package_manager = env->CallObjectMethod(application, getPackageManager);

  14.    // PackageManager class

  15.    jclass package_manager_clz = env->GetObjectClass(package_manager);

  16.    // getPackageInfo()

  17.    jmethodID getPackageInfo = env->GetMethodID(package_manager_clz, "getPackageInfo",

  18.                                                "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");

  19.    // context.getPackageName()

  20.    jmethodID getPackageName = env->GetMethodID(context_clz, "getPackageName",

  21.                                                "()Ljava/lang/String;");

  22.    // call getPackageName() and cast from jobject to jstring

  23.    jstring package_name = (jstring) (env->CallObjectMethod(application, getPackageName));

  24.    // PackageInfo object

  25.    jobject package_info = env->CallObjectMethod(package_manager, getPackageInfo, package_name, 64);

  26.    // class PackageInfo

  27.    jclass package_info_clz = env->GetObjectClass(package_info);

  28.    // field signatures

  29.    jfieldID signatures_field = env->GetFieldID(package_info_clz, "signatures",

  30.                                                "[Landroid/content/pm/Signature;");

  31.    jobject signatures = env->GetObjectField(package_info, signatures_field);

  32.    jobjectArray signatures_array = (jobjectArray) signatures;

  33.    jobject signature0 = env->GetObjectArrayElement(signatures_array, 0);

  34.    jclass signature_clz = env->GetObjectClass(signature0);

  35.    jmethodID toCharsString = env->GetMethodID(signature_clz, "toCharsString",

  36.                                               "()Ljava/lang/String;");

  37.    // call toCharsString()

  38.    jstring signature_str = (jstring) (env->CallObjectMethod(signature0, toCharsString));

  39.    // release

  40.    env->DeleteLocalRef(application);

  41.    env->DeleteLocalRef(context_clz);

  42.    env->DeleteLocalRef(package_manager);

  43.    env->DeleteLocalRef(package_manager_clz);

  44.    env->DeleteLocalRef(package_name);

  45.    env->DeleteLocalRef(package_info);

  46.    env->DeleteLocalRef(package_info_clz);

  47.    env->DeleteLocalRef(signatures);

  48.    env->DeleteLocalRef(signature0);

  49.    env->DeleteLocalRef(signature_clz);

  50.    const char *sign = env->GetStringUTFChars(signature_str, NULL);

  51.    if (sign == NULL) {

  52.        LOGE("分配内存失败");

  53.        return JNI_ERR;

  54.    }

  55.    int result = strcmp(sign, RELEASE_SIGN);

  56.    // 使用之后要释放这段内存

  57.    env->ReleaseStringUTFChars(signature_str, sign);

  58.    env->DeleteLocalRef(signature_str);

  59.    if (result == 0) { // 签名一致

  60.        return JNI_OK;

  61.    }

  62.    return JNI_ERR;

  63. }

JNI_OnLoad()函数是只有使用了JNI,才会被调用。为了确保App一启动就能够进行验证签名。

我还写了一个方法:

 
   
  1. jstring Java_io_merculet_core_utils_EncryptUtils_nativeCheck(JNIEnv *env, jclass type) {

  2.    return env->NewStringUTF("Security str from native.");

  3. }

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

 
   
  1.        EncryptUtils.nativeCheck()

EncryptUtils是一个工具类,用于调用native层的方法。

 
   
  1. /**

  2. * @version V1.0 <描述当前版本功能>

  3. * @FileName: io.merculet.core.utils.EncryptUtils.java

  4. * @author: Tony Shen

  5. * @date: 2018-05-21 20:53

  6. */

  7. public class EncryptUtils {

  8.    // Used to load the 'native-lib' library on application startup.

  9.    static {

  10.        System.loadLibrary("codec");

  11.    }

  12.    public static native String nativeCheck();

  13.    ...

  14. }

关键的密码不要明文传输

例如登录、支付相关的密码,最好不要明文传输,需要进行加密。如果在Java层来做加密容易被反编译,那么可以考虑使用C++来实现。

总结

这些措施也只是冰山一角,因为安全一直是永恒的话题。我们还可以考虑使用加壳、反动态调试等等。

参考资料:

  1. http://qbeenslee.com/article/about-wandoujia-proguard-config/

  2. https://github.com/Qrilee/AndroidObfuseDictionary

  3. https://www.jianshu.com/p/2576d064baf1


关注【Java与Android技术栈】

更多精彩内容请关注扫码

Android App安全防范措施的小结_第3张图片


你可能感兴趣的:(Android App安全防范措施的小结)