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