smali文件和語法

smali文件格式

smali文件的頭3行描述了當(dāng)前類的一些信息刻撒,格式如下:

.class<訪問權(quán)限>[修飾關(guān)鍵字]<類名>
.super<父類名>
.source<源文件名>

打開MainAcivity.smali嚣镜,頭三行代碼:

.class public Lcom/droider/crackme0502/MainActivity;
.super Landroid/app/Activity;
.source "MainActivity.java"

第一行權(quán)限public ,類名為Lcom/droider/crackme0502/MainActivity;吧黄,類名開頭的L是遵循Dalvik字節(jié)碼的相關(guān)約定柬帕,表示后面跟隨的字符串為一個類。

第二行铅搓,Lcom/droider/crackme0502/MainActivity; 的父類是 Landroid/app/Activity;

第三行瑟押,.source指令指定了當(dāng)前類的源文件名

經(jīng)過混淆的dex文件,反編譯出來的smali代碼可能沒有源文件信息星掰,因此多望,.source可能為空。

前三行代碼過后是類的主體部分氢烘,一個類可以由多個字段或方法組成怀偷。smali文件中字段的聲明使用.field指令。字段有靜態(tài)字段與實例字段兩種威始。靜態(tài)字段的聲明格式如下:

# static fields
.field<訪問權(quán)限>[修飾關(guān)鍵字]<字段名>:<字段類型>

實例字段的聲明:

# instance field
.field private btnAnno:Landroid/widget/Button;

private 表示私有枢纠;字段btnAnno,它的類型是Landroid/widget/Button;

直接方法的聲明:

# direct methods
.method <訪問權(quán)限> <方法名>(參數(shù)原型) <方法原型>
    [.prologue]    // 指定代碼開始位置
    [.param]       // 指定方法參數(shù)
    [.line]        // 指定代碼在源代碼中的行數(shù)黎棠,混淆后可能不存在
    [.locals]  // 使用的局部變量個數(shù)
    <代碼體>
.end method

虛方法的聲明和直接方法相同晋渺,只是起始處的注釋為virtual methods

如果一個類實現(xiàn)了接口,會在.smali文件中使用.implement指令指出脓斩。聲明入下:

# interface
.implements<接口名>

注解格式聲明:

# annotations
.annotation[注解屬性]<注解類名>
    [注解字段=值]
.end annotation

注解的作用范圍可以是類木西、方法和字段。如果作用范圍是類随静,指令會直接定義在smali文件中八千,如果是方法或者字段,指令會包含在方法或字段的定義中燎猛。例如:

# instace fields
.field public sayWhat:Ljava/lang/String;
    .annotation runtime Lcom/droider/anno/MyAnnoField;
        info = "Hello my friend"
    .end annotation
.end field

轉(zhuǎn)換成Java代碼:

@ com.droid.anno MyAnnoField(info = "Hello my friend")
public String sayWhat;

原始類型

B—byte
C—char
D—double
F—float
I—int
J—long
S—short
V—void
Z—boolean
[XXX—array
Lpackage/name/ObjName—object;  // 前面表示對象所在包路徑,分號表示類結(jié)束

寄存器操作

p命名法和v命名法:

假設(shè)一個函數(shù)中用到M個寄存器恋捆,實際傳入的參數(shù)是N個。

根據(jù)傳參規(guī)則重绷,參數(shù)使用后N個寄存器沸停,局部變量使用0到M-N個寄存器。

假如用到5個寄存器昭卓,2個局部參數(shù)愤钾,3個傳入?yún)?shù)。

v命名法

v0,v1,v2,v3,v4;

p命名法

v0,v1,p0,p1,p2;

只改變傳入?yún)?shù)寄存器名候醒。

常量賦值

const                   v0, 0x7F030018  # R.layout.activity_challenge   #從R中取出靜態(tài)值
const/4                 v3, 0x2   #4也可以換成16或者h(yuǎn)igh16能颁,表示取整數(shù)值
const-string            v2, "Challenge"  # 取字符串
const-class             v2, Context    #把類對象取出

變量賦值

move  vx,vy   # 將vy的值賦值給vx,也可以是move-object等
move-result vx  # 將上個方法調(diào)用后的結(jié)果賦值給vx倒淫,也可以是move-result-object
return-object vx # 將vx的對象作為函數(shù)返回值
new-instance v0, ChallengePagerAdapter  # 實例化一個對象存入v0中

對象賦值

iput-object a,(this),b   將a的值給b伙菊,一般用于b的初始化
iget-object a,(this),b   將b的值給a,一般用于獲取b的地址,接著調(diào)用它
# eg.
iput-object v0, p0, ChallengeActivity->actionBar:ActionBar
iget-object v0, p0, ChallengeActivity->actionBar:ActionBar

函數(shù)操作

最基礎(chǔ)的函數(shù)操作一般有以下四個:

1.private:invoke-direct
2.public|protected: invoke-virtual
3.static:invoke-static
4.parent:  invoke-super
基本調(diào)用形式:invoke-xxx {參數(shù)},類;->函數(shù)(參數(shù)原型)
# eg.
invoke-super {p0, p1}, Landroid/support/v4/app/FragmentActivity;->onCreate(Landroid/os/Bundle;)V
<=對應(yīng)源碼=>
super.onCreate(savedInstanceState);  // 其中p0是this占业,其父類是FragmentActivity绒怨,p1,是savedInstanceState,其原型是Bundle谦疾;即調(diào)用p0->onCreate(p1)

程序相關(guān)語法

判斷語句

if-eq vA, vB, :cond_X   如果vA等于vB則跳轉(zhuǎn)到:cond_X
if-ne vA, vB, :cond_X   如果vA不等于vB則跳轉(zhuǎn)到:cond_X
if-lt vA, vB, :cond_X   如果vA小于vB則跳轉(zhuǎn)到:cond_X
if-ge vA, vB, :cond_X   如果vA大于等于vB則跳轉(zhuǎn)到:cond_X
if-gt vA, vB, :cond_X   如果vA大于vB則跳轉(zhuǎn)到:cond_X
if-le vA, vB, :cond_X   如果vA小于等于vB則跳轉(zhuǎn)到:cond_X
if-eqz vA, :cond_X      如果vA等于0則跳轉(zhuǎn)到:cond_X
if-nez vA, :cond_X      如果vA不等于0則跳轉(zhuǎn)到:cond_X
if-ltz vA, :cond_X      如果vA小于0則跳轉(zhuǎn)到:cond_X
if-gez vA, :cond_X      如果vA大于等于0則跳轉(zhuǎn)到:cond_X
if-gtz vA, :cond_X      如果vA大于0則跳轉(zhuǎn)到:cond_X
if-lez vA, :cond_X      如果vA小于等于0則跳轉(zhuǎn)到:cond_X

循環(huán)語句

public void encrypt(String str) {
    String ans = "";
    for (int i = 0 ; i < str.length();i++){
        ans += str.charAt(i);
    }
    Log.e("ans:",ans);
}
<=對應(yīng)smali=>
.method public encrypt(Ljava/lang/String;)V   # 方法:public void encrypt(String str)
.locals 4           # 四個變量
.param p1, "str"    # 方法參數(shù):Ljava/lang/String;
.prologue           # 代碼起始處
const-string v0, ""  # 賦值給ans
.local v0, "ans":Ljava/lang/String; 
const/4 v1, 0x0     # 賦值給 i 
.local v1, "i":I   
:goto_0             # 循環(huán)的地方
invoke-virtual {p1}, Ljava/lang/String;->length()I   # 調(diào)用虛函數(shù)(參數(shù)p1)String類中的length方法南蹂,返回int
move-result v2      #把前一步的結(jié)果放在v2中
if-ge v1, v2, :cond_0  # 如果v1<v2,即i<str.length(),就跳到:cond_0
new-instance v2, Ljava/lang/StringBuilder;      # 創(chuàng)建實例 v2 念恍,類型是Ljava/lang/StringBuilder;
invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V    # v2初始化
invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; # v2.append(v0)
move-result-object v2   #  v2.append(v0) => v2 這里 v2是v0的值六剥,v2=ans
invoke-virtual {p1, v1}, Ljava/lang/String;->charAt(I)C   # str.charAt(i)
move-result v3      # str.charAt(i) => v3
invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(C)Ljava/lang/StringBuilder; # ans + v3 
move-result-object v2   # ans + v3 =>v2
invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; # v2.toString()
move-result-object v0   # v2=>v0
add-int/lit8 v1, v1, 0x1    # i++
goto :goto_0    # 跳轉(zhuǎn)指令
:cond_0
const-string v2, "ans:"     # 常量賦值 v2 = "ans:"   
invoke-static {v2, v0}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I # Log.e(v2,v0)
return-void 
.end method

switch語句

public void encrypt(int flag) {
        String ans = null;
        switch (flag){
            case 0:
                ans = "ans is 0";
                break;
            default:
                ans = "noans";
                break;
        }
        Log.v("ans:",ans);
    }
<=對應(yīng)smali=>
.method public encrypt(I)V  # 方法 public void encrypt(int flag)
    .locals 2  # 兩個變量
    .param p1, "flag"    # 一個參數(shù) flag
    .prologue
    const/4 v0, 0x0     # v0賦值, 
    .local v0, "ans":Ljava/lang/String;  # String ans = null; v0就是ans

    packed-switch p1, :pswitch_data_0   # pswitch_data_0指定case區(qū)域的開頭及結(jié)尾
    const-string v0, "noans"     # 默認(rèn) 賦值 ans="noans"
    :goto_0
    const-string v1, "ans:"      # 賦值 v1 = "ans:" 
    invoke-static {v1, v0}, Landroid/util/Log;->v(Ljava/lang/String;Ljava/lang/String;)I # Log.v
    return-void
    :pswitch_0      #pswitch_<case的值>   case 0: ans="ans is 0"
    const-string v0, "ans is 0"
    goto :goto_0  # break
    nop
    :pswitch_data_0 #case區(qū)域的結(jié)束
    .packed-switch 0x0   #定義case的情況
        :pswitch_0   #case 0
    .end packed-switch
.end method

其中case定義情況有兩種:

1.從0開始遞增
packed-switch p1, :pswitch_data_0
...
:pswitch_data_0
.packed-switch 0x0
    :pswitch_0
    :pswitch_1 
2.無規(guī)則switch
sparse-switch p1,:sswitch_data_0
...
sswitch_data_0
.sparse-switch
    0xa -> : sswitch_0
    0xb -> : sswitch_1 # 字符會轉(zhuǎn)化成數(shù)組

try-catch語句

public void encrypt(int flag) {
    String ans = null;
    try {
        ans = "ok!";
    } catch (Exception e){
        ans = e.toString();
    }
    Log.d("error",ans);
}
<=對應(yīng)smali=>
.method public encrypt(I)V      # public void encrypt(int flag) {
    .locals 3           # 3個變量
    .param p1, "flag"    # 參數(shù)
    .prologue           # 代碼開始
    const/4 v0, 0x0     
    .line 20
    .local v0, "ans":Ljava/lang/String;     # String ans = null;
    :try_start_0  # 第一個try開始峰伙,
    const-string v0, "ok!"  # ans = "ok"
    :try_end_0   # 第一個try結(jié)束(主要是可能有多個try)
    .catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
    :goto_0
    const-string v2, "error"
    invoke-static {v2, v0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
    return-void
    :catch_0 #第一個catch
    move-exception v1
    .local v1, "e":Ljava/lang/Exception;
    invoke-virtual {v1}, Ljava/lang/Exception;->toString()Ljava/lang/String;
    move-result-object v0
    goto :goto_0
.end method

baksmali在反編譯時疗疟,為每個類單獨生成一個smali文件,內(nèi)部類作為一個獨立類瞳氓,也擁有自己獨立的smali文件策彤,只是內(nèi)部類的文件名形式為[外部類]$[內(nèi)部類].smali

class Outer{
    class Inner{}
}

上述代碼生成兩個文件:Outer.smaliOut$Inner.smali

this$0 是什么? 是內(nèi)部類自動保留的一個指向所在外部類的引用匣摘。左邊的this表示為父類的引用店诗,右邊的0表示引用層數(shù)。

public class Outer{                      //this$0
    public class FirstInner{            //this$1                                
        public class SecondInner{        //this$2
            public class ThirdInner{
                //在ThirdInner中訪問FirstInner類的引用為this$1
            }
        }
    }
}

this$X型字段都被指定了synthetic屬性音榜,表明他們是被編譯器合成的虛構(gòu)的庞瘸,開發(fā)者并沒有聲明該字段。

Reference

https://www.anquanke.com/post/id/85035

《Android軟件安全與逆向分析》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赠叼,一起剝皮案震驚了整個濱河市擦囊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嘴办,老刑警劉巖瞬场,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異涧郊,居然都是意外死亡泌类,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門底燎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人弹砚,你說我怎么就攤上這事双仍。” “怎么了桌吃?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵朱沃,是天一觀的道長。 經(jīng)常有香客問我,道長逗物,這世上最難降的妖魔是什么搬卒? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮翎卓,結(jié)果婚禮上契邀,老公的妹妹穿的比我還像新娘。我一直安慰自己失暴,他們只是感情好坯门,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著逗扒,像睡著了一般古戴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上矩肩,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天现恼,我揣著相機與錄音,去河邊找鬼黍檩。 笑死叉袍,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的建炫。 我是一名探鬼主播畦韭,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼肛跌!你這毒婦竟也來了艺配?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤衍慎,失蹤者是張志新(化名)和其女友劉穎转唉,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體稳捆,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡赠法,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了乔夯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片砖织。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖末荐,靈堂內(nèi)的尸體忽然破棺而出侧纯,到底是詐尸還是另有隱情,我是刑警寧澤甲脏,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布眶熬,位于F島的核電站妹笆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏娜氏。R本人自食惡果不足惜拳缠,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贸弥。 院中可真熱鬧窟坐,春花似錦、人聲如沸茂腥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽最岗。三九已至帕胆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間般渡,已是汗流浹背懒豹。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留驯用,地道東北人脸秽。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像蝴乔,于是被迫代替她去往敵國和親记餐。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容