1、Smile語法入門
Smali是Java虛擬機中的一種.dex格式文件彙編器,它採用一種寬鬆式的Jasmin/dedexer的語法,表達了.dex格式所有功能和信息,如果瞭解Smali語言基礎,在我們反編譯程序時就可以看出程序的大致代碼,這對於分析競品可能有所幫助,下面一起學習下Smali基礎知識
- 原始類型映射關係:這裏的映射關係和JNI中一致
- v :void ()
- Z:boolean
- B:byte
- S:short
- C:char
- I:int
- J:long
- F:float
- D:double
- 類的表示:smile中採用全路徑的方式進行展示,如:Ljava/lang/String;
- L:表明此處表示類
- java/lang/String:類的全路徑名稱
- ;:類聲明結束符號
- 數組表示:數組採用[+元素類型的方式,如[I 、[Ljava/lang/String
- [:表示聲明數組
- I:表示數組的類型
- [[I:表示二維數組
- 對象的表示:對象的表示分爲兩部分,一部分是持有此對象類型的表示,第二部分是變量展示即變量名+變量類型,如: Ljava/lang/String; ——> FieldName : Ljava/lang/String;
- Ljava/lang/String;:類的全路徑
- FieldName:對象名稱
- Ljava/lang/String;:對象類型
- 方法表示:使用方法的所在類、方法名、方法簽名表示,如 Ljava/lang/String; ——> method(III)V
- Ljava/lang/String;:類的全路徑
- method:方法名稱
- III:方法的傳參類型,傳入3個Int類型數據
- V :方法返回類型
- 方法參數:.param p1, “savedInstanceState” # Landroid/os/Bundle;
- .param:表示參數
- p1:寄存器
- “savedInstanceState":參數名稱
- # Landroid/os/Bundle;:參數類型
- 寄存器:表示此方法在執行中需要的存儲空間個位置
- 方法中聲明使用寄存器個數
.registers 指令指定了方法中寄存器的總數
.locals 指令表明了方法中非參寄存器的總數,出現在方法中的第一行 - 方法的參數被放在最後幾個寄存器中
1.1、Smile實戰示例
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = findViewById(R.id.textView);
}
@Subscribe
public void action(TypeEvent event) {
}
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
public void actionTwo(TypeEvent event) {
}
}
- 查看生成的smali代碼
.class public Lcom/alex/kotlin/jniapplication/MainActivity; //表明類文件的路徑和名稱
.super Landroid/support/v7/app/AppCompatActivity; //繼承的父類
.source "MainActivity.java" //當前類的名稱
# direct methods //聲明方法開始
.method public constructor <init>()V //構造函數
.locals 0
.line 10 //代碼行數
invoke-direct {p0}, Landroid/support/v7/app/AppCompatActivity;-><init>()V
return-void
.end method //聲明方法結束
# virtual methods
.method public action(Lcom/alex/kotlin/jniapplication/TypeEvent;)V //action()標明瞭方法名、參數、返回類型
.locals 0
.param p1, "event" # Lcom/alex/kotlin/jniapplication/TypeEvent; //參數
.annotation runtime Lorg/greenrobot/eventbus/Subscribe; //方法註解
.end annotation
.line 22
return-void
.end method
.method public actionTwo(Lcom/alex/kotlin/jniapplication/TypeEvent;)V //actionTwo()
.locals 0
.param p1, "event" # Lcom/alex/kotlin/jniapplication/TypeEvent;
.annotation runtime Lorg/greenrobot/eventbus/Subscribe;
sticky = true
threadMode = .enum Lorg/greenrobot/eventbus/ThreadMode;->MAIN:Lorg/greenrobot/eventbus/ThreadMode;
.end annotation
.line 27
return-void
.end method
.method protected onCreate(Landroid/os/Bundle;)V //create()
.locals 1 //聲明寄存器個數
.param p1, "savedInstanceState" # Landroid/os/Bundle; //聲明參數
.line 14
invoke-super {p0, p1}, Landroid/support/v7/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V //代碼語句
.line 15
const v0, 0x7f09001c
invoke-virtual {p0, v0}, Lcom/alex/kotlin/jniapplication/MainActivity;->setContentView(I)V //代碼語句
.line 16
const v0, 0x7f070087
invoke-virtual {p0, v0}, Lcom/alex/kotlin/jniapplication/MainActivity;->findViewById(I)Landroid/view/View;//代碼語句
move-result-object v0
.line 17
.local v0, "textView":Landroid/widget/TextView;
return-void
.end method
從上面示例編譯後的smile語言中,根據smile的語句特性可以清晰閱讀到整個代碼的內容,相比之下還是很清晰的,具體細節見代碼註釋;
2、ApkTool反編譯
- 反編譯APK
在開發中使用發編譯的場景不多,但如果想查看某些產品的實現和代碼,就要先將產品反編譯出來,反編譯的過程也很簡單,下面一起使用ApkTool反編譯一個apk,直接命令行工具反編譯現有的apk
apktool d app-debug.apk //編譯解析出資源文件,但會將Class文件解析成smali文件
apktool d -s app.apk //編譯解析出資源文件,但會將Class文件解析成dex文件
- 編譯結果——smile語言
- 反編譯dex語言
- 將dex編譯爲Jar包
- 將上面編譯的classes.dex文件複製到dexjar文件夾下,執行命令語句
sh d2j-dex2jar.sh classes.dex
sudo chmod +x d2j_invoke.sh //處理文件權限
- 文件夾下會自動生成classes-dex2jar.jar
- JD-GUI查看生成的代碼
- 打開GUI將第二部生成的classes-dex2jar.jar包拖入即可查看生成對應的Java代碼
3、Jadx反編譯
- 配置導入工具
git clone https://github.com/skylot/jadx.git //先下載jadx文件
cd jadx
./gradlew dist //編譯
- jadx使用
- 雙擊bin/jadx-gui運行程序,此時會啓動操作界面和terminal(顯示操作日誌)
- 將要反編譯的APK拖進窗口即可自動編譯
- 反編譯的代碼可以在Java 和 Smali之間切換
- 代碼搜索功能
- Navigation -> Text Search 或者 Navigation -> Class Search啓動代碼搜索功能
- jadx支持 Class、Method、Field、Code四個維度的搜索
- 在代碼中可以直接右鍵 -> Find Usage 查找代碼
- deobfuscation
- 對於代碼混淆的APK,點擊 “ 工具 -> 反混淆 “會自動爲每個類創建別名方便查看和查找
- 導出Gradle工程
- 點擊“File -> 導出Gradle工程 “
- 反編譯出現卡頓
- 針對出現的卡頓修改併發線程:File -> 首選項 ->. 並行線程數 (默認爲4)
4、二次打包
關於二次打包主要是修改編譯後smaile代碼,然後重新打包簽名即可,這裏就是要看懂smile代碼,另外保證修改後的smile代碼不衝突即可,詳細參見Android反編譯和二次打包實戰