1、JVM、Dalvik、ART
JVM 是 JAVA 虚拟机,运行 JAVA 字节码程序。
Dalvik 是 Google 为 Android 设计的虚拟机,Dalvik 有专属的文件执行格式 dex。
Art (Android Runtime)相当于 Dalvik 的升级版,本质与 Dalvik 无异。
2、smali 及其语法
smali 是 Dalvik 的寄存器语言,smali 代码是 dex 反编译而来的。
其实和汇编语言很相似。

到手一个程序,要收集硬币,并且完成一键三连,我们点击一键三连会提示让我们充值大会员。
我们这里记住他的关键词,大会员。

看到了关键字了,并且他有两条路径,根据逆向经验,肯定是要把一键三连后面跳转的函数改为当前已经是大会员。

找到了,其实这里就是一个非常简单的点,就是结果跳转,我可以理解为这里是进行 Patch 的操作。

在这里就是 Smali 代码了,我有种看伪代码的感觉。

在这里能看到方法名,并且要前有对应上。

都没问题就继续往下走。
.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 //寄存器数量
//这是一个私有的、静态的不可修改的方法。 onCreate$lambda-2 是方法名,后面跟着的是参数。
.line 33 //代码所在行数。
iget p0, p0, Lkotlin/jvm/internal/Ref$IntRef;->element:I
const/4 p5, 0x1
const/16 v0, 0xa
if-ge p0, v0, :cond_15
.line 34
move-object p0, p1
check-cast p0, Landroid/content/Context;
const-string v0, "\u8bf7\u5148\u83b7\u53d610\u4e2a\u786c\u5e01\u54e6"
check-cast v0, Ljava/lang/CharSequence;
invoke-static {p0, v0, 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
.line 36
:cond_15
invoke-virtual {p1}, Lcom/zj/wuaipojie/ui/ChallengeSecond;->isvip()Z
move-result p0
if-eqz p0, :cond_43
.line 37
check-cast p1, Landroid/content/Context;
const-string p0, "\u5f53\u524d\u5df2\u7ecf\u662f\u5927\u4f1a\u5458\u4e86\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
const p0, 0x7f0d0018
.line 38
invoke-virtual {p2, p0}, Landroid/widget/ImageView;->setImageResource(I)V
const p0, 0x7f0d0008
.line 39
invoke-virtual {p3, p0}, Landroid/widget/ImageView;->setImageResource(I)V
const p0, 0x7f0d000a
.line 40
invoke-virtual {p4, p0}, Landroid/widget/ImageView;->setImageResource(I)V
.line 41
sget-object p0, Lcom/zj/wuaipojie/util/SPUtils;->INSTANCE:Lcom/zj/wuaipojie/util/SPUtils;
const/4 p2, 0x2
const-string p3, "level"
invoke-virtual {p0, p1, p3, p2}, Lcom/zj/wuaipojie/util/SPUtils;->saveInt(Landroid/content/Context;Ljava/lang/String;I)V
goto :goto_50
.line 44
: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
.end method
我们就把这段代码拿出来,逐行分析一下。

分析可以对照表格,当然现在是 2026 年,可以 AI 一把梭哈,但最好还是懂一点原理。
在 smali 中所有操作都必须经过寄存器来进行,本地寄存器用 v 开头数字结尾的符号表示,参数寄存器用 p 开头,在非 static 函数中,p0 代指 this,p1 代表函数的第一个参数,p2 是第二个。其实 p0 可以理解为定位了了。
static 函数中 p0 才对应对一个参数。(JAVA 的 static 方法没有 this 方法)

我们直接关注核心点,果然和我们之前的判断如出一辙,这就是进行判定,那其实我们就找到判定是否是大会员的判定点,进行修改,比如说把 if-ge 改为 if-le,或者把 if-xx 的改为直接跳转 goto xxx 即可。
那我们自己找一下判断点即可了啊,就很简单了,逆向好像都是一家的,太相似了。

就很典型了,那就是给参数 p5 赋值 1,给 v0 寄存器 复制 16。
然后下面,如果 p0 的值大于等于 v0 则跳转到 cond_15。

追踪一下 cond_15,我们翻译一下。
这段翻译成大白话,这里 invoke-vitual 函数就是派个人去问问程序,我们是不是 vip(调用 isvip() 方法)。
move-result p0,这里参数 p0 是存放结果,看返回值是 True 还是 False。
接下来一个 if-eqz 判断,就是如果 p0 这个参数为等于 0,我们跳转到 cond_43,如果不是继续执行。
那其实我们就应该去看 isvip() 的执行逻辑,看看他判断返回值什么意思,按照这个修改即可。
这里为什么去看 isvip() 内部逻辑是更优选择,因为改了调用处的 if-eqz 虽然快,但是改 isvip() 可以全局生效,一个 App 会有多个地方调用,比如去广告、下载速度、进入 vip 专区。隐藏性也会更好,App 会做逻辑校验,只改一处 if-nez,可能会导致程序不一致,当然这个要看具体逻辑程序,我们先自行分析,再看教程。

这段我们分析一下,明显能看到一点啊,就是不管我们输入什么,这里的 const/4 v0, 0x0 都是 0 啊,也就是说我们做啥他都不让我们通过。这就是问题所在。其实这里把他改为 const/4 v0,0x1 就好了,本人理解,但是第一次做安卓逆向,不知道会不会出问题,我感觉就是 Patch 一下,然后重新打包校验签名,但有个问题,这个程序就不是原来的程序了,我这里说的有点绕。
但其实这里我们漏掉了一点,就是没有去看是否满足 10 个硬币的逻辑点,我们回去看。

其实是在这里,const-string p0 这里,其实是判断我们是否获取了 10 个硬币,这里的 string 就是字符串,我们可以去伪代码里面去找。

在这里个位置。其实我们也能看伪代码去理解,就是当我们获取了 10 个硬币后,这是大前提,我们会进入到第二个判断点,是否是大会员,也就是想办法进入到第二个 if 中的逻辑去。
所以这个逻辑就是死的,不修改我们无法通关。
这里 . method 是一个方法的开头,.end method 是一个方法的结束。
那么这里我们就找出了三种修改方案:
修改判断
强制跳转
修改寄存器的值
可以说是最简单的方法。

这里把 if-ge 改为 if-le,这里是硬币数量的判定。

然后这里又判断我们是否是 vip 非常简单了,把 if-eqz 改为 if-nez 即可。
或者这里我们注释掉。

这是修改判断,注释方法。

这里改为 0 也一样的。

这里 0 改为 1。此处是 vip 判断。
还有一种方式。

这里我们也改一下。
拓展
其实我一直有个疑问,就是在安卓逆向中的各个文件作用。我这里就用 iot 安全的语言来换算。
首先提到一点就是 Dalvik/ART,其实他们相当于 iot 中的 linux 核心/裸机,是用于运行程序的土壤。
文件格式 .dex 文件相当于 ELF 文件,程序的打包方式。
这里的 JVM 相当于动态解释,某些 IOT 设备跑 Python 或 Lua 脚本,就像 JVM 一样,边看脚本边翻译。
Dalvik 相当于静态编译,就像 C 写的程序,编译后转换成机器码,跑在 CPU 上。
3、快速定位

这里点击获取硬币,然后看一下他的地址。

都弄好了,往下走。

就在上面不远处能看到。
4、总结
对于开发者而言,打包时最好混淆,否则容易轻松进行 Crack,不要直接写 isvip、getvip 等易识别方法名。