初识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?

    1. 专利与授权:早期 Oracle 对 JVM 专利控制严格,Android 选择绕过。
    2. 移动端优化:Dalvik/ART 针对低内存、低功耗设备设计(如寄存器架构、Dex 优化)。
    3. 生态隔离:Android 通过自定义运行时避免与 Java ME 生态冲突。

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 method

    https://www.52pojie.cn/thread-650395-1-1.html

    安卓游戏内购破解之滚动的天空 - 吾爱破解 - 52pojie.cn

    《教我兄弟学Android逆向03 破解第一个Android游戏 》 - 吾爱破解 - 52pojie.cn

3.示例

  • 1.首先安装好核心破解,勾选后重启可以使用

  • 2.安装课程所需Demo

    https://pan.baidu.com/s/1cUInoi 密码:07p9

  • 3.在jadx中查询关键字(有时候反编译过程中会将汉字反编译为unicode编码)

  • 修改代码逻辑

    将判断条件改为强制跳转

    跳转后注释掉其判断条件,不管是否为真都执行下列逻辑

切水果大战


初识Smail
https://cc-nx.github.io/2025/03/22/ZJ3/初识smail 1aba6f4b4daf80fb9bbfc0bb1c274e44/
作者
CC
发布于
2025年3月22日
许可协议