編輯:關於Android編程
靜態分析Android程序的兩種方法:
一、閱讀反編譯生成的Dalvik字節碼。
1、使用文本編輯器閱讀baksmali反編譯生成的smali文件
(1)解壓apk包
unzip xxx.apk
(2)用baksmali進行對解壓出來的dex文件反編譯
java -jar baksmali-2.0.3.jar classes.dex
2、使用IDA Pro分析dex文件
二、閱讀反編譯生成的Java源碼
(1) 使用 dex2jar 把classes.dex轉換成jar
java -jar dex2jar classes.dex
(2)使用jd-gui 打開這個jar
本篇文章注意介紹第一種方式得到smali文件之後,對應smali文件進行分析。
無論是普通類、抽象類、接口類或者內部類,在反編譯的代碼中,它們都會以單獨的smali文件存放。每個smali文件都由若干語句組成,所有的語句都遵循著一套語法規范。下面來具體介紹。
一、頭信息——類的主體信息
在打開smali文件的時候,它的頭三行描述了當前類的一些信息。
.class <訪問權限> [關鍵修飾字] <類名>;
.super <父類名>;
.source <源文件名>
例如:
//===================================================================
public class MainActivity extends AppCompatActivity {
// ......
}
//===================================================================
.class public Ltestdemo/hpp/cn/test/MainActivity;
.super Landroid/support/v7/app/AppCompatActivity;
.source "MainActivity.java"
//===================================================================
.class指令表示當前的類名,類的訪問權限是public,類名為Ltestdemo/hpp/cn/test/MainActivity,類開頭的L是遵循Dalvik字節碼的相關約定,表示後面跟隨的字符串是一個類。
.super指定了當前類所繼承的父類,後面指的就是這個父類的類名,L表示後面跟的字符串是一個類
.source指定了當前類的源文件名
注意:經過混淆的dex文件,反編譯出來的smali代碼可能沒有源文件信息,因此source行的代碼可能為空。
這三行就是類的主體部分了,另外一個類是由多個字段或者方法組成。
二、接口
如果一個類實現了一個接口,那麼會在smali文件中使用.implements指令指出。
#interfaces
.implements <接口名>
同樣,#interfaces是注釋,.implements是接口關鍵字。
例如:
//===================================================================
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
// ......
}
//===================================================================
# interfaces
.implements Landroid/view/View$OnClickListener;
//===================================================================
三、smali基本語法
Davlik字節碼中,寄存器都是32位的,能夠支持任何類型,64位類型(Long/Double)用2個寄存器表示;
Dalvik字節碼有兩種類型:原始類型;引用類型(包括對象和數組)
1、原始類型
V void (只能用於返回值類型)
Z boolean
B byte
S short
C char
I int
J long(64位)
F float
D double(64位)
2、對象類型
Lpackage/name/ObjectName; 相當於java中的package.name.ObjectName;
L 表示這是一個對象類型
package/name 該對象所在的包
ObjectName 對象名稱
; 標識對象名稱的結束
3、數組類型
[I :表示一個整形的一維數組,相當於java的int[];
對於多維數組,只要增加[ 就行了,[[I = int[][];注:每一維最多255個;
對象數組的表示形式:
[Ljava/lang/String 表示一個String的對象數組;
4、寄存器與變量
android變量都是存放在寄存器中的,寄存器為32位,可以支持任何類型,其中long和double是64為的,需要使用兩個寄存器保存。
寄存器采用v和p來命名,v表示本地寄存器,p表示參數寄存器。
例如:
//===================================================================
private void print(String string) {
Log.d(TAG, string);
}
//===================================================================
.method private print(Ljava/lang/String;)V
.registers 3
.param p1, "string" # Ljava/lang/String;
.prologue
.line 29
const-string v0, "MainActivity"
invoke-static {v0, p1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 30
return-void
.end method
//===================================================================
.registers 3 說明該方法有三個寄存器,其中一個本地寄存器v0,兩個參數寄存器p0,p1,細心的人可能會注意到沒有看到p0,原因是p0存放的是this。如果是靜態方法的話就只有2個寄存器了,不需要存this了。
5、基本指令
smali字節碼是類似於匯編的,如果你有匯編基礎,理解起來是非常容易的。
move v0, v3 把v3寄存器的值移動到寄存器v0上
const-string v0, “MainActivity” 把字符串”MainActivity”賦值給v0寄存器
invoke-super 調用父函數
return-void 函數返回void
new-instance 創建實例
iput-object 對象賦值
iget-object 調用對象
invoke-static 調用靜態函數
invoke-direct 調用函數
例如:
//===================================================================
@Override
public void onClick(View view) {
String str = "Hello World!";
print(str);
}
//===================================================================
# virtual methods
# 參數類型為Landroid/view/View,返回類型為V
.method public onClick(Landroid/view/View;)V
# 表示有三個寄存器
.registers 3
# 參數View類型的view變量對應的是寄存器p1
.param p1, "view" # Landroid/view/View;
.prologue
.line 24
#將"Hello World!"字符串放到寄存器v0中
const-string v0, "Hello World!"
.line 25
# 定義一個Ljava/lang/String類型的str變量對應本地寄存器v0
.local v0, "str":Ljava/lang/String;
# 調用該類的print方法,該方法的參數類型為Ljava/lang/String,返回值為V
# 調用print方法傳入的參數為{p0, v0},及print(p0, v0),p0為this,v0為"Hello World!"字符串
invoke-direct {p0, v0}, Ltestdemo/hpp/cn/annotationtest/MainActivity;->print(Ljava/lang/String;)V
.line 26
return-void
.end method
//===================================================================
6、if判斷語句
if判斷一共有12條指令:
if-eq vA, VB, cond_** 如果vA等於vB則跳轉到cond_**。相當於if (vA==vB)
if-ne vA, VB, cond_** 如果vA不等於vB則跳轉到cond_**。相當於if (vA!=vB)
if-lt vA, VB, cond_** 如果vA小於vB則跳轉到cond_**。相當於if (vAvB)
if-ge vA, VB, cond_** 如果vA大於等於vB則跳轉到cond_**。相當於if (vA>=vB)
if-eqz vA, :cond_** 如果vA等於0則跳轉到:cond_** 相當於if (VA==0)
if-nez vA, :cond_** 如果vA不等於0則跳轉到:cond_**相當於if (VA!=0)
if-ltz vA, :cond_** 如果vA小於0則跳轉到:cond_**相當於if (VA<0)
if-lez vA, :cond_** 如果vA小於等於0則跳轉到:cond_**相當於if (VA<=0)
if-gtz vA, :cond_** 如果vA大於0則跳轉到:cond_**相當於if (VA>0)
if-gez vA, :cond_** 如果vA大於等於0則跳轉到:cond_**相當於if (VA>=0)
7、循環語句
常用的循環結構有:迭代器循環,for循環,do while循環。
8、switch分支語句
9、try/catch語句
四、字段
smali文件中,字段的聲明使用.field指令,字段分為靜態字段和實例字段。
1、靜態字段
#static fields
.field <訪問權限> static [修飾關鍵字] <字段名>:<字段類型>
可以看到,baksmali在生成smali文件時,會在靜態字段聲明的起始處添加注釋”static fields”,注釋是以#開頭。
訪問權限包括:private、protected、public(三者之一)
修飾關鍵字為字段的其他屬性,例如,final
字段名和類型就不用解釋了
例如:
//===================================================================
private static final String TAG = "MainActivity";
//===================================================================
# static fields
.field private static final TAG:Ljava/lang/String; = "MainActivity"
//===================================================================
2、實例字段
相比於靜態自動就少了一個static的靜態聲明而已,其他都一樣。
#instance fields
.field <訪問權限> [修飾關鍵字] <字段名>:<字段類型>
例如:
//===================================================================
private Button mButton;
//===================================================================
# instance fields
.field private mButton:Landroid/widget/Button;
//===================================================================
五、方法
smali的方法聲明使用的.method指令,方法分為直接方法和虛方法兩種。
1、直接方法
直接方法指的是該類中定義的方法。
#direct methods
.method <訪問權限> [修飾關鍵字] <方法原型>
<.registers>
[.param]
[.prologue]
[.line]
<.local>
<代碼體>
.end method
#direct methods是注釋,是baksmali添加的,訪問權限和修飾關鍵字跟字段是一樣的。
方法原型描述了方法的名稱、參數與返回值。
.registers 指令指定了方法中寄存器的總數,這個數量是參數和本地變量總和。
.param表明了方法的參數,每個.param指令表示一個參數,方法使用了幾個參數就有幾個.parameter指令。
.prologue指定了代碼的開始處,混淆過的代碼可能去掉了該指令。
.line指明了該處代碼在源代碼中的行號,同樣,混淆後的代碼可能去掉了行號。
.local 使用這個指定表明方法中非參寄存器
//===================================================================
private void print(String string) {
Log.d(TAG, string);
}
//===================================================================
.method private print(Ljava/lang/String;)V
.registers 3
.param p1, "string" # Ljava/lang/String;
.prologue
.line 29
const-string v0, "MainActivity"
invoke-static {v0, p1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 30
return-void
.end method
//===================================================================
2、虛方法
虛方法指的是從父類中繼承的方法或者實現的接口的方法,它的聲明跟直接方法相同,只是起始的初始為virtual methods
//===================================================================
@Override
public void onClick(View view) {
String str = "Hello World!";
print(str);
}
//===================================================================
# virtual methods
.method public onClick(Landroid/view/View;)V
.registers 3
.param p1, "view" # Landroid/view/View;
.prologue
.line 24
const-string v0, "Hello World!"
.line 25
.local v0, "str":Ljava/lang/String;
invoke-direct {p0, v0}, Ltestdemo/hpp/cn/annotationtest/MainActivity;->print(Ljava/lang/String;)V
.line 26
return-void
.end method
//===================================================================
3、靜態方法
//===================================================================
public static void setTag(String str) {
TAG = str;
}
//===================================================================
.method public static setTag(Ljava/lang/String;)V
.registers 1
.param p0, "str" # Ljava/lang/String;
.prologue
.line 64
sput-object p0, Ltestdemo/hpp/cn/annotationtest/MainActivity;->TAG:Ljava/lang/String;
.line 65
return-void
.end method
//===================================================================
六、注解
如果一個類使用了注解,那麼smali中會使用.annotation指令。
#annotations
.annotation [注解屬性] <注解類名>
[注解字段 = 值]
.end annotation
注解的作用范圍可以是類、方法或者字段。如果注解的作用范圍是類,.annotation指令會直接定義在smali文件中,如果是方法或者字段,.annotation指令則會包含在方法或者字段的定義中。
1、注解類
//===================================================================
@BindInt(100)
public class MainActivity extends AppCompatActivity {
}
//===================================================================
# annotations
.annotation build Ltestdemo/hpp/cn/annotationtest/BindInt;
value = 0x64
.end annotation
//===================================================================
2、注解字段
//===================================================================
@BindView(R.id.button)
public Button mButton;
//===================================================================
# instance fields
.field public mButton:Landroid/widget/Button;
.annotation build Lbutterknife/BindView;
value = 0x7f0c0050
.end annotation
.end field
//===================================================================
3、注解方法
//===================================================================
@OnClick(R.id.button)
public void click() {
String str = "Hello World!";
print(str);
}
//===================================================================
# virtual methods
.method public click()V
.registers 2
.annotation build Lbutterknife/OnClick;
value = {
0x7f0c0050
}
.end annotation
.prologue
.line 29
const-string v0, "Hello World!"
.line 30
.local v0, "str":Ljava/lang/String;
invoke-direct {p0, v0}, Ltestdemo/hpp/cn/annotationtest/MainActivity;->print(Ljava/lang/String;)V
.line 31
return-void
.end method
//===================================================================
七、應用——smali插樁
插樁的原理就是靜態的修改apk的samli文件,然後重新打包。
1、使用上面的方法得到一個apk的smali文件
2、在關鍵部位添加自己的代碼,需要遵循smili語法,例如在關鍵地方打log,輸出關鍵信息
3、重新進行打包簽名
Frame Animation 表示幀動畫,是順序播放事先做好的圖像,跟電影類似,Android SDK提供了另外一個類AnimationDrawable來定義使用Fra
本節引言: 現在很多門戶類信息網站,比如虎嗅,ifanr,钛媒體等等的APP,簡單點說是信息閱讀類的APP,很多 都是直接嵌套一個WebView用
如何讓qq和微信同步?QQ和微信都是騰訊公司的產品,那麼如果可以做到微信與QQ同步的話,那麼我們平時只要登錄一個就不會錯過兩個軟件的信息了。微信和QQ也是可
什麼是XMLXML全稱為Extensible Markup Language, 意思是可擴展的標記語言,它是 SGML(標准通用標記語言)的一個子集。XML語法上和HTM