本文只是对最近工做的一些小结,方便之后的查询。java
关闭打印的日志,防止日志中的调试信息被看到。若是在网络框架中使用了日志,那就更加须要关闭了。android
代码混淆是最基本的作法,至少能让App在被反编译以后不那么顺畅地阅读源码。git
固然,即便是混淆以后的代码,只要花费必定的时间,仍然是能够厘清代码之间的逻辑。github
若是对代码中的类名、变量名变成a、b、c还不爽,那能够自定义一些字符来替代它们。此时须要用到混淆字典。安全
推荐一个github上的库:https://github.com/ysrc/AndroidObfuseDictionarybash
在proguard-rules.pro中添加混淆字典的配置网络
#混淆字典
-obfuscationdictionary dic.txt
-classobfuscationdictionary dic.txt
-packageobfuscationdictionary dic.txt
复制代码
除了混淆以外,App还须要防止被别人进行二次打包。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++来实现。
这些措施也只是冰山一角,由于安全一直是永恒的话题。咱们还能够考虑使用加壳、反动态调试等等。
参考资料:
Java与Android技术栈:每周更新推送原创技术文章,欢迎扫描下方的公众号二维码并关注,期待与您的共同成长和进步。