一、已知防护策略
1.不可或缺的混淆 Java 是一种跨平台、解释型语言,Java 源代码编译成的 class 文件中有大量包含语义的变量名、方法名的信息,很容易被反编译为 Java 源代码。为了防止这种现象,我们可以对 Java 字节码进行混淆。混淆不仅能将代码中的类名、字段、方法名变为无意义的名称,保护代码,也由于移除无用的类、方法,并使用简短名称对类、字段、方法进行重命名缩小了程序的大小。
ProGuard 由 shrink、optimize、obfuscate 和 preverify 四个步骤组成,每个步骤都是可选的,需要哪些步骤都可以在脚本中配置。 参见 ProGuard 官方介绍。
压缩(Shrink):侦测并移除代码中无用的类、字段、方法、和特性(Attribute)。
优化(Optimize):分析和优化字节码。
混淆(Obfuscate): 使用 a、b、c、d 这样简短而无意义的名称,对类、字段和方法进行重命名。
上面三个步骤使代码 size 更小,更高效,也更难被逆向工程。
预检(Preveirfy): 在 java 平台上对处理后的代码进行预检。
一般来说优化和预检选项在 Android 中是关闭的,脚本如下: -dontoptimize 表示不进行优化,建议使用此选项,因为根据 proguard-android-optimize.txt 中的描述,优化可能会造成一些潜在风险,不能保证在所有版本的 Dalvik 上都正常运行。 -dontpreverify 表示不进行预校验。这个预校验是作用在 Java 平台上的,Android 平台上不需要这项功能,去掉之后还可以加快混淆速度。 (在安装 apk 过程中系统会对 dex 校验及优化成 odex) 作为防护来说对于混淆的需求就是 Obfuscate,增加阅读代码的难度。
2.签名校验 校验各个文件的信息,比如微信的 dex 文件校验,阿里聚安全的签名文件校验等高强度操作。
第一:直接在本地做防护,如果发现签名不一致直接退出应用 第二:将签名信息携带请求参数中参与加密,服务端进行签名校验,失败就返回错误数据即可
Android 的签名机制可以有效防止应用二次签名后不能覆盖安装,具体原理这里不分析了,但也导致安装了二次签名的 apk,无法覆盖安装正常签名的 apk,所以在很容易被二次签名的防护基础上进行签名校验是有必要的,当然如果很难被反编译破解就可以酌情考虑了。
3、反调试异常检测 1 ) so 跟踪调试是基于进程的注入技术,然后使用 Linux 中的 ptrace 机制,进行调试目标进程的 ptrace 提供了一种使父进程得以监视和控制子进程的方式,它还能够改变子进程中的寄存器和内核映像,因而可以实现断点调试和系统调用的跟踪
ptrace 机制有一个特点,就是如果一个进程被调试了,在他进程的 status 文件中有一个字段 TracerPid 会记录调试者的进程 id 值,可以选择两种方式:
1.轮训查看文件:/proc/[myPid]/status,读取 TracerPid 字段的值,发现大于 0,就立马退出程序 2.一般一个进程只能被附加一次,我们在破解调试的时候都会附加需要调试应用的进程,如果我们先占坑,父进程附加自己,那么后面在附加调试就会失败
1.检查应用是否属于 debug 模式 直接调用 Android 中的 flag 属性:ApplicationInfo.FLAG_DEBUGGABLE,判断是否属于 debug 模式,为了防止现在破解者为了调试应用将应用反编译在 AndroidManifest.xml 中添加:android:debuggable 属性值,将其设置 true。然后就可以进行调试。
2.检查应用是否处于调试状态 借助系统的一个 api 来进行判断:android.os.Debug.isDebuggerConnected();这个就是判断当前应用有没有被调试
3.循环检查端口 查看设备的 tcp 端口使用情况 cat /proc/net/tcp 比如 Frida 框架,他的端口号是 27042 和 27043,以及进程名是 frida-server
4、加固方案 加固的基本步骤如下:
dex 加固主要是防止被静态反编译,进而获取源码并修改
除了以上业务相关性弱的防护方案,还有防被抓包,防被 hook 等和业务密切相关的防护方案,如传输数据加密,防作弊等策略。
以上每个防护策略都有对应的破解之道,当然破解了不代表不能防,防护只是增加破解的难度和时间,攻防没有永远的胜利方,有人攻就有人防,防护策略也在不断的升级更新换代。
要深入理解 Android 安全防护,就必须了解 Android 应用的结构。
二、APK(Android PacKage)结构
assets 目录: 用于存放需要打包到 APK 中的静态文件,和 res 的不同点在于,assets 目录支持任意深度的子目录,用户可以根据自己的需求任意部署文件夹架构
lib 目录: 程序依赖的 native 库( so 库)
META-INF 目录: 存放应用程序签名和证书的目录,包含的文件有 CERT.RSA ,CERT.DSA ,CERT.SF 和 MANIFEST.MF ,其中 CERT.RSA 是开发者利用私钥对 APK 进行签名的签名文件,CERT.SF ,MANIFEST.MF 记录了文件中文件的 SHA-1 哈希值
res 目录: 存放应用程序的资源,存在这个文件夹下的所有文件都会映射到 Android 工程的.R 文件中,生成对应的 ID,res 文件夹下可以包含多个文件夹,其中 anim 存放动画文件; drawable 目录存放图像资源; layout 目录存放布局文件; values 目录存放一些特征值,colors.xml 存放 color 颜色值,dimens.xml 定义尺寸值,string.xml 定义字符串的值,styles.xml 定义样式对象; xml 文件夹存放任意 xml 文件,在运行时可以通过 Resources.getXML()读取; raw 是可以直接复制到设备中的任意文件,他们无需编译。
resources.arsc: 资源配置文件,用来记录资源文件和资源 ID 之间的映射关系,用来根据资源 ID 寻找资源
AndroidManifest.xml: 应用程序的配置文件,程序打包时,会把 AndroidManifest.xml 进行简单的编译,便于 Android 系统识别,编译之后的格式是 AXML 格式。
classes.dex: dex 可执行文件,传统的 Java 程序,首先先把 Java 文件编译成 class 文件,字节码都保存在了 class 文件中,Java 虚拟机可以通过解释执行这些 class 文件。而 Dalvik 虚拟机是在 Java 虚拟机进行了优化,执行的是 Dalvik 字节码,而这些 Dalvik 字节码是由 Java 字节码转换而来,一般情况下,Android 应用在打包时通过 AndroidSDK 中的 dx 工具将 Java 字节码转换为 Dalvik 字节码。dx 工具可以对多个 class 文件进行合并,重组,优化,可以达到减小体积,缩短运行时间的目的。
对于逆向首入门槛就是 dex,了解 dex 的数据结构对防护和逆向都是极其重要的,dex 文件结构分析文章非常多,这里不多赘述,不了解的先去了解下。
对于 Android 防护目前流行的最后方案就是加固,某些应用市场已经把加固和上架进行了绑定,说明加固的逆向难度公认度是很高的。
上面介绍了加固的基本步骤,市面上的加固方案都大同小异,最核心的部分就是对 apk/dex 进行加密-解析-动态加载,对 dex 加密各有各的方式和算法,了解 dex 结构和动态加载之后就可以对不同加固方案进行具体分析了,不过分析大厂的 apk 之后发现都没有对 dex 进行加固就令人深思,也许在客户端的防护只是门槛,服务端的防护及防作弊才是终极防护策略,而加固会增加崩溃的概率,作为大流量的 app 来说万分之一的概率也是很高的,而对 dex 加固的安全性并不是最高的,所以放弃对 dex 加固也是有迹可循的。
作为 Android 应用开发者来说,navite 层的代码具有更高的挑战性,大部分 Android 开发者并不熟悉 c/c++开发,所以 so 的加固应运而生。
动态链接库 so: 动态链接库是程序运行时加载的库,当动态链接库正确安装后,所有的程序都可以使用动态库来运行程序。动态链接库是目标文件的集合,目标文件在动态链接库中的组织方式是按照特殊方式形成的。库中函数和变量的地址是相对地址,不是绝对地址,其真实地址在调用动态库的程序加载时形成。
so 文件是基于 ELF(Executable and Linking Format)文件格式。而 so 是共享目标文件,所以要想对 so 文件进行加密就必须了解
ELF 文件格式: 可执行链接格式(Executable and Linking Format)最初是由 UNIX 系统实验室(UNIX System Laboratories,USL)开发并发布的,作为应用程序二进制接口(Application Binary Interface,ABI)的一部分。工具接口标准(Tool Interface Standards,TIS)委员会将还 在发展的 ELF 标准选作为一种可移植的目标文件格式,可以在 32 位 Intel 体系结构上的 很多操作系统中使用。
目标文件有三种类型: 可重定位文件(Relocatable File) .o)包含适合于与其他目标文件链接来创建可执行文件或者共享目标文件的代码和数据。 可执行文件(Executable File) .exe) 包含适合于执行的一个程序,此文件规定了 exec() 如何创建一个程序的进程映像。 共享目标文件(Shared Object File) .so) 包含可在两种上下文中链接的代码和数据。首先链接编辑器可以将它和其它可重定位
文件和共享目标文件一起处理, 生成另外一个目标文件。其次动态链接器(Dynamic Linker)可能将它与某 个可执行文件以及其它共享目标一起组合,创建进程映像。
目标文件全部是程序的二进制表示,目的是直接在某种处理器上直接执行。
了解 ELF 格式分析移步 ELF 文件格式与动态链接 /静态链接与动态库 /静态库。
通过对 so 中的 section 和函数进行加密来加固对逆向的难度会提高很多,虽然对于了解 so 的人来说并不是难事,比如动态调试一下,或者 dump 出内存中运行的 dex,所以没有绝对的安全,只有相对的攻防。