【我的Android进阶之旅】Android 如何防止 so库文件被未知应用盗用?

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: 首先,关于Android 如何防止 so库文件被未知应用盗用这个话题并不是我擅长的,只是在开发中遇到了这个问题,因此在这里总结一下。故事回到几个月之前,当时公司和第三方音乐平台合作了一款内置于手表系统的音乐APP应用,合作过程中需要第三方提供so库文件来进行相关操作。

首先,关于Android 如何防止 so库文件被未知应用盗用这个话题并不是我擅长的,只是在开发中遇到了这个问题,因此在这里总结一下。

故事回到几个月之前,当时公司和第三方音乐平台合作了一款内置于手表系统的音乐APP应用,合作过程中需要第三方提供so库文件来进行相关操作。当时提供so库文件的时候,第三方公司要求我们提供一个我们音乐APP应用的签名文件(这个签名文件称呼为V1)中的MD5码,然后再给我们提供了so库文件用于开发。

经过几个月的开发,我们的音乐APP要正式发布的时候,公司要求换一个应用签名文件,即之前的V1签名不用了,改用新的应用签名文件(这个签名文件称呼为V2)。

这个要求并不过分,因此我就立马换了签名文件为V2。可是当我把换了应用签名V2版本的APP运行的时候,直接crash了,crash log 打印为 so库文件找不到,应用无限crash然后无限自动重启,要泪奔了。

一开始以为是新版本的签名文件V2出问题了,但是其他同事使用的也是V2签名文件,并没有什么异常。然后只能将apk解压,对so库文件进行分析。

这里写图片描述

使用Beyond Compare工具打开该so库文件,可以更加清晰的看到十六进制的文件内容,如下图所示
这里写图片描述

这里写图片描述

通过分析so库文件,发现加密后的so库文件中 有 “Signature”以及“MD5”的字样,这个时候我脑海中突然回想起来几个月之前第三方平台提供给我们so库文件的时候,要求我们提供我们的应用签名文件V1的MD5码,是不是就是因为第三方平台提供的so库文件用MD5码进行了签名校验呢???

速度联系第三方平台,经过咨询之后确定是第三方平台提供的so库文件用MD5码进行了签名校验,然后我提供了V2签名文件的MD5给他们,之后第三方平台使用新的MD5码对so文件重新进行签名之后重新发给我们。接着使用新的MD5码签名过的so文件替换之前的老版本的so文件之后,使用V2版本的应用签名的APP正常运行了。

下图是分别使用V1版本的MD5码签名的so库文件使用V2版本的MD5码签名的so库文件 的对比图

这里写图片描述

附,有读者eziowayne看完之后弄不明白为什么同事的可以而我的不可以?

这里写图片描述

这里具体介绍下原因:

因为公司对于小型app都是单人独立开发,其他同事开发的是其他的apk,仅仅是将V1版本签名换成V2版本的签名用于打包apk而已,并没有使用V1或者V2签名对so进行签名等任何操作。

而我的apk用到的第三方音乐平台提供的so开发的音乐播放器,而这个so之前使用的是V1版本签名文件中的MD5码等属性做了加密,而V2版本签名文件的MD5码和V1版本签名中的MD5码不同,所以导致换签名文件之后,程序运行加载so的时候校验程序合法性的时候crash了。


上面内容是我自己遇到so库文件使用签名加密等防止未知应用盗用so库所遇到的问题,由以上的操作可以发现,我们可以通过在so库里面验证App的签名的方式来防止未知应用盗用so文件。

至于具体so库文件的源代码 cpp 里面是如何检验签名如何加密的,可以参考下面的文章了解大概的流程。

文章链接: http://blog.csdn.net/xx753277/article/details/37567951

下面是一段验证APP前面的示例代码,如下所示:

#include 
#include 
#include 
#include 
/**
 * http api 授权 key ,由服务端提供
 */
const char* AUTH_KEY = "0CC175B9C0F1B6A831C399E269772661";

/**
 * 发布的app 签名,只有和本签名一致的app 才会返回合法的 授权 key
 */
const char* RELEASE_SIGN ="3082030d308201f5a0030201020204163677d7300d06092a864886f70d01010b05003037310b30090603550406130255533110300e060355040a1307416e64726f6964311630140603550403130d416e64726f6964204465627567301e170d3134303131343035333835315a170d3434303130373035333835315a3037310b30090603550406130255533110300e060355040a1307416e64726f6964311630140603550403130d416e64726f696420446562756730820122300d06092a864886f70d01010105000382010f003082010a02820101008f02a948189bfa0134bdb17155061a4b62956c08881eac87b39f0a0acf1c808596100b09a16600f5567e5504f035f1deadc1dcc93fa93b930f78ac9a55ee961283458d66d3775e612e4eb5f5076bf06aeabb6e285400e89dd6e1327a52b4739a91d04d7288bc64c336b7776efe0b6341913d655943ffc9da4b8ba157a52f7790a815bf0cc693dd684209a07290b9c8d4220c8f56c00c25bee78500231213adba58b3c2c9e95951308dfab9edc4f9744fe6c99b3311b54971210db4c2e8a5d518def97535a69170827d6c14bd8a822a0d1b179a3633178db16376a9596df91c595b8cb3d3a916b0f10d029a0aad3b345a7ad54e85f2547686f612b62a010d7dd90203010001a321301f301d0603551d0e04160414f7c342dca7f916bb77312bb517105732c5e96328300d06092a864886f70d01010b0500038201010010b1bb9c29118a7eb15193ef15d40bafd4338526b200511f0e348e5c93eb9cbdf9330163cba183022572513dc7dac14cbd384c116bfc2c3d5efc9f545c3972ec8ce32908eb4e54a6940774ac569d682c188388380d60ed1904ecca4f4bf6bdcacd8be71557f1133c2fd5bcc2577fde19adef54cb91e02e635b47e6a6bfb1f46a5956259a10c61727e70cd8595fd632d4ed6c588dcb089967f164090aa89050911fd70eb1f7ccacd4d7b75c0b5ecd2bb84709b16176b9ac21f8bb25a970f65105af7939057cbf616abf5e9617f1ef87e39d16e458b278cbdc4f9ccb5d8e0c69719a5fae55bdd786b7b59c4a37a205972de1b0f3d4e8877267bad8f28ab09800f2";

/**
 * 拿到传入的app  的 签名信息,对比合法的app 签名,防止so文件被未知应用盗用
 */
static jclass contextClass;
static jclass signatureClass;
static jclass packageNameClass;
static jclass packageInfoClass;
JNIEXPORT jstring JNICALL Java_com_android_jni_JNI_getAuthKey(
        JNIEnv * env, jobject obj, jobject contextObject) {

    jmethodID getPackageManagerId = (env)->GetMethodID(contextClass, "getPackageManager","()Landroid/content/pm/PackageManager;");
    jmethodID getPackageNameId = (env)->GetMethodID(contextClass, "getPackageName","()Ljava/lang/String;");
    jmethodID signToStringId = (env)->GetMethodID(signatureClass, "toCharsString","()Ljava/lang/String;");
    jmethodID getPackageInfoId = (env)->GetMethodID(packageNameClass, "getPackageInfo","(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
    jobject packageManagerObject =  (env)->CallObjectMethod(contextObject, getPackageManagerId);
    jstring packNameString =  (jstring)(env)->CallObjectMethod(contextObject, getPackageNameId);
    jobject packageInfoObject = (env)->CallObjectMethod(packageManagerObject, getPackageInfoId,packNameString, 64);
    jfieldID signaturefieldID =(env)->GetFieldID(packageInfoClass,"signatures", "[Landroid/content/pm/Signature;");
    jobjectArray signatureArray = (jobjectArray)(env)->GetObjectField(packageInfoObject, signaturefieldID);
    jobject signatureObject =  (env)->GetObjectArrayElement(signatureArray,0);

    const char* signStrng =  (env)->GetStringUTFChars((jstring)(env)->CallObjectMethod(signatureObject, signToStringId),0);
    if(strcmp(signStrng,RELEASE_SIGN)==0)//签名一致  返回合法的 api key,否则返回错误
    {
       return (env)->NewStringUTF(AUTH_KEY);
    }else
    {
       return (env)->NewStringUTF("error");
    }

}


JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM* vm,void* reserved){

     JNIEnv* env = NULL;
     jint result=-1;
     if(vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK)
       return result;

     contextClass = (jclass)env->NewGlobalRef((env)->FindClass("android/content/Context"));
     signatureClass = (jclass)env->NewGlobalRef((env)->FindClass("android/content/pm/Signature"));
     packageNameClass = (jclass)env->NewGlobalRef((env)->FindClass("android/content/pm/PackageManager"));
     packageInfoClass = (jclass)env->NewGlobalRef((env)->FindClass("android/content/pm/PackageInfo"));

     return JNI_VERSION_1_4;
 }

更多关于Android安全与逆向分析的知识可以参考一下几个链接:

  1. 手把手教你逆向分析 Android 程序
    (https://segmentfault.com/a/1190000005133219)


作者:欧阳鹏 欢迎转载,与人分享是进步的源泉!
转载请保留原文地址: http://blog.csdn.net/ouyang_peng/article/details/65938698

这里写图片描述

相关文章
|
20天前
|
JSON Java Android开发
探索安卓开发之旅:打造你的第一个天气应用
【10月更文挑战第30天】在这个数字时代,掌握移动应用开发技能无疑是进入IT行业的敲门砖。本文将引导你开启安卓开发的奇妙之旅,通过构建一个简易的天气应用来实践你的编程技能。无论你是初学者还是有一定经验的开发者,这篇文章都将成为你宝贵的学习资源。我们将一步步地深入到安卓开发的世界中,从搭建开发环境到实现核心功能,每个环节都充满了发现和创造的乐趣。让我们开始吧,一起在代码的海洋中航行!
|
20天前
|
存储 搜索推荐 Java
打造个性化安卓应用:从设计到实现
【10月更文挑战第30天】在数字化时代,拥有一个个性化的安卓应用不仅能够提升用户体验,还能加强品牌识别度。本文将引导您了解如何从零开始设计和实现一个安卓应用,涵盖用户界面设计、功能开发和性能优化等关键环节。我们将以一个简单的记事本应用为例,展示如何通过Android Studio工具和Java语言实现基本功能,同时确保应用流畅运行。无论您是初学者还是希望提升现有技能的开发者,这篇文章都将为您提供宝贵的见解和实用的技巧。
|
24天前
|
搜索推荐 开发工具 Android开发
打造个性化Android应用:从设计到实现的旅程
【10月更文挑战第26天】在这个数字时代,拥有一个能够脱颖而出的移动应用是成功的关键。本文将引导您了解如何从概念化阶段出发,通过设计、开发直至发布,一步步构建一个既美观又实用的Android应用。我们将探讨用户体验(UX)设计的重要性,介绍Android开发的核心组件,并通过实际案例展示如何克服开发中的挑战。无论您是初学者还是有经验的开发者,这篇文章都将为您提供宝贵的见解和实用的技巧,帮助您在竞争激烈的应用市场中脱颖而出。
|
26天前
|
算法 Java 数据库
Android 应用的主线程在什么情况下会被阻塞?
【10月更文挑战第20天】为了避免主线程阻塞,我们需要合理地设计和优化应用的代码。将耗时操作移到后台线程执行,使用异步任务、线程池等技术来提高应用的并发处理能力。同时,要注意避免出现死循环、不合理的锁使用等问题。通过这些措施,可以确保主线程能够高效地运行,提供流畅的用户体验。
38 2
|
29天前
|
Java API Android开发
安卓应用程序开发的新手指南:从零开始构建你的第一个应用
【10月更文挑战第20天】在这个数字技术不断进步的时代,掌握移动应用开发技能无疑打开了一扇通往创新世界的大门。对于初学者来说,了解并学习如何从无到有构建一个安卓应用是至关重要的第一步。本文将为你提供一份详尽的入门指南,帮助你理解安卓开发的基础知识,并通过实际示例引导你完成第一个简单的应用项目。无论你是编程新手还是希望扩展你的技能集,这份指南都将是你宝贵的资源。
48 5
|
30天前
|
移动开发 Dart 搜索推荐
打造个性化安卓应用:从零开始的Flutter之旅
【10月更文挑战第20天】本文将引导你开启Flutter开发之旅,通过简单易懂的语言和步骤,让你了解如何从零开始构建一个安卓应用。我们将一起探索Flutter的魅力,实现快速开发,并见证代码示例如何生动地转化为用户界面。无论你是编程新手还是希望扩展技能的开发者,这篇文章都将为你提供价值。
|
1月前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
49 4
|
Android开发
Android 进阶的整理
Android 进阶的整理
103 0
Android 进阶的整理
|
6天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
11天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。