初识Smail
初识smail
1.什么是JVM、Dalvik、ART
JVM是JAVA虚拟机,运行JAVA字节码程序
Dalvik是Google专门为Android设计的一个虚拟机,Dalvik有专属的文件执行格式dex(Dalvik executable)
Art(Android Runtime)相当于Dalvik的升级版,本质与Dalvik无异
JVM 是 Java 生态的通用运行时,Dalvik 是 Android 早期的移动优化方案,ART 是更现代的 Android 运行时,通过预编译和混合模式显著提升性能。三者的演进反映了从跨平台兼容性到移动端极致性能的权衡
1. JVM(Java Virtual Machine)
- 定义:JVM 是 Java 平台的核心组件,负责执行 Java 字节码(
.class
文件)。 - 特点:
- 跨平台:遵循“一次编写,到处运行”的理念,通过字节码和 JVM 的适配实现跨平台。
- 基于栈的架构:指令操作基于栈(操作数栈),指令更紧凑但可能执行较慢。
- JIT 编译(Just-In-Time):运行时将热点代码(频繁执行的代码)动态编译为本地机器码以提高性能。
- 应用场景:传统 Java 应用(如桌面、服务器端程序)。
2. Dalvik
- 定义:Dalvik 是 Android 早期(5.0 之前)使用的虚拟机,专为移动设备优化。
- 特点:
- 基于寄存器架构:指令直接操作寄存器,执行效率更高(但指令更长)。
- 运行 Dex 文件:将多个
.class
文件合并优化为单个.dex
文件,减少体积和冗余。 - JIT 编译:在应用运行时动态编译字节码为机器码,但每次启动都需要重新编译。
- 多实例设计:每个 Android 应用运行在独立的 Dalvik 进程中,实现沙盒隔离。
- 局限性:JIT 编译导致启动慢、内存占用高,且运行时性能不如预先编译。
3. ART(Android Runtime)
- 定义:ART 是 Android 5.0(Lollipop)及之后取代 Dalvik 的新一代运行时。
- 核心改进:
- AOT 编译(Ahead-Of-Time):安装时将 Dex 字节码完全编译为本地机器码,直接执行,大幅提升运行速度和能效。
- 性能优化:减少运行时编译开销,CPU 和内存效率更高。
- 垃圾回收改进:减少暂停时间,支持并行处理。
- 支持 64 位:更好地利用现代硬件。
- 代价:应用安装时间变长,存储占用增加(需存储编译后的机器码)。
- 混合编译(Android 7.0+):结合 JIT 和 AOT,安装时仅部分编译,运行时收集热点代码再优化,平衡速度和存储。
对比总结
特性 JVM Dalvik ART 架构 基于栈 基于寄存器 基于寄存器(兼容 Dex) 编译方式 JIT JIT AOT + JIT(混合模式) 文件格式 .class
文件.dex
文件.dex
→ 本地机器码性能 适中(跨平台代价) 启动慢,运行时优化 启动快,运行高效 存储占用 低 低 较高(存储预编译机器码) 适用场景 桌面/服务器 Java 应用 早期 Android 设备 现代 Android 设备(5.0+)
为什么 Android 不用 JVM?
- 专利与授权:早期 Oracle 对 JVM 专利控制严格,Android 选择绕过。
- 移动端优化:Dalvik/ART 针对低内存、低功耗设备设计(如寄存器架构、Dex 优化)。
- 生态隔离:Android 通过自定义运行时避免与 Java ME 生态冲突。
- 定义:JVM 是 Java 平台的核心组件,负责执行 Java 字节码(
2.smali及其语法
smali是Dalvik的寄存器语言,smali代码是dex反编译而来的。(静态修改前提条件)
【原木文章】Android改造者之路-02.初探smali功法 - 吾爱破解 - 52pojie.cn
吾爱破解安卓逆向入门教程(三)-深入Smali文件 - 吾爱破解 - 52pojie.cn
吾爱破解安卓逆向入门教程(四)-Smali函数分析 - 吾爱破解 - 52pojie.cn
关键字
名称 | 注释 |
---|---|
.class | 类名 |
.super | 父类名,继承的上级类名名称 |
.source | 源名 |
.field | 变量 |
.method | 方法名 |
.register | 寄存器 |
.end method | 方法名的结束 |
public | 公有 |
protected | 半公开,只有同一家人才能用 |
private | 私有,只能自己使用 |
.parameter | 方法参数 |
.prologue | 方法开始 |
.line xxx | 位于第xxx行 |
数据类型对应
smali类型 | java类型 | 注释 |
---|---|---|
V | void | 无返回值 |
Z | boolean | 布尔值类型,返回0或1 |
B | byte | 字节类型,返回字节 |
S | short | 短整数类型,返回数字 |
C | char | 字符类型,返回字符 |
I | int | 整数类型,返回数字 |
J | long (64位 需要2个寄存器存储) | 长整数类型,返回数字 |
F | float | 单浮点类型,返回数字 |
D | double (64位 需要2个寄存器存储) | 双浮点类型,返回数字 |
string | String | 文本类型,返回字符串 |
Lxxx/xxx/xxx | object | 对象类型,返回对象 |
常用指令
关键字 | 注释 |
---|---|
const | 重写整数属性,真假属性内容,只能是数字类型 |
const-string | 重写字符串内容 |
const-wide | 重写长整数类型,多用于修改到期时间。 |
return | 返回指令 |
if-eq | 全称equal(a=b),比较寄存器ab内容,相同则跳 |
if-ne | 全称not equal(a!=b),ab内容不相同则跳 |
if-eqz | 全称equal zero(a=0),z即是0的标记,a等于0则跳 |
if-nez | 全称not equal zero(a!=0),a不等于0则跳 |
if-ge | 全称greater equal(a>=b),a大于或等于则跳 |
if-le | 全称little equal(a<=b),a小于或等于则跳 |
goto | 强制跳到指定位置 |
switch | 分支跳转,一般会有多个分支线,并根据指令跳转到适当位置 |
iget | 获取寄存器数据 |
if-eqz 另一个是if-nez 这两条指令是相对的
(1)if-eqz vA, vB, :cond_**” 如果vA等于vB则跳转到:cond_**
(2)if-nez vA, vB, :cond_**” 如果vA不等于vB则跳转到:cond_**
其余指令可用语法工具查询
定位方法:搜索弹窗关键字、抓取按钮id
分析示例
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93//一个私有、静态、不可变的方法 方法名
.method private static final onCreate$lambda-2(Lkotlin/jvm/internal/Ref$IntRef;Lcom/zj/wuaipojie/ui/ChallengeSecond;Landroid/widget/ImageView;Landroid/widget/ImageView;Landroid/widget/ImageView;Landroid/view/View;)Z //(这里面是方法的参数)这里是方法返回值类型,表示布尔值类型,返回假或真
.registers 7 //寄存器数量
.line 33 //代码所在的行数
iget p0, p0, Lkotlin/jvm/internal/Ref$IntRef;->element:I //读取p0(第一个参数,参考寄存器知识)中element的值赋值给p0
const/4 p5, 0x1 //p5赋值1
const/16 v0, 0xa //v0赋值10,在16进制里a表示10
if-ge p0, v0, :cond_15 //判断p0的值是否大于或等于v0的值(即p0的值是否大于或等于10),如果大于或等于则跳转到:cond_15
.line 34 //以下是常见的Toast弹窗代码
check-cast p1, Landroid/content/Context; //检查Context对象引用
const-string p0, "请先获取10个硬币哦" //弹窗文本信息,把""里的字符串数据赋值给p0
check-cast p0, Ljava/lang/CharSequence; //检查CharSequence对象引用
invoke-static {p1, p0, p5}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
//将弹窗文本、显示时间等信息传给p1
move-result-object p0 //结果传递给p0
invoke-virtual {p0}, Landroid/widget/Toast;->show()V //当看到这个Toast;->show你就应该反应过来这里是弹窗代码
goto :goto_31 //跳转到:goto_31
:cond_15 //跳转的一个地址
invoke-virtual {p1}, Lcom/zj/wuaipojie/ui/ChallengeSecond;->isvip()Z //判断isvip方法的返回值是否为真(即结果是否为1)
move-result p0 //结果赋值给p0
if-eqz p0, :cond_43 //如果结果为0则跳转cond_43地址
const p0, 0x7f0d0018 //在arsc中的id索引,这个值可以进行查询
.line 37
invoke-virtual {p2, p0}, Landroid/widget/ImageView;->setImageResource(I)V //设置图片资源
const p0, 0x7f0d0008
.line 38
invoke-virtual {p3, p0}, Landroid/widget/ImageView;->setImageResource(I)V
const p0, 0x7f0d000a
.line 39
invoke-virtual {p4, p0}, Landroid/widget/ImageView;->setImageResource(I)V
.line 40
sget-object p0, Lcom/zj/wuaipojie/util/SPUtils;->INSTANCE:Lcom/zj/wuaipojie/util/SPUtils;
check-cast p1, Landroid/content/Context;
const/4 p2, 0x2 //p2赋值2
const-string p3, "level" //sp的索引
invoke-virtual {p0, p1, p3, p2}, Lcom/zj/wuaipojie/util/SPUtils;->saveInt(Landroid/content/Context;Ljava/lang/String;I)V //写入数据
goto :goto_50 //跳转地址
:cond_43
check-cast p1, Landroid/content/Context;
const-string p0, "\u8bf7\u5148\u5145\u503c\u5927\u4f1a\u5458\u54e6\uff01" //请先充值大会员哦!
check-cast p0, Ljava/lang/CharSequence;
invoke-static {p1, p0, p5}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object p0
invoke-virtual {p0}, Landroid/widget/Toast;->show()V
:goto_50
return p5 //返回p5的值
.end method //方法结束
//判断是否是大会员的方法
.method public final isvip()Z
.registers 2
const/4 v0, 0x0 //v0赋值0
return v0 //返回v0的值
.end methodhttps://www.52pojie.cn/thread-650395-1-1.html
3.示例
1.首先安装好核心破解,勾选后重启可以使用
2.安装课程所需Demo
https://pan.baidu.com/s/1cUInoi 密码:07p9
3.在jadx中查询关键字(有时候反编译过程中会将汉字反编译为unicode编码)
修改代码逻辑
将判断条件改为强制跳转
跳转后注释掉其判断条件,不管是否为真都执行下列逻辑