天天看点

Android开发的安全性方案

Android 接入sdk是指某公司作为能力提供方经常以接入Sdk形式来暴露能力供用户使用,这样的能力对公司来说是财富实现的资本,不能被外界窥探或者破解成免费使用,那下面来分析下有哪些危害以及解决方案。

裸sdk的危害

裸Sdk是指sdk未进行加固,直接简单的通过反编译工具就可以看到其实现,现在混淆不算加固,但很多sdk就仅限于混淆而已,就没有其他安全措施了,这样就很容易暴露如下问题:

  1. 窥视内部实现方法
  2. 暴露了内部调用流程
  3. 避开流程直接调用关键代码从而破解流程以达到某种目的 

    这种情况很多,如第3方sdk功能存在推广或者界面不符合咱们使用,这个时候需要绕过其流程,如脱掉界面,直接绕过界面显示在按钮点击之后的功能才是我们需要的,这个时候可以查看其调用的方法,不能显示调用就直接反射调用。

  4. 泄漏核心技术

Sdk调用入口未鉴权

未鉴权导致Sdk存在随意被其他未经授权用户调用的情况,或者用来未经授权的应用使用Sdk,导致能力未经许可而导致损失

如何提高安全

1. 关键核心功能实现隐藏

java代码实现代码很容易被反编译,转而用Ndk开发实现生成so动态库,提高反编译难度,进一步可以通过加固so文件加密,从而更难被反编译查看。同时要注意的so作为核心库防到了反编译时也要注意对方可以直接使用so库,所以要对jni接口调用进行鉴权判断,入口调用栈检测,以及串联下上下文,尽量避免仅仅是个工具库,不要太解耦。
 关于如何具体开发Ndk jni代码可以参考我的另一篇文章:[Android studio下Cmake配置编译开发jni总结](http://blog.csdn.net/u010019468/article/details/78271965)
           
  • 1
  • 2
  • 3

2. Sdk中Dex和so文件加密及加壳

Sdk中dex文件和so文件加密从而屏蔽外部窥探,提高反编译难度。并且进一步通过jni本地方法来屏蔽核心方法,在so文件内解密dex文件并加载,Sdk暴露的接口全部通过jni来跳转或者通过反射跳转到核心dex内实现。其中对so文件加固可以参考:[对Native层(so文件)进行加固](http://blog.csdn.net/jiangwei0910410003/article/details/49967375),
           
  • 1
  • 2

对Dex文件通过各种加密方式然后动态加载方式,可以总结下来有如下方式:

1、直接通过加密dex文件保存下来,然后native层解密出文件并加载,这种被称为落地式加载,然后被截取到解密后文件
2、通过图片文件格式隐藏逻辑代码二进制文件,然后在native层解析出dex文件,达到dex文件隐藏目的
3、采用不落地式加载:解密dex文件成bytes,直接hook方式利用libdvm.so(低版本的)直接加载字节码
4、不仅dex文件加密,以及加密dex中的指令
5、通过NativeActivity把所有的java层代码都隐藏起来
           
  • 1
  • 2
  • 3
  • 4
  • 5

so如何实现程序的反调试?

同linux反调试基本原理相同,这里提供一种方式就是在JNI_Onload中调用ptrace方法,
一个进程只能被一个进程ptrace,如果你自己调用ptarce,这样其它程序就无法通过ptrace调试或者 
向您程序进程注入代码。**重点内容**
           
  • 1
  • 2
  • 3

3. 网络安全

如果网络请求不安全,直接被抓包,接口和参数直接被窥探从而被模拟,提高安全的话直接用https,如果条件不允许,那至少得加密传输,对参数加密,加密算法必须得so文件来实现。同时配合服务端做一些防刷,有限性鉴定。同时也要简单地对抗网络代理抓包
           
  • 1
  • 2

4. 方法调用栈检查

对于java调用而言,如果不对调用栈入口检查,就会出现方法被肆意调用,用户不受控制的调用干扰调用流程,如上述直接去掉sdk调用中出现的界面,所以可以直接在某个方法入口处先检查下此时的调用栈顺序是否符合流程的设计,可以检查下第一个入口,以及后上方的几个入口是否为设定的流程。

5. 方法入口统一扎口

Java实现方法在sdk,总能通过反射任意调用各个方法,单独使用某个方法来实现某个功能,所以假如我们经常为了共用功能而经常封装成单独方法,这样就为避开sdk设计的流程而单独调用提供了机会,这时会有人会说那我把所有的功能用一个方法实现来达到避免流程干扰,这样显示不切实际,那样的代码又长又臭,没有人敢来维护。即使如此,有个工具通过javassit来实现注入代码从而干扰代码执行流程。那到底有没有好点的办法来统一扎口呢?个人觉得比较好的方式就是通过Ndk来实现封装,毕竟jni接口入口可以独立出来使用,比起java代码反编难度大,调试难,又不好注入代码,更难随意调用ndk内部c++实现的相关方法。
 用一个jni入口暴露出来给java层调用,内部封装整个流程,很好避开大部分破解流程。
           
  • 1
  • 2
  • 3

6. 云方案

这种方案要是能行的话,觉得是最安全的,绝对避免了核心方法的泄露,这种sdk通过请求调用云端直接获取结果,如市场中大多图像识别sdk都是这样来做的。

7. 调用鉴权

主要可以分为静态鉴权和动态鉴权。前者主要是用来检查固定参数,如appKey和appSecret,以及结合限定应用包名(packageName)和应用名(appName)达非到限定sdk被未经授权而扩散使用。
 动态鉴权就是类似授权令牌一样由服务端分发token,调用着带这token来访问接口,这里面就涉及了多次本服务端和目标服务端多次交互,生成token透传给使用者,被调用者再次跟服务端交互校验调用者来源合法性。这种是保护token的生成规则而不在乎是否泄漏使用,即使泄漏,也只是表面这个合法使用者在使用,而不在乎是模拟的合法使用者,即不会生成出现新的合法调用者。
           
  • 1
  • 2
  • 3

8. 混淆 

1)通过字典来jar混淆 

字符混淆用来对抗反编译工具,让字符常量不显示或着显示成16进制数组。 

详细见这边文章:http://bobao.360.cn/learning/detail/3704.html?spm=a313e.7916648.0.0.11b0b6cqnOKtJ 

字典混淆是指通常混淆字典都是abc…可以通过配置特殊字典,让反编译者看得怀疑人生,有常见开源项目示例如下: 

Android开发的安全性方案

开源项目地址: https://github.com/ysrc/AndroidObfuseDictionary 

2)资源res混淆 

对代码的混淆能够增加一定的代码阅读难度,有时候我们为了防止资源的保护也是可以做混淆的,这个资源混淆原理这里就不多解释了,微信团队已经将这个功能开源,不了解的同学可以转战github查看: 

https://github.com/shwenzhang/AndResGuard 

Android开发的安全性方案

当然资源混淆还有一个很大的好处就是减小apk包的大小,当然这个不是本文讨论的知识点,这里我们讨论的是混淆资源增加破解查找资源的难度,先来看一下混淆资源之后的结果:

这里我们可以看到,一个混淆资源的应用,反编译之后查看他的string.xml内容,发现他的name全是简单的混淆字母,那么这个对于我们之前的那种可以通过name的值,来查找对应的字符串内容来获取消息,这个将是很蛋疼的一件事,因为你这时候如果全局搜索一个name值的话,比如这里的name=’a’,那么得搜出多少个这样的name,查找也是很好时间的,其实在没有混淆之前,一般string中的name都是比较唯一的一种值,查找的话不会有那么多查找结果,而且查找时间也是很短的。

9.反调试检测 

摘自《 Android安全防护之旅—Android应用”反调试”操作的几种方案解析》 

第一、自己附加进程,先占坑,ptrace(PTRACE_TRACEME, 0, 0, 0)! 

第二、签名校验不可或缺的一个选择,本地校验和服务端校验双管齐下! 

第三、借助系统api判断应用调试状态和调试属性,最基础的防护! 

第四、轮训检查android_server调试端口信息和进程信息,防护IDA的一种有效方式! 

第五、轮训检查自身status中的TracerPid字段值,防止被其他进程附加调试的一种有效方式!

个人思路总结方式: 

1、对于java调用,检查其调用stack。StackTraceElement来判断是被绕过执行 

2、结合服务端上传调用日志,后期延迟判断是否存在被破解 

3、对调试工具的反调试:如《Android DEX安全攻防战》

10.更多前沿加密加固技术学习 

加固技术发展与对抗——虚机源码保护的未来级别方案

技术前沿 | 虚拟机保护技术(VMP)的实践与体会

Android DEX安全攻防战 

“暗隐间谍”–利用NDK NativeActivity技术实现Android加固 

Android逆向之旅—Android应用的安全的攻防之战