前段时间老板思想又有点邪恶了,总想破解别人的软件,让我研究研究。于是我在膜拜之余也四方打探,决定从java字节码指令集和dalvik虚拟机的arm指令集入手,虽然后来由于非安全领域专业人员,就放弃了,不过对于技术来说,研究的价值不可遗失,遂成此博客。
当然本篇博客只会分析Java字节码解析和Android机器码解析,让那些邪恶的想法先消停消停,当然,有条件的同学,单凭看懂此篇的智慧,应该也不是什么难事。话归正题,接下来让我们一起揭开JVM和ARM指令集的神秘面纱。
要了解本章内容,先抛出几个问题:
1.JVM、Dalvik和Art是什么,他们的区别?
2..class和.dex的区别?
3.寄存器和虚拟栈是什么,他们的优缺点?
带着以上问题,我们来具体分析一下,首先上一张java虚拟机和Android虚拟机内部结构图如下:
根据以上结构图可知,java虚拟机与Android虚拟机唯一区别是执行引擎不同,jvm执行字节码指令,Android虚拟机执行机器码指令.
有兴趣的朋友,可以深入剖析每个模块的细节,这里贴一张图,不再做过多叙述
为剖析jvm执行引擎与Android虚拟机执行引擎的区别,接下来写一段代码来说明细节:
如下代码,类中写了一个方法,声明了变量相加
Java字节码解析
使用编译工具把它变成.class字节码,这个字节码由于是二进制文件,使用文本查看工具是看不了的,需要通过JDK中的javap工具解析,Win+R调出命令行窗口,输入以下指令
可以看到如下图,内容
别的可以不看,直接看红框标注的内容,给它拷贝出来分析:(对于jvm指令,可以参考)
为便于理解,这里用一张图来辅助分析以上字节码执行流程
Android虚拟机指令解析
同样的,由于.dex是通过.class文件编译形成的,也就是.class对于Android来说是属于中间过渡文件,所以先要在android sdk 下使用dx批处理指令从.class中打包出dex文件(注意:路径要添加一下环境变量,或者把class文件拷贝到sdk这个目录下),然后再将dex文件使用指令解析
这里我们通过命令一气呵成,执行命令:
运行指令后,得到如下结果,其中以local开头的都是在执行命令中打印的日志,实际机器码中是不存在的,我们只需要关注红色框框的内容
整理一下
为了帮助理解,同样也做了一张图辅助分析
通过以上分析,下面回答上面提到的几个问题:
问题1:
针对JVM和Android虚拟机:
1..class运行在JVM上,.dex运行在Android虚拟机上;
2.JVM运行字节码存在冗余信息,Android虚拟机运行机器码去除了冗余信息;
3.JVM是基于虚拟栈的结构,Android虚拟机是基于寄存器结构;
针对Dalvik和Art:
1.Dalvik环境下需要每次在程序运行是通过即时编译器(JIT)将字节码转换成机器码,即每次都要编译加运行,虽然这样提升了安装速度,但会拖慢应用每次启动的效率;
而Art环境下,在安装时字节码就会预编译(AOT)成机器码,虽然安装的时间变长了,但是以后每次启动执行都可以直接运行,运行效率会提高,也就是典型的空间换时间的模式;
2.由于Art通过预编译成机器码,所以会比Dalvik占用更多的存储空间,也由于这个特性,因此不用每次运行前都要先编译,从而减少了CPU的使用率,降低了能耗;
问题2:
1.dex减少了整体的文件尺寸,就像是一个压缩文件,可以表示更多的类,而class则像是单个的文件;
2.Android虚拟机加载类时,只对dex进行一次IO操作可以加载很多类,而多个class则需要多次IO才能完成加载,提高了查找速度;
3.class指令较多,dex指令更加密集;
4.dex指令方便寻址,而class需要多次入栈(load)和弹栈(store)指令;
5.class适用于PC大内存,单指令小的情况快速运行,dex适用于移动设备性能不太高的要求;
问题3:
1.虚拟栈是通过无变量声明来操作虚拟机执行,使用的指令只占一个字节,由于使用栈操作,使指令变得很紧凑,但由于load/store的频繁调用,意味着更多的指令分派次数和内存访问次数,访问内存是执行速度的一个重要瓶颈;
2.寄存器是通过变量寻址来达到操作虚拟机执行,由于需要指定源地址和目标地址,所以每条指令需要占用更多的空间,但总体可以用更少的指令完成操作,指令分派和内存访问次数都较少,但由于访问内存次数较少,数据缓冲更易失效;
综上,基于栈的操作需要更多的指令,而基于寄存器则需要更多的指令空间。栈需要更多的指令意味着需要占用CPU更多的时间,寄存器需要更多指令空间意味着数据缓冲更易失效。是否基于虚拟栈和寄存器最本质的区别就是有无变量声明。
总结
JVM指令明显更多,更消耗CPU,由于每个指令只占1个字节,占用空间少;
Android虚拟机指令少,由于指令分配了源地址和目的地址,使得指令空间变大,更浪费空间