-
-
[原创]Java层逆向分析方法和技巧
-
发表于:
2023-2-8 22:11
7239
-
本公众号分享的所有技术仅用于学习交流,请勿用于其他非法活动,如有错漏,欢迎留言指正
Java层逆向分析方法和技巧
一、smali汇编
1. Dalvik字节码
- java字节码、Dalvik字节码、机器码之间的关系?
- 在Android上,
Java代码
首先经过编译器编译成Java字节码
,然后再经过dx工具转换成Dalvik字节码
,并打包到apk文件中(存储在DEX(Dalvik Executable)文件中),Dalvik字节码最终转换为机器码
Android设备上运行。(因为大多数Android设备都是使用ARM处理器。因此,Android系统对应用程序的代码进行编译时,生成的是与ARM处理器兼容的机器码
。)
- 编译器编译Java代码成
Java字节码
是为了将Java代码转换成机器无关
的中间代码,使得编译后的代码可以在任何支持JVM的系统上运行,不受机器的体系结构的限制。
- 而
Dalvik字节码
是专门为Android系统优化的字节码,它比Java字节码更加高效。Dalvik字节码在一定程度上是Java字节码的一个子集
。
- Java字节码和Dalvik字节码是两种不同的字节码格式,它们的区别主要体现在
文件格式
(java字节码文件扩展名为.class,Dalvik字节码文件扩展名为.dex。),存储方式
(Java字节码在Java虚拟机中一次性加载,并在内存中存储;Dalvik字节码是以dalvik指令为单位存储的,仅加载需要的部分。),运行方式
等方面(Java字节码直接在Java虚拟机中执行,Dalvik字节码在Dalvik虚拟机中执行,但需要经过dalvik指令集解释执行
(Dalvik字节码->arm机器码)。)
- Dalvik字节码和smali的关系?
Smali
是一种对Dalvik字节码
进行编码的人类可读的汇编语言,是对Dalvik指令的高级抽象。
Dalvik字节码
是以二进制形式存在于 .dex
文件中,而 smali
是以文本形式存在于磁盘文件中,是将 Dalvik字节码
反编译回文本文件的结果。
- 为什么有了
ART
虚拟机,为什么还需要学Dalvik
字节码?
- Dalvik虚拟机是在
启动应用程序时
,将Dalvik字节码动态编译
成机器码来实现运行的。所以对于每次打开一个应用程序,是需要进行编译的。
- 在Android 5.0以后的版本,ART已经取代了Dalvik作为Android的默认虚拟机。
- ART虚拟机机是在
安装应用程序时
,将Dalvik字节码预先编译
成机器码来实现运行的。因此不再需要在每次启动应用程序时进行编译,提高运行速度。
- 可以看到只是编译的时机不同而已。无论是Dalvik虚拟机还是ART虚拟机,
dex文件是没有变的
,即还是Dalvik
字节码,反汇编得到Smali汇编,可以通过Smali汇编语言重写和修改应用程序的Dalvik字节码,以实现各种定制化效果和优化性能。2. Smali汇编
寄存器命名方法:V与P
- JAVA虚拟机使用了
栈架构
,Java字节码被执行时通过一个操作栈来进行解释,每个方法在运行时都有一个私有的操作栈。
- Dalvik (ART)虚拟机基于
寄存器架构
,不是使用操作栈来执行字节码,而是使用寄存器,速度快,节省代码。
- V命名法:所有变量(局部,参数都用V0,1,...命名)
- P命名法:
局部变量用V
,参数用P
,容易判断局部变量和参数
类型描述符
C
表示字符(char),用来存储 8 位字符,用一个32位的Dalvik寄存器来存储。
l
表示布尔型(boolean),用来存储 8 位布尔型,用一个32位的Dalvik寄存器来存储。
S
表示短整型(short),用来存储 16 位短整型,用一个32位的Dalvik寄存器来存储。
I
表示整型(int),用来存储 32 位有符号整数。用一个32位的Dalvik寄存器来存储。
J
表示长整型(long),用来存储 64
位有符号整数。用两个相邻的32位Dalvik寄存器来存储。
F
表示单精度浮点类型(float),用来存储 32 位单精度浮点数。用一个32位的Dalvik寄存器来存储。
D
表示双精度浮点类型(double),用来存储 64
位双精度浮点数。用两个相邻的32位Dalvik寄存器来存储。
v
表示无返回值的void类型
[
表示数组类型,后面跟着一个 Smali 类型描述符,代表这个数组存储的数据类型。
[I
表示int []
[II
表示int[][]
,每多一维就加一个方括号,最多可以设置255维。
L
+对象的全限定名
表示对象类型(L``Package/Name/ObjectName;
注意后面分号结束)
- 比如String,其完整名称是
java.lang.String
,那么其全限定名就是java/lang/String;
,即java.lang.String
的”.”用”/”代替,并在末尾添加分号”;”做结束符.
- 成员:
Lcom/MyClass;``->``name:Ljava/lang/String;
(`对象类型;->成员名:成员类型)``
- 方法:
LPackage/Name/objectName;``->``myFunc(III)Z
(对象类型;->函数名:(参数类型)返回值
)III
表示3int参数
- 函数:fun(
Z
[I
[II
Ljava/lang/String;
J
[Ljava/lang/Object;
)Ljava/lang/String;
(string fun(boolean, int[],int[][],String,long,Object[]
))
- 直接方法:static、构造、包含静态语句块(比如静态数组初始化)
- 虚方法
指令
- 数据定义指令
- 数据操作指令
- 实例操作指令
- 数组操作指令
- 比较指令
- 跳转指令
- 字段操作指令
- 数据转换指令
- 算术指令
- 空指令锁指令
- 异常指令
- 方法调用指令
- 返回指令
3. smali文件详解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | . class public Lcom / example / helloworld / HelloWorld;
. super Ljava / lang / Object ;
.method public static main([Ljava / lang / String;)V
.registers 4
.parameter 1
. locals 3
.prologue
nop
const / 16 v0, 0x8
const / 4 v1, 0x5
const / 4 v2, 0x3
move v1, v2
new - array v0, v0, [I
array - length v1, v0
new - instance v1, Ljava / lang / StringBuilder;
invoke - direct {v1}, Ljava / lang / StringBuilder; - ><init>()V
if - nez v0, :cond_0
goto :goto_0
:cond_0
int - to - float v2, v2
add - float v2, v2, v2
cmpl - float v0, v2, v2
sget - object v0, Ljava / lang / System; - >out:Ljava / io / PrintStream;
const - string v1, "Hello World"
invoke - virtual {v0, v1}, Ljava / io / PrintStream; - >println(Ljava / lang / String;)V
:goto_0
return - void
.end method
|
还原成java代码
更多内容请移步公众号:坚毅猿
https://mp.weixin.qq.com/s?__biz=MzU5NzE2NjMyMw==&mid=2247487537&idx=1&sn=26d9fea3d93a5d998cdc5a317575f834&chksm=fe56c1adc92148bbb33f0335696a4f2f1403d6c1fdc7bc2036b4250876a3bd8c23a8a7331cf5&payreadticket=HGWO0ZYJbRzFkh4u1ETH6GEVMQki22kKGBZvvQE6s6nav5Fs2BZwrVUry8U1pH1kYqpz1xw#rd
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2023-2-9 11:04
被公众号坚毅猿编辑
,原因: