smali to java_Smali —— 数学运算,条件判断,循环
通過上一篇 Smali 語法解析——Hello World 的學習,了解了 Smali 文件的基本格式。這一篇從最基本的數(shù)學運算,條件判斷,循環(huán)等開始,更加詳細的了解 Smali 語法。
數(shù)學運算
加法
先看源文件:
public class BaseSmali {private float add() {int a = 1;float b = 1.5f;return a + b;}
}
通過 javac dx 和 baksmali 工具生成對應(yīng)的 smali 文件,具體方法在 上一篇 中有所介紹。我們看一下生成的 smali 文件:
.class public LBaseSmali;
.super Ljava/lang/Object;
.source "BaseSmali.java"# direct methods
.method public constructor <init>()V.registers 1.prologue.line 1invoke-direct {p0}, Ljava/lang/Object;-><init>()Vreturn-void
.end method.method private add()F.registers 3 // 使用 3 個寄存器.prologue.line 5const/4 v0, 0x1 // 將 0x1 放入 v0.line 6const/high16 v1, 0x3fc00000 # 1.5f 將 1.5f 放入 v1.line 7int-to-float v0, v0 // 將 v0 中的 int 值強轉(zhuǎn)為 float 再存入 v0add-float/2addr v0, v1 // 將 v0 和 v1 中的值相加再存入 v0return v0 // 返回 v0 中的值
.end method
代碼邏輯很簡單,可以看到 int 值和 float 值相加的過程中會先將 int 值強轉(zhuǎn)為 float,再進行加法。這里用到了數(shù)據(jù)定義,強轉(zhuǎn),加法三種 smali 語法。
數(shù)據(jù)定義指令
Dalvik 虛擬機中每個寄存器都是 32 位的。int 等 4字節(jié)表示的數(shù)據(jù)類型一個寄存器就可以表示,而 double 等 64 位的數(shù)據(jù)類型則需要兩個寄存器來表示。數(shù)據(jù)定義指令用到的基本字節(jié)碼是 const,一般帶 -wide 后綴表示的是 64 位數(shù)據(jù),不帶 -wide 后綴則是 32 位數(shù)據(jù)。上面的例子中定義了 兩種基本數(shù)據(jù)類型。 const/4 v0, 0x1表示將數(shù)值 0x1 擴展為 32 位之后賦給寄存器 v0。const/high16 v1, 0x3fc00000 ,表示將 0x3fc00000 右邊零擴展至 32 位賦給寄存器 v1。0x3fc00000 是 1.5f 在內(nèi)存中的表示,如果你了解 float 數(shù)值在內(nèi)存中的表示方法的話,就會理解這里為什么要右邊零擴展了。不理解的話可以閱讀我的文章,[先挖一個坑吧,還沒有寫]() 。下面介紹一些常見的數(shù)據(jù)定義指令(來自官網(wǎng)):
語法參數(shù)說明const/4 vA, #+BA: 目標寄存器(8 位) B: 有符號整數(shù)(8 位)將給定的字面值(符號擴展為 32 位)移到指定的寄存器中。const/16 vAA, #+BBBBA: 目標寄存器(8 位) B: 有符號整數(shù)(16 位)將給定的字面值(符號擴展為 32 位)移到指定的寄存器中。const vAA, #+BBBBBBBBA: 目標寄存器(8 位) B: 任意 32 位常量將給定的字面值移到指定的寄存器中。const/high16 vAA, #+BBBB0000A: 目標寄存器(8 位) B: 有符號整數(shù)(16 位)將給定的字面值(右零擴展為 32 位)移到指定的寄存器中。const-wide/16 vAA, #+BBBBA: 目標寄存器(8 位) B: 有符號整數(shù)(16 位)將給定的字面值(符號擴展為 64 位)移到指定的寄存器對中。const-wide/32 vAA, #+BBBBBBBBA: 目標寄存器(8 位) B: 有符號整數(shù)(32 位)將給定的字面值(符號擴展為 64 位)移到指定的寄存器對中。const-wide vAA, #+BBBBBBBBBBBBBBBBA: 目標寄存器(8 位) B: 任意雙字寬度(64 位)常量將給定的字面值移到指定的寄存器對中。const-wide/high16 vAA, #+BBBB000000000000A: 目標寄存器(8 位) B: 有符號整數(shù)(16 位)將給定的字面值(右零擴展為 64 位)移到指定的寄存器對中。const-string vAA, string@BBBBA: 目標寄存器(8 位) B: 字符串索引將通過給定的索引獲取的字符串引用移到指定的寄存器中。const-string/jumbo vAA, string@BBBBBBBBA: 目標寄存器(8 位) B: 字符串索引將通過給定的索引獲取的字符串引用移到指定的寄存器中。const-class vAA, type@BBBBA: 目標寄存器(8 位) B: 類型索引將通過給定的索引獲取的類引用移到指定的寄存器中。如果指定的類型是原始類型,則將存儲對原始類型的退化類的引用。
強轉(zhuǎn)指令
強轉(zhuǎn)的語法比較簡單,直接看官網(wǎng)截圖:
除了常見的基本類型之間的強制轉(zhuǎn)換,還有 neg 求補,not 求反,也同樣適用這一語法。
加法指令
add-float/2addr v0, v1 // 將 v0 和 v1 中的值相加再存入 v0
加法指令還有一種三個參數(shù)的寫法,如下所示:
add-float v0, v1, v2 // 將 v1 和 v2 中的值相加再存入 v0
這里的 float 可以替換為其他基本數(shù)據(jù)類型, add 也可以替換為其他數(shù)學運算操作。同樣,還是用過官網(wǎng)截圖來了解一下支持的運算語法:
一個加法延伸出來不少知識,看到這里,不知道你有沒有一個疑問,想想最初的 java 源代碼:
private float add() {int a = 1;float b = 1.5f;return a + b;
}
代碼中定義了兩個變量 a 和 b,可是 smali 中的這兩個變量呢?虛擬機中的編譯器,不論是 JVM 還是 DVM,都會竭盡所能的在編譯階段對代碼進行優(yōu)化以提升運行速度。a 和 b 這兩個變量在 add() 方法中并不是必須存在的,所以 DVM 不會浪費時間和空間再去申明這兩個變量。如果變量 b 也是 int 類型的話,DVM 甚至連加法都會省略,直接返回 a+b 的數(shù)值,大家可以動手試一下。那么,如果在學習過程中想了解每一句代碼的 smali 指令該怎么辦呢?使用 IDEA 的 java2smali 插件,就不會存在這些優(yōu)化了。
減法
源代碼:
private double sub(){int a = 1;double b = 2.5;return a-b;}
Smali 代碼:
.method private sub()D.registers 5.prologue.line 11const/4 v0, 0x1.line 12const-wide/high16 v2, 0x4004000000000000L # 2.5.line 13int-to-double v0, v0sub-double/2addr v0, v2return-wide v0
.end method
減法指令用 sub 表示。
另外這里要注意的是 const-wide 和 return-wide,添加了 -wide 后綴的操作符表示的是 64 位數(shù)據(jù)類型。上面例子中定義了 double 類型常量,返回值也是 double 類型。
乘法
源代碼:
private double mul(){float a = 1.5f;double b = 2;return a * b;}
Smali 代碼:
.method private mul()D.registers 5.prologue.line 17const/high16 v0, 0x3fc00000 # 1.5f.line 18const-wide/high16 v2, 0x4000000000000000L # 2.0.line 19float-to-double v0, v0mul-double/2addr v0, v2return-wide v0
.end method
乘法指令用 mul 表示
除法
源代碼:
private int div() {int a = 3;int b = 2;int c = a / b;return c;}
Smali 代碼:
.method private div()I.registers 2.prologue.line 23.line 25const/4 v0, 0x1.line 26return v0
.end method
顯然,編譯器對這段代碼進行了優(yōu)化,提前計算了 3/2 ,在 div() 方法中直接返回結(jié)果。我們在通過 java2smali 插件看一下未經(jīng)優(yōu)化的 Smali 代碼:
.method private div()I.registers 4.prologue.line 28const/4 v0, 0x3.line 29.local v0, "a":Iconst/4 v1, 0x2.line 30.local v1, "b":Idiv-int v2, v0, v1.line 31.local v2, "c":Ireturn v2
.end method
可以看到除法指令用 div 表示
布爾運算
源代碼:
private boolean bool(boolean a, boolean b,boolean c) {return a && b || c;}
Smali 代碼:
.method private bool(ZZZ)Z.registers 5.prologue.line 35if-eqz p1, :cond_4 // 如果 p1 = 0, 跳至 cond_4 處if-nez p2, :cond_6 // 如果 p2 != 0,跳至 cond_6 處:cond_4if-eqz p3, :cond_8 // 如果 p3 = 0,跳至 cond_8 處:cond_6const/4 v0, 0x1 // 將 0x1 賦給 v0:goto_7return v0 // 返回 v0 的值:cond_8const/4 v0, 0x0 // 將 0x1 賦給 v0goto :goto_7 // 跳至 goto_7 處
.end method
布爾運算在 smali 中被轉(zhuǎn)化為一系列的條件判斷加指令跳轉(zhuǎn)。上面例子中使用了兩種跳轉(zhuǎn)指令,if 判斷之后的條件跳轉(zhuǎn)和 goto 表示的無條件跳轉(zhuǎn),表示從當前地址跳轉(zhuǎn)到指定的偏移處。條件判斷指令在后面會具體羅列。
好像還沒提到過參數(shù)寄存器,這里用到三個參數(shù)寄存器,p1 p2 p3 ,再加上一個局部變量寄存器 v0,看起來只用了四個寄存器,但是 .registers 5 卻告訴我們這個方法用了五個寄存器,往上翻翻之前的 Smali 代碼,你會發(fā)現(xiàn),都無緣無故 “消失” 了一個寄存器。其實那是 p0 寄存器,函數(shù)被調(diào)用時會傳入一個隱式的對當前對象的引用,存儲在 p0 寄存器當中。
其他運算
源代碼:
private void other(int a) {int or = a | 1;int and = a & 1;int right = a >> 2;int left = a << 2;int mod = a % 2;}
Smali 代碼:
.method private other(I)V.registers 3.prologue.line 39or-int/lit8 v0, p1, 0x1.line 40and-int/lit8 v0, p1, 0x1.line 41shr-int/lit8 v0, p1, 0x2.line 42shl-int/lit8 v0, p1, 0x2.line 43rem-int/lit8 v0, p1, 0x2.line 44return-void
.end method
or 或 ,and 與 , shr 右移 , shl 左移 , rem 取模
條件判斷
條件判斷在之前的布爾運算中已經(jīng)演示過,這里羅列一些具體的判斷指令:
指令說明if-eq vA, vB, +CCCC如果 vA=vB,跳轉(zhuǎn)指定偏移量if-nevA != vBif-ltvA < vBif-gevA >= vBif-gtvA > vBif-levA <= vBif-eqz vA, +BBBBvA = 0if-nezvA != 0if-ltzvA < 0if-gezvA >= 0if-gtzvA > 0if-lezvA <= 0
循環(huán)
源代碼:
private void loop(){for (int i=0;i<10;i++){System.out.println(i);}}
Smali 代碼:
.method private loop()V.registers 3.prologue.line 47const/4 v0, 0x0 // v0 = 0:goto_1const/16 v1, 0xa // v1 = 10if-ge v0, v1, :cond_d // 如果 v0 >= v1,跳至 cond_d 處.line 48sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V.line 47add-int/lit8 v0, v0, 0x1 // v0++goto :goto_1 // 跳轉(zhuǎn)至 goto_1 處.line 50:cond_dreturn-void
.end method
顯然,循環(huán)也是通過條件判斷和指令跳轉(zhuǎn)來完成的。
本節(jié)中學習了 Smali 的數(shù)學運算,條件判斷和循環(huán)的語法,也基本涵蓋了大部分的 Smali 基本語法。下一篇學習 Smali 中類的用法。傳送門 —— Smali 語法解析 —— 類
文中所有示例代碼地址: https://github.com/lulululbj/android-reverse
文章同步更新于微信公眾號: 秉心說 , 專注 Java 、 Android 原創(chuàng)知識分享,LeetCode 題解,歡迎關(guān)注!
總結(jié)
以上是生活随笔為你收集整理的smali to java_Smali —— 数学运算,条件判断,循环的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: class没有发布到tomcat_Tom
- 下一篇: cout 数组_C语言学习笔记(十)二维