安卓逆向學習----Dalvik彙編代碼(smali語言)

這裏的dalvik彙編代碼值得是針對dalvik虛擬機設計的指令集,與一般的彙編代碼不同

1.dalvik指令的格式

Dalvik彙編代碼由一系列的dalvik指令組成,指令語法由指令位描述和格式標識來決定,這部分感覺沒用先不看了,在Android4.0及之前的安卓源碼Dalvik/docs目錄下的instruction-formats.html可以看到具體介紹

2.相關工具

smali.jar用來將smali文件彙編爲dex文件,而baksmali.jar則是用來將dex文件反彙編爲smali文件,ddx.jar將dex文件反彙編爲ddx文件,其語法規則與smali類似

使用方法分別爲:

java -jar smali.jar -o xxx.dex  xxx.smali
java -jar baksmali.jar -o ouput/ XXX.dex
java -jar baksmali.jar -o ouput/ XXX.dex

 3.dalvik寄存器

dalvik虛擬機採用ARM架構,將一部分寄存器映射到ARM寄存器上,另一部分使用調用棧進行模擬。Dalvik的寄存器都是32位的,64位需要用兩個相鄰寄存器表示。寄存器編號取值範圍爲v0-v65535

Android SDK中有名爲dalvik.bytecode.Opcodes的接口,處理字節碼的函數爲一個宏HANDLE_OPCODE(),每個字節碼對應一個cpp文件,在其中對這個宏進行重寫。

寄存器的命名有兩種,v命名和p命名。一個函數有m個寄存器用於儲存局部變量和隱式傳入的函數引用對象,n個用於儲存參數,v命名法將寄存器命名爲v0~vm+n-1,而p命名法前m個寄存器仍然爲v0~vm-1,儲存參數的寄存器則被命名爲p0~pn-1。

4.Dalvik字節碼的類型、方法與字段表示方法

可以參考官網https://source.android.com/devices/tech/dalvik/dalvik-bytecode的介紹,百度了好多還是官網說的比較清楚

(1)類型:

Dalvik字節碼只有基本類型和引用類型兩種,這兩種類型可以表示Java語言的全部類型,除了對象和數組屬於引用對象以外,其它的Java類型都是基本類型 

  • J、D這種64位的類型需要使用相鄰的兩個寄存器儲存
  • L可以代表Java類型的任何類,這些類在java代碼中以package.name.ObjectName的方式引用,對應的彙編形式爲Lpackage/name/ObjectName;最後有一個分號。例如Ljava/lang/String;對應於java.lang.String
  • [表示所有基本類型的數組,[後面緊跟類型描述符,例如[I代表java中的int[],n個[表示n維數組,與L組合就可以表示對象數組,[Ljava/lang/String就表示字符串數組

(2)方法

方法的格式爲Lpackage/name/ObjectName;->MethodName(III)Z   III表示該方法有三個int型參數,Z表示返回值爲boolean型,轉換爲java方法應該爲Boolean MethodName(int,int, int)

(3)字段

格式爲Lpackage/name/ObjectName;->FildName:Ljava/lang/String;

5.Dalvik指令集

(1)特點

參數順序先是目的操作數,後是源操作數

名稱後綴:64位字節碼添加-wide後綴,特殊類型字節碼根據具體類型添加後綴,例如-boolean -byte -class -void。

字節碼後綴:根據佈局和選項的不同,添加後綴用“/”分隔開例如/from16表示源爲16位的寄存器變量

(2)空操作指令

助記符爲nop,被用來對齊代碼

(3)數據操作指令

move vA, vB 將vB的值賦給vA ;  move-wide vA, vB 賦值爲64位

寄存器默認爲4位,/16表示源寄存器和目的寄存器都爲16位,/from16表示源爲16位  -object表示爲對象賦值,

move-result vA表示將上一個invoke類型操作的單字非對象結果賦值給vA寄存器,這裏只有destination

move-exception vA 保存一個異常到vA寄存器

(4)返回指令

return-void 從一個void方法返回

return-vAA 表示返回一個32位非對象類型的值,返回值寄存器爲8位的寄存器

return-wide vAA 表示函數返回64位非對象類型的值,返回值寄存器爲8位的寄存器

return-object vAA 表示函數返回對象類型的值,返回值寄存器爲8位的寄存器

(5)數據定義指令

const-wide/16 vAA, #+BBBB   將該常量值符號拓展至64位後賦值給vAA

......

(6)鎖指令

monitor-enter vAA 爲指定的對象獲取鎖

monitor-exit vAA 釋放指定對象的鎖

(7)實例操作指令

包括實例的類型轉換、檢查以及新建等

check-cast vAA,type@BBBB 將vAA寄存器中的對象引用轉換爲指定類型,失敗會拋出異常

instance-of vA , vB ,type@CCCC  判斷vB寄存器中的對象引用是否可以轉換爲指定類型,可以則給vA賦值爲1,否則爲0

new-instance vAA,type@BBBB 構造一個制定類型對象的新實例,並將對象引用賦值給vAA寄存器,type不能是數組類型

這三種指令加上/jumbo之後功能一樣,但是寄存器值與指令索引取值範圍更大

(8)數組操作指令

array-length vA, vB 獲取vB寄存器數組的長度並將值賦給vA寄存器,數組長度指的是數組條目個數

new-array vA, vB, type@CCCC 構造指定類型的與大小的數組,將索引賦給vA,大小由vB決定

filled-new-array {vC,vD,vE,vF,vG} , type@BBBB 構造指定類型與大小的數組並填充數組內容,大小由vA隱含提供,並將值賦給vA

fill-array-data vAA,+BBBBBBBB 用指定的數據填充數組,vAA寄存器爲數組引用,引用必須爲基礎類型的數組

(9)異常指令

throw vAA 拋出vAA寄存器中制定的異常

(10)跳轉指令

分爲無條件跳轉goto、分支跳轉switch與條件跳轉if

goto +AA 跳轉AA的偏移量

packed-switch vAA, +BBBBBBBB分支跳轉指令,vAA寄存器爲switch分支需要判斷的值。+BBBBBBB指向一個packed-switch-payload格式的偏移表

sparse-switch vAA, +BBBBBBBB分支跳轉指令,vAA寄存器爲switch分支需要判斷的值。+BBBBBBB指向一個sparse-switch-payload格式的偏移表

if-xx  vA,vB, +CCCC 比較vA和vB,滿足xx條件跳轉到CCCC的偏移處,條件包括eq(等於)、ne(不等於)、lt(小於)、ge(大於等於)等等

if-xxx  vA, +CCCC 把vA和0比較,滿足xxx條件跳轉到CCCC的偏移處,條件包括eqz(等於)、nez(不等於)、ltz(小於)、gez(大於等於)等等

(11)比較指令

cmpl-xxx vAA,vBB,vCC  如果vBB小於vCC,則結果爲1,相等爲0,否則-1,xxx指的是float、double

cmpg-xxx vAA,vBB,vCC  如果vBB大於vCC,則結果爲1,相等爲0,否則-1,xxx指的是float、double

cmp-long vAA,vBB,vCC  如果vBB大於vCC,則結果爲1,相等爲0,否則-1

(12)字段操作指令

這些指令用來對對象實例的字段進行讀寫操作,按照普通字段和靜態字段分爲兩類:

iop vA,vB, field@CCCC 和 sop vAA, field@BBBB

iop代表iget iget-wide iget-object iget-boolean iget-byte iput iput-wide......

sop代表sget sget-wide sget-object sget-boolean sget-byte sput sput-wide......

其中get表示讀操作,put表示寫操作

(13)方法調用指令

invoke-kind {vC,vD,vE,vF,vG},meth@BBBB和 invoke-kind/range {vCCCC..vNNN},meth@BBBB ,二者相似,只是後者使用range指定寄存器範圍。按照調用方法的kind具體分爲以下幾種:

invoke-virtual 或 invoke-virtual/range調用實例的虛方法

invoke-super 或 invoke-super/range調用實例的父類方法

invoke-direct 或 invoke-direct/range調用實例的直接方法

invoke-static 或 invoke-static/range調用實例的靜態方法invoke-interface 或 invoke-interface/ragne調用實例的接口方法

(14)數據轉換指令

op vA,vB  表示將VB寄存器的數據進行轉換然後儲存到vA寄存器.op分爲以下幾種

neg-int/long/float/double 求補

not-int/long/float/double 求補

int-to-long int轉換爲long

float-to-int

.......

(15)數據運算指令

數據運算指令包括算術運算指令(加減乘除移位等)和邏輯運算指令(與或非異或得等)

指令格式爲以下幾種:

op vAA,vBB,vCC  將vBB,vCC 進行運算,保存到vAA

op/2addr vA,vB    將 vA,vB進行運算,保存到vA

op/lit16 vA,vB,#+CCCC 將vB與常量CCCC運算,保存到vA

op/lit8 vAA,vBB,#+CC   將vBB與常量CC運算,保存到vAA

op包含add-type sub-type mul-type div-type rem-type and-type or-type xor-type shl-type shr-type ushr-type

6.smali代碼實例

.class public LHelloWorld; 	#定義類名
.super Ljava/lang/Object; 	#定義父類
.method public static main([Ljava/lang/String;)V
        .registers 4		#程序中使用4個寄存器,v0,v1,v2和一個參數寄存器
        .parameter		#一個參數,有n個參數則有n行
        .prologue		#代碼起始的指令
        #空指令
        nop
        nop
        nop
        nop
        #數據定義指令
        const/16 v0, 0x8
    	const/4 v1, 0x5
	const/4 v2, 0x3
	#數據操作指令
	move v1, v2
	#數組操作指令
	new-array v0, v0, [I
	array-length v1, v0
	#實例操作指令
	new-instance v1, Ljava/lang/StringBuilder;
	#方法調用指令
	invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V
	#跳轉指令
	if-nez v0, :cond_0
	goto :goto_0
	:cond_0
	#數據轉換指令
	int-to-float v2, v2
	#數據運算指令
	add-float v2, v2, v2
	#比較指令
	cmpl-float v0, v2, v2
	#字段操作指令
	sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
	const-string v1, "Hello World" #構造字符串
	#方法調用指令
	invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
	#返回指令
	:goto_0
        return-void
.end method

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章