0x01 smali生成
使用apktool反編譯apk後,會在反編譯工程目錄下生成一個smali文件夾
其中android下存放所調用庫的smali文件,com才是我們自己寫的代碼的smali文件。
0x02 基礎語法
a.文件基本格式
基本信息
.class
.super
.source
# eg.
.class
public Lcom/reoky/crackme/challengeone/activities/ChallengeActivity;
.super
Landroid/support/v4/app/FragmentActivity;
.source "ChallengeActivity.java" //經過混淆後這項可能為空
類變量聲明
.field :
# eg.
.field actionBar:Landroid/app/ActionBar; ActionBar actionBar;
局部變量聲明:
.local ,:
#eg
.local v0, "ans":Ljava/lang/String; String ans="";
類方法聲明
.method (參數原型)
[.prologue] // 指定代碼開始位置
[.param] // 指定方法參數
[.line] // 指定代碼在源代碼中的行數,混淆後可能不存在
[.locals] // 使用的局部變量個數
.end method
# eg
.method public onTabReselected(Landroid/app/ActionBar$Tab;Landroid/app/FragmentTransaction;)V
.locals 0
.param p1, "tab" # Landroid/app/ActionBar$Tab;
.param p2, "fragmentTransaction" # Landroid/app/FragmentTransaction;
.prologue
.line 55 //可能經過混淆後不存在
return-void
.end method
public void onTabReselected(ActionBar$Tab tab, FragmentTransaction fragmentTransaction){
}
b.原始類型
B—byte
C—char
D—double
F—float
I—int
J—long
S—short
V—void
Z—boolean
[XXX—array
Lpackage/name/ObjName—object // 前面表示對象所在包路徑
c.寄存器操作
傳入的參數寄存器由p表示,而函數內的本地寄存器則由v表示,多個的話則在後面加上0,1,2...
需要注意的是,在非static函數中,p0表示`this`,p1才表示第一個參數。
常量賦值
主要是各種const
const v0, 0x7F030018 # R.layout.activity_challenge #從R中取出靜態值
const/4 v3, 0x2 #4也可以換成16或者high16,表示取整數值
const-string v2, "Challenge" # 取字符串
const-class v2, Context #把類對象取出
變量間賦值
move vx,vy # 將vy的值賦值給vx,也可以是move-object等
move-result vx # 將上個方法調用後的結果賦值給vx,也可以是move-result-object
return-object vx # 將vx的對象作為函數返回值
new-instance v0, ChallengePagerAdapter # 實例化一個對象存入v0中
對象賦值
iput-object a,(this),b 將a的值給b,一般用於b的初始化
iget-object a,(this),b 將b的值給a,一般用於獲取b的地址,接著調用它
# eg.
iput-object v0, p0, ChallengeActivity->actionBar:ActionBar
iget-object v0, p0, ChallengeActivity->actionBar:ActionBar
d.函數操作
最基礎的函數操作一般有以下四個:
1.private:invoke-direct
2.public|protected: invoke-virtual
3.static:invoke-static
4.parent: invoke-super
基本調用形式:invoke-xxx {參數},類;->函數(參數原型)
# eg.
invoke-super {p0, p1}, Landroid/support/v4/app/FragmentActivity;->onCreate(Landroid/os/Bundle;)V
super.onCreate(savedInstanceState); // 其中p0是this,其父類是FragmentActivity,p1,是savedInstanceState,其原型是Bundle;即調用p0->onCreate(p1)
0x03 程序語句相關語法
這裡列舉以下常見程序語句對應的smali語句,並與Android源碼相比較分析
a.判斷語句
if-eq vA, vB, :cond_X 如果vA等於vB則跳轉到:cond_X
if-ne vA, vB, :cond_X 如果vA不等於vB則跳轉到:cond_X
if-lt vA, vB, :cond_X 如果vA小於vB則跳轉到:cond_X
if-ge vA, vB, :cond_X 如果vA大於等於vB則跳轉到:cond_X
if-gt vA, vB, :cond_X 如果vA大於vB則跳轉到:cond_X
if-le vA, vB, :cond_X 如果vA小於等於vB則跳轉到:cond_X
if-eqz vA, :cond_X 如果vA等於0則跳轉到:cond_X
if-nez vA, :cond_X 如果vA不等於0則跳轉到:cond_X
if-ltz vA, :cond_X 如果vA小於0則跳轉到:cond_X
if-gez vA, :cond_X 如果vA大於等於0則跳轉到:cond_X
if-gtz vA, :cond_X 如果vA大於0則跳轉到:cond_X
if-lez vA, :cond_X 如果vA小於等於0則跳轉到:cond_X
b.循環語句
下面列出一個簡單的for循環,其他也差不多
public void encrypt(String str) {
String ans = "";
for (int i = 0 ; i
ans += str.charAt(i);
}
Log.e("ans:",ans);
}
# public void encrypt(String str) {
.method public encrypt(Ljava/lang/String;)V
.locals 4
.param p1, "str"# Ljava/lang/String;
.prologue
# String ans = "";
const-string v0, ""
.local v0, "ans":Ljava/lang/String;
# for (int i 0 ; i
# int i=0 =>v1
const/4 v1, 0x0
.local v1, "i":I
:goto_0# for_start_place
# str.length()=>v2
invoke-virtual {p1}, Ljava/lang/String;->length()I
move-result v2
# i
if-ge v1, v2, :cond_0
# ans += str.charAt(i);
# str.charAt(i) => v2
new-instance v2, Ljava/lang/StringBuilder;
invoke-direct {v2}, Ljava/lang/StringBuilder;->()V
invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v2
#str.charAt(i) => v3
invoke-virtual {p1, v1}, Ljava/lang/String;->charAt(I)C
move-result v3
# ans += v3 =>v0
invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(C)Ljava/lang/StringBuilder;
move-result-object v2
invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v0
# i++
add-int/lit8 v1, v1, 0x1
goto :goto_0
# Log.e("ans:",ans);
:cond_0
const-string v2, "ans:"
invoke-static {v2, v0}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I
return-void
.end method
c.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);
}
#public void encrypt(int flag) {
.method public encrypt(I)V
.locals 2
.param p1, "flag" # I
.prologue
#String ans = null;
const/4 v0, 0x0
.local v0, "ans":Ljava/lang/String;
#switch (flag){
packed-switch p1, :pswitch_data_0 # pswitch_data_0指定case區域的開頭及結尾
#default: ans="noans"
const-string v0, "noans"
#Log.v("ans:",ans)
:goto_0
const-string v1, "ans:"
invoke-static {v1, v0}, Landroid/util/Log;->v(Ljava/lang/String;Ljava/lang/String;)I
return-void
#case 0: ans="ans is 0"
:pswitch_0 #pswitch_
const-string v0, "ans is 0"
goto :goto_0 # break
nop
:pswitch_data_0 #case區域的結束
.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.無規則switch
sparse-switch p1,:sswitch_data_0
...
sswitch_data_0
.sparse-switch
0xa -> : sswitch_0
0xb -> : sswitch_1 # 字符會轉化成數組
d.try-catch語句
public void encrypt(int flag) {
String ans = null;
try {
ans = "ok!";
} catch (Exception e){
ans = e.toString();
}
Log.d("error",ans);
}
#public void encrypt(int flag) {
.method public encrypt(I)V
.locals 3
.param p1, "flag" # I
.prologue
#String ans = null;
const/4 v0, 0x0
.line 20
.local v0, "ans":Ljava/lang/String;
#try { ans="ok!"; }
:try_start_0 # 第一個try開始,
const-string v0, "ok!"
:try_end_0 # 第一個try結束(主要是可能有多個try)
.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
#Log.d("error",ans);
:goto_0
const-string v2, "error"
invoke-static {v2, v0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
return-void
#catch (Exception e){ans = e.toString();}
: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