JVM、Dalvik、ART
- JVM是Java虚拟机,运行Java字节码程序
- Dalvik是Android专属的虚拟机,有专属的文件执行格式DEX(Dalvik executable)
- ART(Android Runtime)是Dalvik的升级版
smali基础语法
smali是Dalvik的寄存器语言,smali代码是dex反编译得到的。
常见关键词
名称 |
注释 |
.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 |
获取寄存器数据 |
寄存器
在smali中所有的操作都必须经过寄存器来进行:
- v开头数字结尾的寄存器为本地寄存器,如v0、v1、v2等
- p开头数字结尾的寄存器为参数寄存器,如p0、p1、p2等,需要注意的是
- 在非static函数中,p0代指的是
this
,p1才是第一个参数
- 在static函数中,p0是第一个参数
smali代码分析
现在我们看到示例app的第二关,我们长按一键三连发现要求我们充值大会员,现在我们需要找到他的这个判断逻辑代码
打开jadx,选择apk文件打开,搜索“大会员”作为关键词,如果没有搜索到的话可能需要unicode进行编码再搜索
跳转到对应的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public static final boolean m36onCreate$lambda2(Ref.IntRef intRef, ChallengeSecond challengeSecond, ImageView imageView, ImageView imageView2, ImageView imageView3, View view) { if (intRef.element < 10) { Toast.makeText(challengeSecond, "请先获取10个硬币哦", 1).show(); } if (challengeSecond.isvip()) { ChallengeSecond challengeSecond2 = challengeSecond; Toast.makeText(challengeSecond2, "当前已经是大会员了哦!", 1).show(); imageView.setImageResource(R.mipmap.zan_active); imageView2.setImageResource(R.mipmap.coin_active); imageView3.setImageResource(R.mipmap.collect_active); SPUtils.INSTANCE.saveInt(challengeSecond2, "level", 2); } else { Toast.makeText(challengeSecond, "请先充值大会员哦!", 1).show(); } return true; }
|
这是jadx反编译后给我们得到的代码,我们要去Smali部分找到对应lambda2函数,我这边找到的是如下代码,并在代码中,我将一行一行注释代码所代表的含义
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 94 95 96 97 98 99 100 101 102 103 104 105
| .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
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
|
下面还有isvip()
的方法smali代码,实际上这个方法固定返回为否,并不会判断是否为vip
1 2 3 4 5 6 7 8
| # virtual methods .method public final isvip()Z .registers 2
const/4 v0, 0x0
return v0 .end method
|
smali代码修改
修改方法:修改判断、强制跳转、修改寄存器的值
我们使用到np管理器,进入classes.dex
的编辑,通过搜索功能快速定位到我们要修改的位置
修改判断
首先我们要修改硬币数量的判断逻辑,这里我们选择把if-ge
大于等于修改为if-le
小于等于

接着我们要修改判断是否为vip部分的逻辑,我们直接将他的判断注释掉即可

保存看一下效果
修改寄存器的值
一样的,快速定位到需要修改的函数。我们这里首先要把v0的值赋值为0,则v0和p0相比则符合条件

接着我们修改isvip()返回的值,跳转到isvip()函数,修改返回值

修改完成后,保存测试
抓取按钮id定位
使用开发者工具,定位到按钮,找到View Id(HEX)

搜索选择整数,勾选十六进制即可查找到对应的功能点附近的位置