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

移动安全:Smali语法学习示例与实践,第1张

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

文章目录

前言一、Smali简介二、Smali语法简介

1.基本类型2.引用类型3.方法体定义4.关键字5.寄存类别区分6.条件跳转7.Smali针对函数返回结果的 *** 作8.Smali变量 *** 作权限 三、阅读部分

APK效果打包和反编译阅读 四、实际 *** 作部分总结


前言

本节介绍移动安全相关知识中的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的值赋值给v0invoke-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…

可以看到这里的locals后面的参数为2,表明使用两个v开头的本地寄存器,若修改数量为1,可以编译通过,但是运行时,会发生错误

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

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

    .line 18
    invoke-direct {p0}, Landroidx/appcompat/app/AppCompatActivity;->()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 ()V//方法开始
    .locals 0

    .line 12//这个方法对应Java代码中的12行
    invoke-direct {p0}, Landroidx/appcompat/app/AppCompatActivity;->()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;->(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 (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;->()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;->(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, "u8f93u5165u5bc6u7801u6709u8bef"//这里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:2022NamesAndroidSafeSign.jks D:2022移动安全apksreleaseAndroidSafeTest02.apk -signedjar D:2022移动安全apksAfterRenamedAndroidSafeTest01.apk key0

执行效果:

修改过后的视频播放地址

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

总结

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

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5706800.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存