Android 逆向代碼調試及smali文件結構

Smali語法詳解

apk文件通過apktool反編譯出來的都有一個smali文件夾,裏面都是以.smali結尾的文件。
smali語言是Davlik的寄存器語言,語法上和彙編語言相似,Dalvik VM與JVM的最大的區別之一就是Dalvik VM是基於寄存器的。基於寄存器的意思是,在smali裏的所有操作都必須經過寄存器來進行。

寄存器是什麼

在smali所有的操作都必須經過寄存器來進行:本地寄存器用v開頭數字結尾的符號來表示,如v0,v1,v2…,參數寄存器用p開頭數字結尾表示,如:p0,p1,p2…。特別注意的是,p0不一定是函數中的第一個參數,在非static函數中,p0代指“this”,p1表示函數的第一個參數,p2表示第二個參數,而在static函數中p0纔對應第一個參數(因爲java的static方法中沒有this方法)

  const/4 v0, 0x0
  iput-boolean v0,p0,Lcom/aa;->IsRegistered:Z
  上段表示的是:把0x0存寄到v0中,把這個值存放進com.aa.IsRegistered這個變量中,相當於this.IsRegistered=true,在非靜態方法中p0代表的是this,不是參數。

基本類型定義

Java Type Type descriptor
boolean Z
char C
byte B
short S
int I
float F
long J
double D
Object Ljava/lang/Object;
int[] [I
Object[][] [[Ljava/lang/object;
內部類 Lpackagename/objectname&subobjectname

數組表示方式在 基本數據類型前加“[”符號,對象的則表示以L作爲開頭格式爲Ljava/lang/Object; 分號在在後面。 Ljava/lang/String;其中java/lang對應java.lang包,String就是對應的該包中的對象。

方法定義:FuncName(para-type para-type1)ReturnType

  1. hello(III)I ->int hello(int,int,int)方法返回的是int型
  2. hello(J[I[Ljava/lang/String;ZLjava/lang/String) Z->blooean hello(long,string[],blooean,string) 方法返回的是blooean型

smali基本語法

  • .field private isflag:Z 定義變量爲 blooean isflag
  • .method 方法
  • .parameter 方法參數
  • .prologue 方法開始 序幕
  • .line123 此方法位於123行
  • const/high16 v0,0x7fo3 把0x7fo3賦值給v0
  • invoke-direct 調用private函數
  • invoke-virtual 調用除private其它函數
  • invoke-static 調用靜態函數
  • invoke-super 調用父函數
  • invoke-interface 調用接口
  • return-void 函數返回void
  • .end method 函數結束
  • new-instance 創建實例
  • iput-object 對象賦值
  • iget-object 調用對象

條件跳轉分支

  • "if-eq vA,vB, :cond_ ** " 如果vA等於vB,則跳轉到:cond_ **
  • "if-ne vA,vB, :cond_ ** " 如果vA不等於vB,則跳轉到:cond_ **
  • "if-It 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-Itz 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_ **
if-eq p1, v0, :cond_8 
    :cond_8
    invoke-direct {p0}, Lcom/paul/test/a;->d()V
    上段表示如果p1和v0相等,則執行cond_8的流程:調用com.paul.test.a的d()方法
    
if-ne p1, v0, :cond_b 
    :cond_b
    const/4 v0, 0x0
    invoke-virtual {p0, v0}, Lcom/paul/test/a;->setPressed(Z)V
    invoke-super {p0, p1, p2}, Landroid/view/View;->onKeyUp(ILandroid/view/KeyEvent;)Z
    move-result v0
    上段表示p1不等於p0,跳轉到:cond_b。把0x0賦值給v0,執行com/paul/test/a類中的void setPressed(blooean )公有方法。再調用View中的onkeyUp方法。返回一個基本數據類型。
    move-result(返回基本數據類型)和move-result-object(返回對象)指令獲取返回結果
    

包信息:

  • .class public Lcom/test/reversetest/MainActivity;
  • .super Landroid/support/v7/app/AppCompatActivity;
  • .source “MainActivity.java”
    這是一個由MainActivity.java類編譯得到smali文件,它是com/test/reversetest/MainActivity下的一個類同時該類繼承了android/support/v7/app/AppCompatActivity

samli聲明:

  • #annotations
  • .annotation system Ldalvik/annotation/MemberClasses;
    values={
    Lcom/aaa&qqq;,
    Lcom/aaa&www;
    }
    .end annotation

這個聲明是內部類的聲明:aaa 這個類有2個成員內部類qqq和www

成員變量格式:.field public /private[static][final]varName:<類型>。

  • 對於不同的成員變量也有不同的指令
  • 獲取的指令有:iget,sget,iget-boolean,sget-boolean,iget-object,sget-object。
  • 操作的指令有:iget,sput,iput-boolean,sput-boolean,iput-object,sput-boolean,iput-object,sput-object等。
  • 沒有 “-object” 表示成員變量對象是基本數據類型,帶了就是對象類型。特別的是,boolean類型則帶-boolean。
sget-object v0,Lcom/aaa;->ID:Ljava/lang/String;
sget-object用來獲取變量值並保存到挨着的參數寄存器中,它獲取的ID這個String類型的成員變量放到v0中。
注意:前面需要該變量所屬的類的類型,後面需要加冒號和該成員變量的類型,中間是 "->"表示所屬關係。

iget-object v0,p0,Lcom/aaa;->ID:Ljava/lang/String;
iget-object 比sget-object多了一個p0參數表示“this”,獲取array我們用aget和aget-object和上述指令一致。

put 指令和get指令是統一的:
const/4 v3, 0x0
sput-object v3,Lcom/aaa;->timer:Lcom/aaa/timer;
相當於:this.timer=null
這是因爲是object所以是null,,
sput-boolean v3,Lcom/aaa;->timer:Z;
相當於:this.timer=true

.local v0,args:Landroid/os/Message;
const/4 v1,0x12
iput v1,v0,Landroid/os/Message;->what:I
相當於 args.what=18;

函數的調用

smali 中的函數與變量分爲2種類型,分別爲direct和virtual。direct method就是private函數,其餘的函數都是virtual method。

  1. invoke-static:調用static函數。例如:
invoke-static{},Lcom/aaa;->Check()Z,
這裏invoke-static後有一對“{}”,其實調用的是該方法的實例+參數列表,由於這個方法既不需參數也是static的,所以{}爲空。  

const-string v0,"NDK"
invoke-static{v0},Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
這個調用static void  System.loadLibrary(v0)來編譯NDK的。

2.invoke-super:調用父類方法的指令,通常是onCreat,Ondestory。
3.invoke-direct:調用private函數。
4.invoke-virtual :調用非private函數。修改smali中不要錯用direct/virtual函數。
5.invoke-xxx/range:當方法的參數多餘5個(含5個),不能直接使用以上指令,而是在後面加上"/range",表示範圍。 invoke-direct/range{v0…v5},Lcom.aaa;->h(IIIIIZ)Z

函數返回的結果調用

在java代碼中,調用函數和返回函數結果可以用一條語句完成,而在Smali裏則需要分開來完成,在上面指令後,如果調用的函數返回非void,那麼還需要用move-result(返回基本數據類型)和move-result-object(返回對象)指令:

const-string v0,"eric"
invoke-static{v0},Lcom/aaa;->t(Ljava/lang/String;)Ljava/lang/String;
move-result-object v2
v2 保存的是t方法中的返回的String字符串

分析一段代碼

.method private ifRegister()Z
.locals 2 //有2個本地寄存器
.prologue //開始
const/4 v0, 0x1 //v0賦值爲1
.locals v0,tempFlag:Z
if-eqz v0,:cond_0
const/4 v1,0x1
:goto_0 //標籤
return v1  //返回v1的值
:cond_0 //標籤
const/4 v1,0x0 
goto :goto_0  //跳到goto_0執行,返回v1的值,這裏可以改成retutn v1
.end method
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章