稀有猿诉

十年磨一剑,历炼出锋芒,说话千百句,不如码二行。

深入学习Java虚拟机知识

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之外,ScalaGroovy,以及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的存在,编译只是把源码『翻译』成字节码。

Guide to Java Reflection

原生东西不好用,还是用三方库来反射jOOR

JNI

一些三方的号称可以热修复的工具如exposed和Andfix这些东西,之所以能够实现,是因为JVM本身就开了口子支持JNI,为了能让方法能让JVM找得到,就需要一个method table,而此method table是可以被修改的。 Guide to JNI (Java Native Interface)

Java Native Interface(JNI)从零开始详细教程

java native方法与JNI实现

JNI本身会降低效率吗?

如果JNI接口较多,较复杂,建议用SWIG,参见它的说明文档

Swig有点重了,这个库也相当的好用JniHelpers

动态代理

除了官方JDK支持的以接口为基础的动态代理 方式之外,还有其他几种以子类化方式实现动态代理,但它们都是基于ASM的。

Dynamic Proxies in Java

Java的动态代理(dynamic proxy)

动态代理大揭秘,带你彻底弄清楚动态代理!

代码覆盖率检测

Cobertura

JaCoCo

Intro to JaCoCo

Mock

这是自动化测试以及单元测试必然会用到的利器。

Mockito

EasyMock

ASM

这是一个神器,专门用来处理字节码的,所有其他的Java底层工具都是基于它来实现的,足可见它的牛逼之处。

A Guide to Java Bytecode Manipulation with ASM

Dependency Injection

AOP

研究字节码的意义

所有这些基于字节码的工具和技术存在的意义,是帮助我们如何更好的写出Java代码,而并不是纯粹去做一些逆向工程的事情。比如,效率工具,测试工具,调试工具和动态生成代码的技术等等。

参考资料

Comments