目录:
一、APP加固背景二、APP加固前世今生三、整体框架四、详细流程分析五、总结
Android系统是基于Linux开发己具有其开放性、自由性的一种操作系统,现主要应用于移动设备,如手机、平板电脑和车载系统等。从2007年Google推出第一代Android操作系统至今已有10多年的时间,移动行业的市场份额与规模也在急速增长,现在几乎每人都在用或曾用过接触过Android智能手机。
移动APP越来越普及,大多业务己放到APP中完 成,带来的安全隐患也越来越突出,漏洞、APP破解、恶意代码植入、广告植入、病毒木马、支付篡改、数据爬取等安全问题。
在发版前可通过对APP进行安全检测,加固APP可以提高安全性,解决大部分风险。
App加固技术,前后经历了四代技术变更,保护级别在每一代都有所提升,破解成本也越来超高,发展流程大致如图2-1所示:
图2-1
第一代Android加固技术用于保护应用的逻辑不被逆向与分析,最早普遍在恶意软件中使用,其主要基于虚拟机提供的动态加载技术。
缺陷:只能对抗静态分析,无法对抗攻击者通过动态调试或自定义虚拟机进行脱壳。
第二代加固技术在APK修改方面已经完善,能做到对开发的零干扰。开发过程中不需要对应用做特殊处理,只需要在最终发布前进行保护即可。而为了实现这个零干扰的流程,Loader需要处理好Android的组件的生命周期。hook读写等方法,读写文件时进行加解密。
缺陷:只能对抗静态分析,无法对抗攻击者通过动态调试、内存dump或自定义虚拟机进行脱壳。
第三代加固技术对Dex中代码的方法名和方法体进行分离,并对分离的方法体进行加密,通过Hook虚拟机方法,在程序运行的同时对方法进行解密运行。这种保护技术有效的防止了破解者通过内存dump的方式获取明文dex,将保护级别降到了函数级别。
缺陷:无法对抗攻击者通过自定义Android虚拟机进行脱壳。
第四代加固技术对DEX中方法提取并转化成native方法后在底层进行注册,在调用native方法的同时在底层使用自定义解释器解释虚拟机指令。
java2c是将DEX文件内的函数被标记为native,内容被抽离并转换成一个符合JNI要求的动态库。动态库内通过JNI方法和Android系统进行交互。
缺陷:不论VMP还是java2c,都必须通过虚拟机提供的JNI接口与虚拟机进行交互,攻击者可以直接hook系统JNI接口、记录和分析执行流程,进而推断出完整DEX程序。
梆梆加固产品主要分为免费版与定制版,应用场景如下:
抽取classes.dex中的所有代码,剥离敏感函数功能,混淆关键逻辑代码,整体文件深度加密加壳,防止通过apktool,dex2jar,JEB等静态工具来查看应用的Java层代码,防止通过IDA,readelf等工具对so里面的逻辑进行分析,保护native代码。
每个代码文件、资源文件、配置文件都会分配唯一识别指纹,替换任何一个文件,将会导致应用无法运行,存档替换、病毒广告植入、内购破解、功能屏蔽等恶意行为将无法实施。
多重加密技术防止代码注入,彻底屏蔽游戏外挂、应用辅助工具,避免钓鱼攻击、交易劫持、数据修改等调试行为。
支持存储数据加密,提供输入键盘保护、通讯协议加密、内存数据变换、异常进程动态跟踪等安防技术,有效防止针对应用动、静态数据的捕获、劫持和篡改。免费版与定制版本区别如图2-2所示:
图2-2
免费版本相对于定制化加固在安全能力方面要弱很多,我们分析的目标是定制版本的加固。
梆梆定制版加固保护代码的方式主要分为两种,一是指令抽取,二是指令虚拟化,指令还原与虚拟化基本的流程与逻辑如图3-1所示:
图3-1
通过JEB反编译DUMP出来的DEX,方法指令抽取与指令虚拟化后如图4-1与4-2所示,方法指令抽取后的结果如图4-1所示:
图4-1
方法指令虚拟化后大部分函数是调用JniLib.cV解析执行的,最后一个参数是一个函数code索引,用来查找被虚拟化后的指令,其它是方法参数,如图4-2所示:
图4-2
壳的SO文件本身做了加壳保护,壳入口为.init_proc,如图4-3所示:
图4-3
解壳流程如大致为获取加密的代码基址->解密->修改属性,解密代码如下:
#define CODE_DATA_SIZE 0X00053DB3
壳解密完成后执行到JNI_OnLoad方法,JNI_OnLoad主要完成几个重要步骤。
1、注册JNI方法,方法如下:
i()V
2、hook libc.so方法,hook如下方法:
__open
3、读取classes0.jar、classes.dgc并解密,代码逻辑如下:
//读取资源文件
classes0.jar:解密出DEX明文。
classes.dgc:解密出被抽取的指令。
4、hook libart.so虚拟机方法
我调试的系统版为android 9,该系统主要hook了如下两个方法:
art::ArtDexFileLoader::Open
5、反调试
反调试主要分为以下几种方式:
1、fork多进程相互监控。
2、多线程监控,创建3个线程监控调试状态,检查TracerPid和命令行中的gdb gdbserver android_server,文件监控。
3、hook ptrace 方法判断是否有调试器。
4、hook art::Runtime::AttachAgent 监控是否在调试
5、hook vmDebug_notifyDebuggerActivityStart监控调试器
当被抽取指令的方法被执行时,会调用art::ClassLinker::LoadMethod,在hook LoadMethod中,先判断是否为目标方法,然后修复,会给每一个加载的目标类方法设置一个DEX文件类方法索引,以及关联一个ArtMethod对象指针数组,如下所示:
//判断是否为目标方法
VMP的实现逻辑是在libdexjni.so中,在java层注册如下几个jni方法供被虚拟化的方法调用:
public static native byte cB(Object[] arg0)
如果加载的方法中调用JniLib.cV这样的Native方法时,该方法指令被虚拟化,如图4-4所示:
图4-4
JNI方法中前几个参数为原始方法参数,最后一个整形参数是一个索引值,虚拟机解释执行的过程主要分为如下几个步骤:
1、根据Java层传入的最后一个整形参数索引值获取方法的DexCode指令,代码如下:
seg135:C6299EF0 loc_C6299EF0
2、从获取的数据内容还原结构体如下:
strut VMJavaInfo {
3、通过上面结构体获取dexcode指针,代码如下:
seg135:C629A0F4 loc_C629A0F4
dexcode结构体如下:
struct DexCode {
4、根据DexCode结构体获取自定义指令,代码如下:
seg135:C632663A C8 68 LDR R0, [R1,#0xC] ; 取得DexCodeInfo数据
5、解密自定义指令,代码如下:
//解密指令
6、VMP解释执行,代码如下:
//根据解密出来的指令计算对应的handle地址
7、循环取指令解密指令到跳转到对应Handle执行的模板代码如下:
seg135:C62A78E2 A4 48 LDR R0, =(_GLOBAL_OFFSET_TABLE_ - 0xC62A78E8)
8、指令模拟过程
一条VMP指令:
48 10 10 19
上面指令是设置BaseEntity中的code字段值(int类型),代码模拟如下:
seg135:C62D16DA 01 9E LDR R6, [SP,#0x40+var_3C]
其它指令也是类似的解释过程进行模拟执行。
梆梆企业定制版主要功能与上面产品介绍中功能相同,主要是DEX文件加壳保护、DEX抽取加密、DEX虚拟化保护(VMP)、多Dex加固保护、SO文件加壳保护。壳本身做了加壳与指令混淆,每一个Handle之间都连接着的,再加上流程上混淆,字符串加密,方法名混淆,反调试,检测自动脱壳框架,增加了一定的逆向分析难度。
从Java层到Native层都做了相应的保护,由其是代码虚拟化加上指令混淆抗破解能力还是比较强的,要想完整还原Java代码还是需要花很多的时间与精力,整体而言从个人破解者角度来说安全系数还是比较高的。