从编程思想到dex保护方案

编程架构

从我目前接触到的语言特性上来说,主要有以下几种。其中AOP是现在javaweb框架中很火的编程方式,而这种思想与逆向工程中的hook、代码保护不谋而合。因为其大火的形势,势必有很多AOP的方案与轮子,基于这种思考,借助AOP的轮子重新计划java2native的方案。

POP

“面向过程”(Procedure Oriented)是一种以过程为中心的编程思想。它强调的是系统的数据被加工和处理的过程,从思维上来讲,面向过程更强调细节,忽视了整体性和边界性,但这与现实世界有很大的出入,因为现实世界中,这种过程都不是孤立存在的,而是从属于某个对象。

OOP

面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)。世界是由一个个对象组成的,因此面向对象的思维方式更加接近现实世界,面向对象编程的组织方式也更加贴近现实世界。面向对象以对象为中心,将对象的内部组织与外部环境区分开来,将表征对象的内部属性数据与外部隔离开来,其行为与属性构成一个整体,而系统功能则表现为一系列对象之间的相互作用的序列,能更加形象的模拟或表达现实世界。

AOP

面向方面应该属于面向对象的范畴,从对象组织角度来讲,我们一般采用的分类方法都是使用类似生物学分类的方法,以“继承”关系为主线,我们称之为纵向。但事实上,对象之间除了这种纵向分类之外,我们同样可以从横向的角度去观察这些对象,这就是面向方面(切面)编程的基本出发点。原来要解决这类问题,我们一般是采用接口来完成,但这有两个问题,一是对象设计的时候一般都是纵向思维,如果这个时候需要就需要考虑这些不同类的对象的这些共性,不仅会增加设计的难度和复杂性,还会造成类的接口过多而难以维护,二是需要对现有的对象动态增加这种行为或者责任的时候非常困难。现在很多程序的都是以中间语言存在,执行的时候是解释执行或者即时编译执行,这也为增加这种切面行为或者责任提供了比较好的切入口。面向方面跟Api hook很类似。

方案设计

看郭lin的公众号推送时突然发现一个针对AOP在android下使用的文章,于是便优化了旧的实现路线。
现有的实现AOP技术如反射、代理等,大多为源码级别。更底层的字节码级别也有实现,这正是这里所需要的。

旧的方案

之前的方案是直接针对java代码,做到AST,之后使用llvm编译为符合虚拟机接口的本地代码。在IR这一层使用优化式混淆,并插入自己的反调试等代码。后来失败在:

  1. 语言选择问题,使用c语言得到的兼容十分不友好,而且代码很混乱,编码效率很低
  2. 从java代码开始自己用lex、yacc分析语法,这个做法十分不明智,也是造成最后失败的根本原因,语法十分庞大根本无法分析,且这俩个工具对c++的支持简直坑,最后写成AST也放弃了
  3. 对虚拟机的接口还不是很了解,这个问题现在也还是比较模糊。不过做到llvm的使用是必须的,因此llvm肯定是要用的,下面会给出俩套虚拟机接口方案。

新的考量

因为class文件是跨平台且直接的,dex可以与class文件互相转化。因此若针对class文件实现native化将使得该native方案是可跨平台的,不论一般的java还是anroid下的java、kotlin。
同时,针对class的解析库是有成熟实现的,如ASM等,它可以直接将class文件转化为AST,并且支持修改、重输出等。
因此流程为:

1
2
3
4
解析apk提出dex、修改xml->dex拆分为class->class化为AST->修改ASTclass抽出函数实现->
针对方法的AST->使用llvm本地代码化、同时注入保护代码与混淆->生成所需平台so
针对AST其他部分->源class删除函数实现、混淆符号->输出修改后的class->打包class为dex
->将so与dex加密,藏于壳dex-so中,在壳代码中动态不落地加载->打包apk

这个流程的核心是实际围绕dex与so实现的,注意dex是android下虚拟机的中间码,其表示形式是不会改变的。这样可以很好的提供稳定性与易于实现。不会依赖具体的版本。
其次针对class的部分是不依赖dex的,这样该模块可以用于其他地方。

所需支持:

  1. apk拆包:不用apktool解析为smali,有现成实现
  2. dex解析为class:google官方的实现就行
  3. class转化为AST:有现成实现-ASM就是一个,并且同时支持了重输出class。这部分的选择是前端的核心,因为直接决定了中间表示AST(注意AST用解析库带的,不要自己造!!!!很不标准根本没法用)
  4. AST到本地代码:llvm
  5. 本地代码与虚拟机接口:见下

有俩种本地执行java转过来代码的方案:

  1. class2cpp:这种直接把java代码翻译成jni调用。
  2. 自定义指令,执行时解析:这种翻译为自己的指令,用自己的虚拟机去执行。

第一种简单点,相当于调用虚拟机提供的本地代码接口。但保护效率不高,因为可以直接hook jni接口。
第二种是真正的vmp,相当于实现了一部分的虚拟机,需要对目标虚拟机的代码有很深了解,可以参考art的实现与art的aot翻译过程。

俩种都有llvm的用处,毕竟不论jni调用还是实现vm都是要翻译成本地代码才是可执行的,llvm正是负责编译器的后端部分,使自己实现、修改的代码可以在目标机运行。

实现相关信息

ASM是一个成熟的class文件<->AST转化框架。并提供AST实现

收集补充中….

参考

关于android下class文件的处理:http://quinnchen.me/2018/09/13/2018-09-13-asm-transform/?nsukey=GDUp4HTlidaahNx4EVO1O%2BhkBEpuPYfEk4P6tApFex%2BiH57sO5xDMo%2FCERuj%2BsioTD7lwwbpikC8Fu4oJHpysZW8jKq6lnAgncouuarFl18kMkLrDEqYOx91J9iodfQ9JxJ%2FYDakgBRndXZfzBuQs%2FSUOSczAz1c4USgv376C3n8192EeL91EJGgqZ7HcrgmkiB7ZzDlbh30zJvlFQFmuA%3D%3D

art的本地代码执行:https://www.jianshu.com/p/f6e3c8b6e120