乐趣区

Android-App加固原理与技术历程

App 为什么会被破解入侵

随着黑客技术的普及化平民化,App,这个承载我们移动数字工作和生活的重要工具,不仅是黑客眼中的肥肉,也获得更多网友的关注。百度一下“App 破解”就有 5290 万条结果。

一旦 App 被破解,不仅使用者的照片、身份证、手机号、联系住址、邮箱和支付密码等敏感信息会泄露,还可能感染手机的操作系统,进而导致手机被入侵篡改,乃至成为攻击者操控的“僵尸网络”中的一部分。

安卓 App 的开发除了部分功能采用 C /C++ 编码外,其余主要都是采用 Java 进行编码开发功能。Java 源码最终编译成 smali 字符码,以 classes.dex 保存在 App 的 APK 中。

Java 是一种解释性语言,功能强大,易用性强。初学者能轻松地学习 Java,并编写简单的应用程序。而且 Java 的基本类库(JDK)是开源的,这就使很多 Java 开发的应用被逆向破解的门槛很低。目前市面上有大量的逆向破解工具,例如:Dex2Jar、JEB、JD-GUI 等等。只要懂代码编程,利用这些工具就可以破解市面上那些防御薄弱、存在大量安全漏洞的 App。这就很好理解为什么会有如此多人去搜索“App 破解”了。

之前曾有媒体报道,有网络黑产专门从各种渠道找到 App 的 apk,然后将 apk 文件逆向破解,再植入广告、病毒代码,最后重新打包投入公开市场,当不明真相的网友将带病毒广告的 App 下载后,会带来巨大经济损失。

加固技术发展历程

传统 App 加固技术,前后经历了四代技术变更,保护级别每一代都有所提升,但其固有的安全缺陷和兼容性问题始终未能得到解决。而下一代加固技术—虚机源码保护,适用代码类型更广泛,App 保护级别更高,兼容性更强,堪称未来级别的保护方案。

第一代加固技术—动态加载

第一代 Android 加固技术用于保护应用的逻辑不被逆向与分析,最早普遍在恶意软件中使用,其主要基于 Java 虚拟机提供的动态加载技术。

保护流程

开发阶段

开发阶段中将程序切分成加载(Loader)与关键逻辑(Payload)两部分,并分别打包;

启动流程

运行时加载部分(Loader)会先运行,释放出关键逻辑(Payload),然后 java 的动态加载技术进行加载,并转交控制权。

核心代码:

备注 (multidex 组件的加固原理):

Android 的 DEX 文件在设计之初程序普遍较小,所以在 DEX 文件设计时,只允许包含 65535 个函数引用。而随着 Android 应用的发展,大量的应用的代码已经超过了 65535 的限制,为了解决这个问题,Android5.0 之后原生支持加载多个 dex,而为了对旧版本的兼容,Android 提供了 multidex 组件。该组件的实现原理与上面介绍的是一致的。

缺陷与对抗

第一代加固技术的缺陷是依赖 Java 的动态加载机制,而这个机制要求关键逻辑(Payload)部分必须解压,并且释放到文件系统,这就给了攻击机会去获取对应的文件。虽然可以通过关键逻辑(Payload)被加载后,被从文件系统删除,用于防止被复制,但是攻击者可以拦截对应的删除函数,阻止删除。

而关键逻辑(Payload)会被加密后保存,可用于对抗静态分析,但是攻击者可以通过自定义虚拟机,拦截动态加载机制所使用的关键函数,在这个函数内部,复制文件系统中的关键逻辑(Payload)文件。

第二代加固技术—不落地加载

相对第一代加固技术,第二代加固技术在 APK 修改方面已经完善,能做到对开发的零干扰。开发过程中不需要对应用做特殊处理,只需要在最终发布前进行保护即可。而为了实现这个零干扰的流程,Loader 需要处理好 Android 的组件的生命周期。

保护流程

1)Loader 被系统加载。

2)系统初始化 Loader 内的 StubApplication。

3)StubApplication 解密并且加载原始的 DEX 文件(Payload)。

4)StubApplication 从原始的 DEX 文件(Payload)中找到原始的 Application 对象,创建并初始化。

5)将系统内所有对 StubApplication 对象的引用使用替换成原始 Application,此步骤使用 JAVA 的反射机制实现。

6)由 Android 系统进行其他组件的正常生命周期管理。

对开发零干扰的加固后启动流程:

另一方面,不落地加载技术是在第一代加固技术的基础上改进,主要解决第一代技术中 Payload 必须释放到文件系统(俗称落地)的缺陷,其主要的技术方案有两种:

A.拦截系统 IO 相关的函数(如 read、write),在这些函数中提供透明加解密。具体的流程是:

1)关键逻辑(Payload)以加密的方式存储在 APK 中。

2)运行时加载部分(Loader)将关键逻辑释(Payload)放到文件系统,此时关键逻辑(Payload)还处于加密状态。

3)加载部分拦截对应的系统 IO 函数(read,write 等)。

4)加载部分(Loader)正常调用 Java 动态加载机制。由于虚拟机的 IO 部分被拦截,所以虚拟机读取到已经解密的关键逻辑(Payload)。

透明加解密方案流程:

B.直接调用虚拟机提供的函数进行不落地的加载,具体流程是:

1)关键逻辑(Payload)以加密的方式存储在 APK 中。

2)运行时加载部分(Loader)将关键逻辑释(Payload)放到内存。

3)加载部分调用虚拟机内部接口进行加载。

不落地加载流程:

关键的系统函数如下:

兼容性

方案 A 透明加密方案由于其需要拦截系统的 IO 函数,这部分会使用 inline hook 或者 got hook 等技术,其会带来一定的兼容性问题

方案 B 的不落地加载方案由于其调需要调用系统内部的接口,而这个接口并不导出,各个厂商在实现时又有各自的自定义修改,导致该方案存在兼容性问题。

缺陷与对抗

第二代加固技术在应用启动时要处理大量的加解密加载操作,会造成应用长时间假死(黑屏),用户体验差。

在加固技术实现上没有本质区别,虽然能防止第一代加固技术文件必须落地被复制的缺陷,但是也可以从以下方面进行对抗:

例如内存中的 DEX 文件头会被清除,用于防止在 dump 文件中被找到;DEX 文件结构被破坏,例如增加了一些错误的数据,提高恢复的成本。

但是 Payload 被加载之后,在内存中是连续的,利用 gdb 等调试工具 dump 内存后可以直接找到 Payload,进行简单的处理之后可以恢复出 100% 的 Payload 文件。

和第一代加固技术的对抗方法一样,不落地加载也无法对抗自定义虚拟机。只需对上述的关键函数进行拦截然后将对应的内存段写出去,即可恢复 Payload。注意,由于 IO 相关的函数被拦截,所以无法直接调用 read/write 等函数进行直接的读写,需要使用 syscall 函数进行绕过。

虽然厂商会自己实现可能上述函数,从而绕过上述函数的拦截。但是 Android 的类加载器必须能找到对于的结构体才能正常执行,攻击者可以以类加载器做为起点,找到对应的 Payload 在内存中的位置。

第三代加固技术—指令抽离

由于第二代加固技术仅仅对文件级别进行加密,其带来的问题是内存中的 Payload 是连续的,可以被攻击者轻易获取。第三代加固技术对这部分进行了改进,将保护级别降到了函数级别。

保护流程

发布阶段

发布阶段将原始 DEX 内的函数内容(Code Item)清除,单独移除到一个文件中。

运行阶段

运行阶段将函数内容重新恢复到对应的函数体。恢复的时间点有几个方式:

A. 加载之后恢复函数内容到 DEX 壳所在的内存区域

B. 加载之后将函数内容恢复到虚拟机内部的结构体上:虚拟机读取 DEX 文件后内部对每一个函数有一个结构体,这个结构体上有一个指针指向函数内容(CodeItem),可以通过修改这个指针修改对应的函数内容。

C. 拦截虚拟机内与查找执行代码相关的函数,返回函数内容。

兼容性

指令抽离技术使用了大量的虚拟内部结构与未被文档的特性,再加上 Android 复杂的厂商定制,带来大量的兼容性问题。

缺陷与对抗

指令抽离技术的某些方案与虚拟机的 JIT 性能优化冲突,无法达到最佳的运行性能。依旧使用了 java 虚拟机进行函数内容的执行。攻击者可以通过自定义 Android 虚拟机,在解释器的代码上做记录一个函数的内容(CodeItem)。接下来遍历触发所有函数,从而获取到全部的函数内容。最终重新组装成一个完整的 DEX 文件。目前已经有自动化工具可以指令抽离技术中脱壳。

第三代加固 DEX 文件脱壳流程:

第四代加固技术:指令转换 /VMP

第三代加固技术在函数级别的保护,使用 Android 虚拟机内的解释器执行代码,带来可能被记录的缺陷,第四代加固技术使用自己的解释器来避免第三代的缺陷。而自定义的解释器无法对 Android 系统内的其他函数进行直接调用,必须使用 JAVA 的 JNI 接口进行调用。

保护流程

主要实现由两种:

A.DEX 文件内的函数被标记为 native,内容被抽离并转换成一个符合 JNI 要求的动态库。动态库内通过 JNI 和 Android 系统进行交互。

B.DEX 文件内的函数被标记为 native,内容被抽离并转换成自定义的指令格式,该格式使用自定义接收器执行,和 A 一样需要使用 JNI 和 Android 系统进行调用。

兼容性

第四代 VMP 加固技术一般配合第三代加固技术使用,所以第三代的所有兼容性问题,指令转换 /VMP 加固也存在。

缺陷与对抗

不论使用指令转换 /VMP 加固的 A 方案或者 B 方案,其必须通过虚拟机提供的 JNI 接口与虚拟机进行交互,攻击者可以直接将指令转换 /VMP 加固方案当作黑盒,通过自定义的 JNI 接口对象,对黑盒内部进行探测、记录和分析,进而得到完整 DEX 程序。

第四代加固 DEX 文件恢复:

另外,第四代 VMP 加固技术只实现 Java 代码保护,没有做到使用 VMP 技术来保护 C /C++ 等代码,安全保护能力有所欠缺。

下一代加固技术—虚机源码保护

跟第四代的 VMP 加固技术,虚机源码保护加固是用虚机技术保护所有的代码,包括 Java,Kotlin,C/C++,Objective-C,Swift 等多种代码,具备极高的兼容性;使 App 得到更高安全级别的保护,运行更加稳定。

保护流程

虚机源码保护为用户提供一套完整的工具链,首先把用户待保护的核心代码编译成中间的二进制文件,随后生成独特的虚机源码保护执行环境和只能在该环境下执行的运行程序。

虚机源码保护会在 App 内部隔离出独立的执行环境,该核心代码的运行程序在此独立的执行环境里运行。即便 App 本身被破解,这部分核心代码仍然不可见。

虚机源码保护加固流程:

对抗优势

生成的虚机源码保护拥有独特的可变指令集,极大的提高了指令跟踪、逆向分析的难度。同时,虚机源码保护还提供了反调试能力和监控能力。虚机源码保护可以通过自身的探针感知到环境的变化,实时探测到外界对本环境的调试、注入等非正常执行流程变化,将调试动作引入程序陷阱,并发出警报,进而进行实时更新,提高安全强度。

加固技术发展及其攻防对抗的更迭,伴随着互联网技术发展不断升级,我们深信邪不能胜正,而虚机源码保护加固作为当前领先的加固技术,在未来很长一段时间,能够为 App 提供足够强度的保护,为企业和开发者的业务发展保驾护航。

————————————————

有加固需求的同学看这里:

Android 应用加固:https://www.dingxiang-inc.com/business/stee

iOS 应用加固:https://www.dingxiang-inc.com/business/ios

退出移动版