Java编程语言的真核心是其虚拟机(Java Virtual Machine or JVM),JVM是真正的让Java宣言『Write Once,Run Anywhere』变成现实,JVM封装并隔离了不同的OS,JVM有它自己的标准和规范,从而凡是符合JVM的『代码』都可以在JVM上运行。Java编程语言并不是直接运行在JVM上面的,Java语言只是套在JVM上面的一层语言规则。
准确的说JVM接收的是一套叫做字节码(Bytecode)的东西,只要是能把一套语法规则『翻译』成为符合JVM规范的字节码,就可以在JVM上面运行,除了正统的Java之外,Scala,Groovy,以及Kotlin等等都是这样实现的,它们编译之后得到的就是字节码文件,字节码文件可直接运行在JVM之上。
那么字节码才是Java编程语言的真核心,值得深入研究和学习。前面写过一篇介绍安卓高级逆向方法的文章,里面涉及到一些JVM的高级技术,还需要进一步的深入学习一下,以能更好的理解插件化和热修复的核心原理。
ClassLoader
除了标准Java中的以外,在Android当中的ClassLoader也要深入学习一下,这个是相当多的逆向技术的基础,基本的原理和流程如委托机制看文章或者文档就可以了。
需要重点记住的就是两点核心要点,一是ClassLoader是类的作用域,它是类的沙箱,同一个ClassLoader里面只能有一个类,必须唯一,但不同的ClassLoader对象,可以有同样的类。这里类的意思是全量类名,也即其packageName+ClassName,Fully Qualified Name。第二就是惰性加载机制,也就是说,对于同一个ClassLoader对象,一个类只会加载一次,加载过了,就不会再去loadClass了。
根据这两个核心要点,就理解了各种热修复的原理了,惰性加载机制决定了热生效和冷生效,因为ClassLoader只加载一次,所以Class替换的方式,只能下次启动生效(准确的说是下次需要loadClass时生效)。而替换的方法就是把修复的patch的想办法放在常规的前面,这样就会优先加载要替换的类了。
注意:Android中并不是官方Java的bytecode,而一种叫做dex的东西,它是在编译时把标准Class文件经过转化再打包到一起形成的,最初安卓的VM叫做Dalvik,所以就把它的字节码命名为dex意即Dalvik Executable,这是dex的由来。虽然目标格式不一样,但是dex与class是可以自由转换的,且dex的生成在编译为标准class之后的,所以字节码的一切工具,对于安卓也都可以用。
Android中的ClassLoader,重点是DexPathList,它里面决定了各个dex的顺序,插件和热修复基本上都是在dex的顺序 上做文章,要么是把新的dex放在最前面,要么是找到原dex,然后替换,这就是核心原理,也是这一套逆向方法的可行之处。
反射
也即是运行时修改代码的能力,它是直接去修改JVM中的代码,也即是修改bytecode。纯编译型语言如C/C++是不可能有这种能力的。Java有这种能力是因为JVM的存在,编译只是把源码『翻译』成字节码。
原生东西不好用,还是用三方库来反射jOOR。
JNI
一些三方的号称可以热修复的工具如exposed和Andfix这些东西,之所以能够实现,是因为JVM本身就开了口子支持JNI,为了能让方法能让JVM找得到,就需要一个method table,而此method table是可以被修改的。 Guide to JNI (Java Native Interface)
Java Native Interface(JNI)从零开始详细教程
如果JNI接口较多,较复杂,建议用SWIG,参见它的说明文档。
Swig有点重了,这个库也相当的好用JniHelpers。
动态代理
除了官方JDK支持的以接口为基础的动态代理 方式之外,还有其他几种以子类化方式实现动态代理,但它们都是基于ASM的。
代码覆盖率检测
Mock
这是自动化测试以及单元测试必然会用到的利器。
ASM
这是一个神器,专门用来处理字节码的,所有其他的Java底层工具都是基于它来实现的,足可见它的牛逼之处。
A Guide to Java Bytecode Manipulation with ASM
Dependency Injection
- Using dependency injection in Java
- What is Dependency Injection with Java Code Example
- What Is Dependency Injection? – Know How To Implement Dependency Injection
AOP
- Aspect-Oriented Programming (AOP)in Java
- Java Aspect Oriented Programming with AspectJ (AOP)
- Spring AOP Example
- AspectJ
- Aspectj source
- Intro to AspectJ
研究字节码的意义
所有这些基于字节码的工具和技术存在的意义,是帮助我们如何更好的写出Java代码,而并不是纯粹去做一些逆向工程的事情。比如,效率工具,测试工具,调试工具和动态生成代码的技术等等。