原文鏈接:https://github.com/JesusFreke/smali/wiki
一、概述
smali和backsmali是dalvik虛擬機使用的dex文件的彙編器和反彙編器,它寬鬆的語法基於Jasmin/dedexer語法,並支持dex格式的所有功能。
二、寄存器
dalvik字節碼中,寄存器是32位的,可以存放任意類型的值。64位數據類型可以使用連個寄存器來存放。
(1)指定一個方法中寄存器的數量
有兩種方式可以指定一個方法中可用寄存器的數量。.register指令指定了方法中寄存器的總數,另一種方式,.locals指令指定了方法中非參數寄存器的數量。寄存器的數量包括存放方法參數的寄存器。
(2)方法的參數是如何傳入一個方法
當一個方法被執行,方法的參數將被存放到最後n個寄存器中。如果一個方法有兩個參數,五個寄存器(v0-v4),則參數將被存放到最後兩個寄存器v3和v4中。
非靜態方法的第一個參數總是執行該方法的對象。例如,非靜態方法LMyObject;->callMe(II)V中有兩個×××參數,但是它在兩個×××參數之前隱含一個LMyObject;參數,所以該方法中總共有三個參數。
我們可以指定這個方法中有五個寄存器,可以使用.registers 5指令或者.local 2指令(連個local寄存器和3個參數寄存器)。一旦這個方法被執行,執行該方法的對象將會被存放到v2中,第一個×××參數存放到v3中,第二個×××參數被存放到v4中。
對於靜態方法也是一樣的,只是沒有隱含該參數。
(3)寄存器命名
存在兩種寄存器命名方案,正常的v命名方案和針對參數的p命名方案。p命名方案中第一個寄存器是方法中第一個參數。上個例子中有三個參數和總共五個寄存器。下表分別是v命名方案和p命名方案:
Local | Param | |
v0 | 第一個local寄存器 | |
v1 | 第二個local寄存器 | |
v2 | p0 | 第一參數寄存器 |
v3 | p1 | 第二個參數寄存器 |
v4 | p2 | 第三個參數寄存器 |
(4)引入參數寄存器的動機
p命名方案的引進是很實用的事情,用來解決在編輯smali代碼時一個普通的問題。
例如存在一個有一定數量參數的方法,你將在該方法中添加一些代碼並且你發現你需要一個額外的寄存器。此時你想:“沒什麼大不了的,我只需要修改.register指令就可以了”。不幸的的是,它並沒有這麼簡單。注意,參數被存放在最後的寄存器中。如果你增加了寄存器的數量,也就是說你改變了參數get和put的位置。所以你不得不改變.register指令並且必須對每個參數寄存器進行重新編碼。但是如果你使用p命名方案,你可以簡單的改變寄存器的數量而不需要擔心重新編碼已經存在的寄存器。
注意:默認情況下,baksmali使用p命名方法。如果你想強制baksmali使用v命名方案,你可以使用
-p/--no-parameter-register選項。
(5)Long、Double
前面提到過,long和double是64位值,需要兩個寄存器。這一點是非常重要的。例如,你有個一非靜態方法:LMyObject;->MyMethod(IJZ)V.該方法的參數爲:LMyObject;、int、long、bllo。所以該方法需要5個寄存器存放所有的參數:
Register | Type |
p0 | this |
p1 | I |
p2,p3 | J |
p4 | Z |
三、Types、Methods、Fields
(1)Types
dalvik的字節碼有兩大類型:primitive types和reference types。Reference types是對象和數組,其他的都是primitive type。
primitives用一個字母表示。這些縮略詞不是我提出來的,它們實際上以字符串形式存放在dex文件中。在dex-format.html文檔中有詳細的說明。
V | void-can only be used for return types |
Z | boolean |
B | byte |
S | short |
C | char |
I | int |
J | long(64bits) |
F | float |
D | double(64bits) |
對象的形式:Lpackage/name/ObjectName;L表示它是一個對象類型,package/name/是對象的包,ObjectName是對象的名字,;表示對象名字的結尾。在java中等價於package.name.ObjectName。例如,Ljava/lang/String;等價於java.lang.String。數組的形式[I-表示爲一維×××數組,例如:int[]。要表示多維數組只需要簡單的增加‘[’的數量。[[I=int[][],[[[I=int[][][]等等。注意:數組的最大維度爲255.也可以表示對象的數組,例如:[Ljava/lang/String;表示一個字符串數組。
(2)Methods
methods總是被表示爲一個非常冗長的形式包括:包含方法的類型、方法的名稱、參數的類型和返回類型。虛擬機需要這些信息才能正確的定位方法,在字節碼中執行靜態方法。
Methods形式例子:
Lpackage/name/ObjectName;->MethodName(III)Z
在這個例子中:Lpackage/name/ObjectName;爲類型,MethodsName是方法的名稱。(III)Z是方法的聲明。III表示有三個int類型的參數,Z表示返回類型是boolean。
一個更加複雜的例子:
method(I[[IILjava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
java中表示爲:
String method(int,int[][],int,String,Object[])
(3)Fields
同樣的,fields總是表示爲一個冗長的形式包括:包含field的類型、field的名稱、field的類型。同樣的,這是爲了讓虛擬機可以找到正確的field,同樣的適用於字節碼上的靜態分析。
Fieldsde 形式:
Lpackage/name/ObjectName;->FieldName:Ljava/lang/String;