• 使用jni技术进行android应用签名信息核查及敏感信息保护


           近期业余时间写了一款应用《摇啊摇》,安智、安卓、360等几个应用商店已经陆续审核通过并上线。从有想法到终于将产品做出来并公布,断断续续花了近二个半月的业余时间,整体来讲还算顺利,尽管期间也遇到几个小技术难点。最后解决的还算惬意。今天说下当中一个小技术难点,如今想想这个小技术难点也非常寻常,但还是分享出来,希望对有同样疑惑的同学有帮助。

           因为java语言自身特性的原因,导致android程序非常easy被反编译。尽管能够採用代码混淆的方式,可是假设用了第三方库。混淆脚步编写不好,代码混淆后又会出现程序执行不稳定问题。

    而没有混淆的程序一旦被反编译后。源代码中大量的敏感信息将会暴露无遗。比方与server交互的url地址信息,假设使用了动态链接库,那么native方法也将暴露。况且混淆时是不混淆native方法的。别人看到native方法,就能够自己载入so文件,那么非常多核心的东西,别人就能够间接的使用了,尽管他不一定能用好,但至少能够调用了。

    针对上面的林林总总程序被反编译后可能出现的问题,我的解决方法是使用jni技术。在ndk环境下做包签名信息核查,因为是ndk环境,所以这个非常难反编译,也非常难绕过该核查。理论上,签名文件keystore是唯一的,而且仅仅有程序作者才拥有。详细做法为,在所有的native方法内。添加签名信息核查推断,仅仅有签名信息核查通过。程序才干做进一步操作,否则直接返回NULL,这样,即使别人拿到了so文件,摸清楚了native方法參数及使用方法,但因为签名信息不一致。native方法所有返回NULL,so文件瞬间变成砖头。

    相同。对于url地址等敏感信息,添加签名信息核查,仅仅有核查通过。程序才会返回正确的字符串,否则直接返回NULL。这样能够非常好的隐藏和保护敏感信息。

    说了半天,关键的一步是怎样在ndk环境下,获取包签名信息,以下的代码为相关实现。

    jstring loadSignature(JNIEnv* env, jobject obj)
    {
    	// 获得Context类
        jclass cls = (*env)->GetObjectClass(env, obj);
        // 得到getPackageManager方法的ID
        jmethodID mid = (*env)->GetMethodID(env, cls, "getPackageManager", "()Landroid/content/pm/PackageManager;");
    
        // 获得应用包的管理器
        jobject pm = (*env)->CallObjectMethod(env, obj, mid);
    
        // 得到getPackageName方法的ID
        mid = (*env)->GetMethodID(env, cls, "getPackageName", "()Ljava/lang/String;");
        // 获得当前应用包名
        jstring packageName = (jstring)(*env)->CallObjectMethod(env, obj, mid);
    
        // 获得PackageManager类
        cls = (*env)->GetObjectClass(env, pm);
        // 得到getPackageInfo方法的ID
        mid  = (*env)->GetMethodID(env, cls, "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
        // 获得应用包的信息
        jobject packageInfo = (*env)->CallObjectMethod(env, pm, mid, packageName, 0x40); //GET_SIGNATURES = 64;
        // 获得PackageInfo 类
        cls = (*env)->GetObjectClass(env, packageInfo);
        // 获得签名数组属性的ID
        jfieldID fid = (*env)->GetFieldID(env, cls, "signatures", "[Landroid/content/pm/Signature;");
        // 得到签名数组
        jobjectArray signatures = (jobjectArray)(*env)->GetObjectField(env, packageInfo, fid);
        // 得到签名
        jobject sign = (*env)->GetObjectArrayElement(env, signatures, 0);
    
        // 获得Signature类
        cls = (*env)->GetObjectClass(env, sign);
        // 得到toCharsString方法的ID
        mid = (*env)->GetMethodID(env, cls, "toCharsString", "()Ljava/lang/String;");
    
        // 返回当前应用签名信息
        return (jstring)(*env)->CallObjectMethod(env, sign, mid);
    }
           上述代码获得的包签名信息实际是一个非常长的字符串。为了更高效的进行签名信息比对,还能够将其进行md5加密,加密成32位字符串形式。另外,我在查阅资料过程中,看到有的资料提到用包签名的hashcode值做比对。这样的方式更简单一点。但我没有採用这样的方式,总认为这样的方式可能不精确,不过个人认为,有兴趣的同学能够查阅很多其它相关资料。这里还要说明一点,上面代码获得的包签名字符串信息,使用md5加密后,得到的加密结果与包签名实际的md5 fingerprint是不一致的。主要是由于将签名信息使用toCharsString()转换成字符串后在进行md5加密所致。假设使用toByteArray()将其转成数组。然后加密,加密结果与包签名实际md5 fingerprint将是一致的。


  • 相关阅读:
    基础概述 01
    VMware Workstation Pro 无法在Windows 上运行 检查可在Windows上运行的此应用的更新版
    在博客园安家
    mysql的主从复制配置
    PHP 9 大缓存技术总结
    Mysql分库分表方案
    VMware 虚拟机的网络连接方式详解
    关于memecache的使用及清楚示意
    SVN:冲突解决 合并别人的修改
    Linux环境Nginx安装多版本PHP
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/5141066.html
Copyright © 2020-2023  润新知