移动安全:Smali语法学习示例与实践

39 篇文章 0 订阅
2 篇文章 0 订阅


前言

本节介绍移动安全相关知识中的Smali语法,并自主动手完成一个小练习

一、Smali简介

Smali是Android虚拟机Davlik的寄存器语言,语法上和汇编语言类似. Davlik是基于寄存器的,就是说smali里的所有操作都必须经过寄存器来进行.
简单来说就是高级语言Java和机器语言的一个中间层代码语言,执行效率相比高级语言更加高效,更加贴近底层,通过我们用Apktool反编译APK出来的结果中便会含有Smali文件夹,文件夹下的内容就是Smali语言
在这里插入图片描述
本节主要针对基于Java编写的Android应用反编译出的Smali文件进行分析。

二、Smali语法简介

1.基本类型

B—byte
C—char
D—double
F—float
I—int
S—short
V—void
J—long
Z—boolean

2.引用类型

  • [XXX->X代表类型的数组
    例:
    [B -->Byte[]
    [I -->Int[]
  • Lxx/yyy -->Object对象
    例如:
    String str–>str Ljava/lang/String;
    subobjectName objectName->objectName LpackageaName/objectName s u b o b j e c t N a m e 注 : 这 里 的 subobjectName 注:这里的 subobjectName符号代表内部类,内部类的含义我就不用解释了吧

3.方法体定义

方法体格式:
Func-Name (Para-Type1Para-Type2Para-Type3…)Return-Type 注意: 参数之间没有任何分隔符,返回值在最后

例:
void hello() —> hello()V
boolean hello(int,int,int) —>hello(III)Z
String fun(boolean, int[], int[], String, long)
—>fun(Z[I[ILjava/lang/String,J)Ljava/lang/String

4.关键字

  • .field private isFlag:z 定义变量
  • .method 方法
  • .parameter 方法参数
  • .prologue 方法开始
  • .line123 此方法开始于123行
  • invoke-super 调用父函数
  • const/high16 v0,0x7fox 把0x7fox的值赋值给v0
  • invoke-direct 调用函数
  • return-void 函数返回void
  • .end method 函数结束
  • new-instance 创建实例
  • iput-object 对象赋值
  • iget-object 调用对象
  • invoke-static 调用静态函数
  • .class public Lcom/disney/WMW/WMWActivty; 类名
  • .super Lcom/XXX/XXX/XXX; 父类名
  • .source “XXX.java” 源文件名
  • .implements Lcom/XXX/XXX/XXX; 实现了接口
  • .annotation 内部类

5.寄存器类别区分

  • 寄存器v,本地寄存器 (local register, 非参寄存器)
    常用v开头数字结尾的符号表示 v0,v1,v2…
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/6638f064f4f049ee8b3e420f38de1cee.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAVm9sZGVtb3J0UWlhbg==,size_17,color_FFFFFF,t_70,g_se,x_16
    可以看到这里的locals后面的参数为2,表明使用两个v开头的本地寄存器,若修改数量为1,可以编译通过,但是运行时,会发生错误

  • 寄存器p,参数寄存器 (parameter regisgter)
    常用p开头数字结尾的符号来表示 p0,p1,p2,p3…

# direct methods
.method public constructor <init>()V
    .locals 0

    .line 18
    invoke-direct {p0}, Landroidx/appcompat/app/AppCompatActivity;-><init>()V

    return-void
.end method

p0为非静态方法自动创建的寄存器,存储着this,也就是该方法所属类的实例本身,例如这段函数中,一个Activity invoke-direct调用其父类AppCompatActivity的init()方法,传入的参数是这个实例本身的this

6.条件跳转

  • if-eq vA,vB, :cond_** 如果vA等于vB,则跳转到:cond_**
  • if-ne vA,vB, :cond_** 如果vA不等于vB,则跳转到:cond_**
  • if-lt vA,vB, :cond_** 如果vA小于vB,则跳转到:cond_**
  • if-ge vA,vB, :cond_** 如果vA大于等于vB,则跳转到:cond_**
  • if-gt vA,vB, :cond_** 如果vA大于vB,则跳转到:cond_**
  • if-le vA,vB, :cond_** 如果vA小于等于vB,则跳转到:cond_**
  • if-eqz vA, :cond_** 如果vA等于0,则跳转到:cond_**
  • if-nez vA, :cond_** 如果vA不等于0,则跳转到:cond_**
  • if-ltz vA, :cond_** 如果vA小于0,则跳转到:cond_**
  • if-gez vA, :cond_** 如果vA大于等于0,则跳转到:cond_**
  • if-gtz vA, :cond_** 如果vA大于0,则跳转到:cond_**
  • if-lez vA, :cond_** 如果vA小于等于0,则跳转到:cond_**

7.Smali针对函数返回结果的操作

在Java代码中调用函数和返回函数结果可以只用一条语句来表示,如:

	A=getPoint();

但在Smali里则需要分开完成,在使用上述指令后,如果调用的函数返回非void, 那么还需要用到move-result(返回基本函数类型)和move-result-object(返回对象指令);

例:

	const-string v0,"Eric"
	invoke-static {v0},Lcmb/pbi;->t(Ljava/lang/String)LJava/lang/String;
	move-result-object v2

此时v2保存的就是调用t方法得到的String字符串

8.Smali变量操作权限

字段操作指令表示对对象字段进行设值和取值操作,就像是你在代码中长些的set和get方法.基本指令是iput-type,iget-type,sput-type,sget-type.type表示数据类型.

普通字段读写操作
前缀是i的iput-type和iget-type指令用于字段的读写操作.

指令 描述

  • iget-object vAA,vBB,filed_id 读取vAA寄存器中的对象中的filed_id对象的引用值给vBB寄存器
  • iget-boolean vAA,vBB,filed_id 读取vAA寄存器中的对象中的filed_id的值给vBB寄存器
  • iget-wide vAA,vBB,filed_id 读取vAA寄存器中的对象中的filed_id的值给vBB寄存器
  • iget vAA,vBB,filed_id 读取vAA寄存器中的对象中的filed_id的值给vBB寄存器
  • iput-object vAA,vBB,filed_id 把vAA寄存器指向的对象的引用赋值给vBB寄存器中的filed_id对象
  • iput-boolean vAA,vBB,filed_id 把vAA寄存器的值给vBB寄存器中的boolean类型
  • iput-wide vAA,vBB,filed_id 把vAA寄存器的值给vBB寄存器中的wide类型
  • iput vAA,vBB,filed_id 把vAA寄存器的值给vBB寄存器中的int类型

静态字段读写操作
前缀是s的sput-type和sget-type指令用于静态字段的读写操作

指令 描述

  • sget-object vAA,vBB,filed_id 读取vAA寄存器中的对象中的filed_id对象的引用值给vBB寄存器
  • sget-boolean vAA,vBB,filed_id 读取vAA寄存器中的对象中的filed_id的值给vBB寄存器
  • sget-wide vAA,vBB,filed_id 读取vAA寄存器中的对象中的filed_id的值给vBB寄存器
  • sget vAA,vBB,filed_id 读取vAA寄存器中的对象中的filed_id的值给vBB寄存器
  • sput-object vAA,vBB,filed_id 把vAA寄存器指向的对象的引用赋值给vBB寄存器中的filed_id对象
  • sput-boolean vAA,vBB,filed_id 把vAA寄存器的值给vBB寄存器中的boolean类型
  • sput-wide vAA,vBB,filed_id 把vAA寄存器的值给vBB寄存器中的wide类型
  • sput vAA,vBB,filed_id 把vAA寄存器的值给vBB寄存器中的int类型

三、阅读部分

我们这里采取自己动手打包一个登录验证的APK进行阅读和练习

APK效果

登录界面
在这里插入图片描述
登陆后的界面
在这里插入图片描述

打包和反编译

打包用AS一键打包
反编译用APKTool对APK进行反编译出相关Smali代码

在这里插入图片描述
在这里插入图片描述
点击Smali用VS Code工具进行阅读

阅读

MainActivity.smali

.class public Lcom/example/androidsafetest01/MainActivity;//包名+类名
.super Landroidx/appcompat/app/AppCompatActivity;//父类名称
.source "MainActivity.java"//.source 源文件名称


# instance fields
.field private btn:Landroid/widget/Button;

.field private editText:Landroid/widget/EditText;//实例变量 变量名称:包名


# direct methods
.method public constructor <init>()V//方法开始
    .locals 0

    .line 12//这个方法对应Java代码中的12行
    invoke-direct {p0}, Landroidx/appcompat/app/AppCompatActivity;-><init>()V//调用父类中的构造函数,返回值为空

    return-void
.end method//方法结束

.method static synthetic access$000(Lcom/example/androidsafetest01/MainActivity;)Landroid/widget/EditText;
    .locals 0

    .line 12
    iget-object p0, p0, Lcom/example/androidsafetest01/MainActivity;->editText:Landroid/widget/EditText;//将MainActivity的id值送给EditText,初步猜测是在给权限

    return-object p0
.end method

.method private initEvent()V
    .locals 2//使用了两个寄存器

    .line 26
    iget-object v0, p0, Lcom/example/androidsafetest01/MainActivity;->btn:Landroid/widget/Button;

    new-instance v1, Lcom/example/androidsafetest01/MainActivity$1;

    invoke-direct {v1, p0}, Lcom/example/androidsafetest01/MainActivity$1;-><init>(Lcom/example/androidsafetest01/MainActivity;)V

    invoke-virtual {v0, v1}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V//调用实例的虚方法

    return-void
.end method

.method private initView()V
    .locals 1

    const v0, 0x7f08008f

    .line 44
    invoke-virtual {p0, v0}, Lcom/example/androidsafetest01/MainActivity;->findViewById(I)Landroid/view/View;

    move-result-object v0

    check-cast v0, Landroid/widget/EditText;

    iput-object v0, p0, Lcom/example/androidsafetest01/MainActivity;->editText:Landroid/widget/EditText;

    const v0, 0x7f080057

    .line 45
    invoke-virtual {p0, v0}, Lcom/example/androidsafetest01/MainActivity;->findViewById(I)Landroid/view/View;

    move-result-object v0

    check-cast v0, Landroid/widget/Button;

    iput-object v0, p0, Lcom/example/androidsafetest01/MainActivity;->btn:Landroid/widget/Button;

    return-void
.end method


# virtual methods
.method protected onCreate(Landroid/os/Bundle;)V
    .locals 0
    .annotation system Ldalvik/annotation/MethodParameters;
        accessFlags = {
            0x0
        }
        names = {
            "savedInstanceState"
        }
    .end annotation

    .line 19
    invoke-super {p0, p1}, Landroidx/appcompat/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V

    const p1, 0x7f0b001d

    .line 20
    invoke-virtual {p0, p1}, Lcom/example/androidsafetest01/MainActivity;->setContentView(I)V

    .line 21
    invoke-direct {p0}, Lcom/example/androidsafetest01/MainActivity;->initView()V

    .line 22
    invoke-direct {p0}, Lcom/example/androidsafetest01/MainActivity;->initEvent()V

    return-void
.end method

MainActivity$1.smali

.class Lcom/example/androidsafetest01/MainActivity$1;
.super Ljava/lang/Object;
.source "MainActivity.java"

# interfaces
.implements Landroid/view/View$OnClickListener;


# annotations
.annotation system Ldalvik/annotation/EnclosingMethod;
    value = Lcom/example/androidsafetest01/MainActivity;->initEvent()V
.end annotation

.annotation system Ldalvik/annotation/InnerClass;
    accessFlags = 0x0
    name = null
.end annotation


# instance fields
.field final synthetic this$0:Lcom/example/androidsafetest01/MainActivity;


# direct methods
.method constructor <init>(Lcom/example/androidsafetest01/MainActivity;)V
    .locals 0
    .annotation system Ldalvik/annotation/MethodParameters;
        accessFlags = {
            0x8010
        }
        names = {
            "this$0"
        }
    .end annotation

    .line 26
    iput-object p1, p0, Lcom/example/androidsafetest01/MainActivity$1;->this$0:Lcom/example/androidsafetest01/MainActivity;

    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    return-void
.end method


# virtual methods
.method public onClick(Landroid/view/View;)V
    .locals 2
    .annotation system Ldalvik/annotation/MethodParameters;
        accessFlags = {
            0x0
        }
        names = {
            "view"
        }
    .end annotation

    .line 29
    iget-object p1, p0, Lcom/example/androidsafetest01/MainActivity$1;->this$0:Lcom/example/androidsafetest01/MainActivity;

    invoke-static {p1}, Lcom/example/androidsafetest01/MainActivity;->access$000(Lcom/example/androidsafetest01/MainActivity;)Landroid/widget/EditText;

    move-result-object p1

    invoke-virtual {p1}, Landroid/widget/EditText;->getText()Landroid/text/Editable;

    move-result-object p1

    invoke-virtual {p1}, Ljava/lang/Object;->toString()Ljava/lang/String;

    move-result-object p1

    if-eqz p1, :cond_0

    .line 30
    invoke-virtual {p1}, Ljava/lang/String;->length()I

    move-result v0

    if-eqz v0, :cond_0

    const-string v0, "123456"

    .line 31
    invoke-virtual {p1, v0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

    move-result p1

    if-eqz p1, :cond_1

    .line 32
    new-instance p1, Landroid/content/Intent;

    iget-object v0, p0, Lcom/example/androidsafetest01/MainActivity$1;->this$0:Lcom/example/androidsafetest01/MainActivity;

    invoke-virtual {v0}, Lcom/example/androidsafetest01/MainActivity;->getApplicationContext()Landroid/content/Context;

    move-result-object v0

    const-class v1, Lcom/example/androidsafetest01/EndActivity;

    invoke-direct {p1, v0, v1}, Landroid/content/Intent;-><init>(Landroid/content/Context;Ljava/lang/Class;)V

    .line 33
    iget-object v0, p0, Lcom/example/androidsafetest01/MainActivity$1;->this$0:Lcom/example/androidsafetest01/MainActivity;

    invoke-virtual {v0, p1}, Lcom/example/androidsafetest01/MainActivity;->startActivity(Landroid/content/Intent;)V

    goto :goto_0

    .line 36
    :cond_0
    iget-object p1, p0, Lcom/example/androidsafetest01/MainActivity$1;->this$0:Lcom/example/androidsafetest01/MainActivity;

    invoke-virtual {p1}, Lcom/example/androidsafetest01/MainActivity;->getApplicationContext()Landroid/content/Context;

    move-result-object p1

    const/4 v0, 0x0

    const-string v1, "\u8f93\u5165\u5bc6\u7801\u6709\u8bef"//这里Smali采用的是Unicode编码

    invoke-static {p1, v1, v0}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

    move-result-object p1

    invoke-virtual {p1}, Landroid/widget/Toast;->show()V

    :cond_1
    :goto_0
    return-void
.end method

四、实际操作部分

这里通过修改其中的明文密码判定部分,改变其登录逻辑为,密码等于123456789时,进行登录行为
在这里插入图片描述
改变后
在这里插入图片描述
再次打包APK,进行重签名发送到手机上进行安装

重签名,这里的签名方式采用的是Java中的jarsigner命令,jarsigner命令只可以针对V1的签名方式进行签名,后期应该会进行更改签名方式
在这里插入图片描述

jarsigner -verbose -keystore D:\2022\Names\AndroidSafeSign.jks D:\2022\移动安全\apks\release\AndroidSafeTest02.apk -signedjar D:\2022\移动安全\apks\AfterRenamed\AndroidSafeTest01.apk key0

执行效果:

在这里插入图片描述

修改过后的视频播放地址

但是这种修改Smali的方法基于静态调试,实际反编译APK过程中基本不会有打静态补丁的机会,这里也是仅供练习使用。

总结

以上是Smali语法学习部分,欢迎大家指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值