網絡安全攻防:Android App逆向
1. Dalvik虛擬機概述
Google于2007年底正式發布了Android SDK,Dalvik虛擬機也第一次進入了我們的視野。它的作者是丹·伯恩斯坦(Dan Bornstein),名字來源于他的祖先曾經居住過的名叫Dalvik的小漁村。Dalvik虛擬機作為Android平臺的核心組件,擁有如下幾個特點。
(1)體積小,占用內存空間小。
(2)專用的DEX可執行文件格式,體積更小,執行速度更快。
(3)常量池采用32位索引值,尋址類方法名、字段名、常量更快。
(4)基于寄存器架構,并擁有一套完整的指令系統。
(5)提供了對象生命周期管理、堆棧管理、線程管理、安全和異常管理以及垃圾回收等重要功能。
(6)所有的Android程序都運行在Android系統進程里,每個進程對應著一個Dalvik虛擬機實例。
Dalvik虛擬機與傳統的Java虛擬機有著許多不同點,兩者并不兼容,它們顯著的不同點主要表現在以下幾個方面。
(1)Java虛擬機運行的是Java字節碼,Dalvik虛擬機運行的是Dalvik字節碼。
(2)Dalvik可執行文件體積更小。
(3)Java虛擬機基于棧架構,Dalvik虛擬機基于寄存器架構。
2. Smali概述
Dalvik虛擬機(Dalvik VM)是Google專門為Android平臺設計的一套虛擬機。區別于標準Java虛擬機JVM的class文件格式,Dalvik VM擁有專屬的DEX可執行文件格式和指令集代碼。Smali和Baksmali 則是針對 DEX 執行文件格式的匯編器和反匯編器,反匯編后DEX文件會產生.smali后綴的代碼文件,Smali代碼擁有特定的格式與語法,Smali語言是對Dalvik虛擬機字節碼的一種解釋。
Smali 語言起初是由一個名叫 JesusFreke 的黑客對 Dalvik 字節碼的翻譯,并非一種官方標準語言,因為 Dalvik 虛擬機名字來源于冰島一個小漁村的名字,Smali和Baksmali 便取自冰島語中的“匯編器”和“反編器”。目前,Smali 是在 Google Code上的一個開源項目。
雖然主流的DEX可執行文件反匯編工具不少,如Dedexer、IDA Pro,但Smali提供反匯編功能的同時,也提供了打包反匯編代碼重新生成 DEX 的功能,因此 Smali 被廣泛地用于App廣告注入、漢化和破解,ROM定制等方面。
3. APKtool工具介紹
APKtool工具是在Smali工具的基礎上進行封裝和改進的,除了對DEX文件的匯編和反匯編功能外,還可以對 APK 中已編譯成二進制的資源文件進行反編譯和重新編譯。同時也支持給Smali代碼添加調試信息以支持斷點調試。
在介紹APKtool使用之前,我們先來看一個APK包的組成。APK文件其實是zip壓縮包格式,使用工具進行解壓后,以HelloWorld.apk為例,如圖1所示。
圖1 解壓文件
使用APKtool反編譯HelloWorld.apk文件的方法,如圖2所示。
圖2 反編譯文件
執行apktool d命令成功后會在HelloWorld目錄下產生如下所示的一級目錄結構,如圖3所示。
圖3 一級目錄結構
在瀏覽各個子目錄的結構后,我們可以發現其結構原始 App 工程目錄結構基本一致,Smali目錄結構對應原始的Java源碼SRC目錄,而META-INF目錄已經不見了,因為反編譯會丟失簽名信息。反編譯后會多生成apktool.yml文件,這個文件記錄著APKtool版本和APK文件名和是否是framework文件等基本信息,在APKtool重新編譯時會使用到。
4. Smali格式結構介紹
(1)文件格式
無論是普通類、抽象類、接口類或內部類,在反編譯出的代碼中,它們都以單獨的Smali文件來存放。每個Smali文件頭3行描述了當前類的一些信息,格式如下。
- .class<訪問權限>[修飾關鍵字]<類名>
- .super<父類名>
- .source<源文件名>
打開HelloWorld.smali文件,頭3行代碼如下。
- .class public LHelloWorld;
- .super Landroid/app/Activity;
- .source "HelloWorld.java"
第1行“.class”指令指定了當前類的類名。在本例中,類的訪問權限為public,類名為“LHelloWorld;”,類名開頭的L是遵循Dalvik字節碼的相關約定,表示后面跟隨的字符串為一個類。
第 2 行的“.super”指令指定了當前類的父類。本例中的“LHelloWorld;”的父類為“Landroid/app/Activity;”。
第3行的“.source”指令指定了當前類的源文件名。經過混淆的DEX文件,反編譯出來的Smali代碼可能沒有源文件信息,因此,“.source”行的代碼可能為空。
前3行代碼過后就是類的主體部分了,一個類可以由多個字段或方法組成。
(2)類的結構
無論普通類、抽象類、接口類還是內部類,反編譯的時候會為每個類單獨生成一個Smali文件,但是內部類相存在相對比較特殊的地方。
內部類的文件是“[外部類]$[內部類].smali”的形式來命名的,匿名內部類文件以“[外部類]$[數字].smali”來命名。
內部類訪問外部類的私有方法和變量時,都要通過編譯器生成的“合成方法”來間接訪問。
編譯器會把外部類的引用作為第一個參數插入到會內部類的構造器參數列表。
內部類的構造器中是先保存外部類的引用到一個“合成變量”,再初始化外部類,最后才初始化自身。