初识smali
本文最后更新于 2 天前,其中的信息可能已经有所发展或是发生改变。

前言

在我大一的时候遇到过安卓的一道题,该题的逻辑是检测到debug则输出flag,也就是说原本的考点是动态调试app,我当初就只用了动调的方法,而在与学长的交流中,学长直接修改了smali代码里的if判断条件使其未检测到debug则输出flag,使我对smali印象深刻

教程地址:《安卓逆向这档事》三、初识smali,vip终结者 - 吾爱破解 - 52pojie.cn

JVM、Dalvik与ART

  • JVM是java虚拟机,运行java字节码
  • Dalvik是Google专门为Android设计的虚拟机,执行.dex文件
  • Art相当于升级版的Dalvik

smali语法

.dex文件反编译得到smali代码
我们修改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代码分析

我们来到教程demo的第二关

这里哪怕是获取到了硬币再长按一键三连也无法成功,下面小弹窗提示请先充值大会员

所以我们可以在jadx或者JEB中搜索这一串字符串,jadx左上角有一个搜索图标可以对文本进行搜索,JEB的话可以按Ctrl+f打开文本搜索框



我们双击就可以定位到代码所在的地方了

package com.zj.wuaipojie.ui;

import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import kotlin.Metadata;
import kotlin.jvm.internal.Ref.IntRef;

@Metadata(d1 = {"\u0000\u001E\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u000B\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u0006\u0010\u0003\u001A\u00020\u0004J\u0012\u0010\u0005\u001A\u00020\u00062\b\u0010\u0007\u001A\u0004\u0018\u00010\bH\u0014¨\u0006\t"}, d2 = {"Lcom/zj/wuaipojie/ui/ChallengeSecond;", "Landroidx/appcompat/app/AppCompatActivity;", "()V", "isvip", "", "onCreate", "", "savedInstanceState", "Landroid/os/Bundle;", "app_release"}, k = 1, mv = {1, 7, 1}, xi = 0x30)
public final class ChallengeSecond extends AppCompatActivity {
    public final boolean isvip() {
        return false;
    }

    @Override  // androidx.fragment.app.FragmentActivity
    protected void onCreate(Bundle bundle0) {
        super.onCreate(bundle0);
        this.setContentView(0x7F0B001F);  // layout:activity_challenge_second
        IntRef ref$IntRef0 = new IntRef();
        TextView textView0 = (TextView)this.findViewById(0x7F08007D);  // id:coin_tv
        Button button0 = (Button)this.findViewById(0x7F08007B);  // id:coin_btn
        Button button1 = (Button)this.findViewById(0x7F0801EB);  // id:yjsl_btn
        ImageView imageView0 = (ImageView)this.findViewById(0x7F0801EC);  // id:zan_img
        ImageView imageView1 = (ImageView)this.findViewById(0x7F08007C);  // id:coin_img
        ImageView imageView2 = (ImageView)this.findViewById(0x7F08007F);  // id:collect_img
        button0.setOnClickListener((View view0) -> {
            ++ref$IntRef0.element;
            textView0.setText(((CharSequence)String.valueOf(ref$IntRef0.element)));
        });
        button1.setOnClickListener((View view0) -> Toast.makeText(((Context)this), "请长按完成一键三连", 1).show());
        button1.setOnLongClickListener((View view0) -> {
            if(ref$IntRef0.element < 10) {
                Toast.makeText(((Context)this), "请先获取10个硬币哦", 1).show();
            }

            Toast.makeText(((Context)this), "请先充值大会员哦!", 1).show();
            return true;
        });
    }

    // Detected as a lambda impl.
    private static final void onCreate$lambda-0(IntRef ref$IntRef0, TextView textView0, View view0) {
        ++ref$IntRef0.element;
        textView0.setText(((CharSequence)String.valueOf(ref$IntRef0.element)));
    }

    // Detected as a lambda impl.
    private static final void onCreate$lambda-1(ChallengeSecond challengeSecond0, View view0) {
        Toast.makeText(((Context)challengeSecond0), "请长按完成一键三连", 1).show();
    }

    // Detected as a lambda impl.
    private static final boolean onCreate$lambda-2(IntRef ref$IntRef0, ChallengeSecond challengeSecond0, ImageView imageView0, ImageView imageView1, ImageView imageView2, View view0) {
        if(ref$IntRef0.element < 10) {
            Toast.makeText(((Context)challengeSecond0), "请先获取10个硬币哦", 1).show();
        }

        Toast.makeText(((Context)challengeSecond0), "请先充值大会员哦!", 1).show();
        return true;
    }
}

我们切换到smali代码就能看见上面java代码中对应的一些方法

//一个私有、静态、不可变的方法   方法名
.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

copy一下这些注释,至于注释是如何得到的,我们可以去搜索这里面的smali代码在smali语法中对应的效果(有种在IDA中看汇编的感觉

smali代码修改

其实光静态看的话感觉JEB更直观一些

.method private static final onCreate$lambda-2(Ref$IntRef, ChallengeSecond, ImageView, ImageView, ImageView, View)Z
          .registers 7
00000000  iget                p0, p0, Ref$IntRef->element:I
00000004  const/4             p5, 1
00000006  const/16            v0, 10
0000000A  if-ge               p0, v0, :2A
:E
0000000E  move-object         p0, p1
00000010  check-cast          p0, Context
00000014  const-string        v0, "请先获取10个硬币哦"
00000018  check-cast          v0, CharSequence
0000001C  invoke-static       Toast->makeText(Context, CharSequence, I)Toast, p0, v0, p5
00000022  move-result-object  p0
00000024  invoke-virtual      Toast->show()V, p0
:2A
0000002A  invoke-virtual      ChallengeSecond->isvip()Z, p1
00000030  move-result         p0
00000032  if-eqz              p0, :86
:36
00000036  check-cast          p1, Context
0000003A  const-string        p0, "当前已经是大会员了哦!"
0000003E  check-cast          p0, CharSequence
00000042  invoke-static       Toast->makeText(Context, CharSequence, I)Toast, p1, p0, p5
00000048  move-result-object  p0
0000004A  invoke-virtual      Toast->show()V, p0
00000050  const               p0, 0x7F0D0018        # mipmap:zan_active
00000056  invoke-virtual      ImageView->setImageResource(I)V, p2, p0
0000005C  const               p0, 0x7F0D0008        # mipmap:coin_active
00000062  invoke-virtual      ImageView->setImageResource(I)V, p3, p0
00000068  const               p0, 0x7F0D000A        # mipmap:collect_active
0000006E  invoke-virtual      ImageView->setImageResource(I)V, p4, p0
00000074  sget-object         p0, SPUtils->INSTANCE:SPUtils
00000078  const/4             p2, 2
0000007A  const-string        p3, "level"
0000007E  invoke-virtual      SPUtils->saveInt(Context, String, I)V, p0, p1, p3, p2
00000084  goto                :A0
:86
00000086  check-cast          p1, Context
0000008A  const-string        p0, "请先充值大会员哦!"
0000008E  check-cast          p0, CharSequence
00000092  invoke-static       Toast->makeText(Context, CharSequence, I)Toast, p1, p0, p5
00000098  move-result-object  p0
0000009A  invoke-virtual      Toast->show()V, p0
:A0
000000A0  return              p5
.end method

可以先修改这一行
0000000A if-ge p0, v0, :2A
改为
0000000A if-le p0, v0, :2A
使其直接进到2A也就是isvip()是否为大会员的判断,根据smali代码中的描述,
00000032 if-eqz p0, :86
这一行会使程序逻辑进到86也就是提示你充值大会员,所以我们可以把这一行注释掉,
让程序直接执行36也就是"当前已经是大会员了哦!"的逻辑,从而实现跳过

我们可以使用MT管理器找到对应的smali代码进行修改,这样似乎很方便

包括后续我们也可以将isvip的返回值改为1,这样直接能判断你是大会员,以及将之前比较的p0 v0改为相同的值实现跳转

也可以将当前已经是大会员的代码行加上:cond_42这种然后在上面跳转到cond_43的地方改为goto


以及将上面的cond_15直接改goto

都是没有问题的

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇