APK介紹 - Android逆向(一)
手頭上有個(gè)校園寬帶客戶端,拿著想要破解掉,于是開始了我的不歸之路,現(xiàn)在整理一下我的學(xué)習(xí)歷程,也算是一個(gè)知識(shí)儲(chǔ)備吧。
首先奉上enjarify的github地址,小伙伴們可以clone到本地使用哦
APK結(jié)構(gòu)
首先來講一下我們拿到的Android安裝包.apk文件。.apk文件可以使用WinRAR或者7zip等壓縮工具解壓,其本質(zhì)是一個(gè)MIME為ZIP的壓縮包,我們將.apk修改為.zip后可以看到安裝包內(nèi)的文件結(jié)構(gòu),下面是一些常見文件夾:
- assets目錄 存放需要打包到APK中的靜態(tài)文件
- lib目錄 程序依賴的Native庫(kù)
- res目錄 存放應(yīng)用程序的資源
- META-INF目錄 存放應(yīng)用程序簽名和證書的目錄
- AndroidManifest.xml 應(yīng)用程序的配置文件
- classes.dex dex可執(zhí)行文件
- resources.arsc 資源配置文件
一個(gè)典型的文件結(jié)構(gòu)如下:

下面我們就具體講講這些文件或目錄的作用和在逆向中的涉及到的修改操作:
assets目錄
用于存放需要打包到APK中的靜態(tài)文件,和res的不同點(diǎn)在于,assets目錄支持任意深度的子目錄,用戶可以根據(jù)自己的需求任意部署文件夾架構(gòu),而且res目錄下的文件會(huì)在.R文件中生成對(duì)應(yīng)的資源ID,assets不會(huì)自動(dòng)生成對(duì)應(yīng)的ID,訪問的時(shí)候需要AssetManager類。里面可以存放一些html文件或者圖片等資源,可以看一下在正常寫Android程序時(shí)對(duì)assets目錄的使用
lib目錄
這里存放應(yīng)用程序依賴的native庫(kù)文件,一般是用C/C++編寫,這里的lib庫(kù)可能包含4中不同類型,根據(jù)CPU型號(hào)的不同,大體可以分為ARM,ARM-v7a,MIPS,X86,分別對(duì)應(yīng)著ARM架構(gòu),ARM-V7架構(gòu),MIPS架構(gòu)和X86架構(gòu),這些so庫(kù)在APK包中的構(gòu)成如下:

這里的文件都是一些相對(duì)不容易被逆向的程序邏輯,在抖音中,其加密邏輯被編譯到.so文件中,防止被輕易地破解。案例
res目錄
res是resource的縮寫,這個(gè)目錄存放資源文件,存在這個(gè)文件夾下的所有文件都會(huì)映射到Android工程的.R文件中,生成對(duì)應(yīng)的ID,訪問的時(shí)候直接使用資源ID即R.id.filename,res文件夾下可以包含多個(gè)文件夾,其中anim存放動(dòng)畫文件;drawable目錄存放圖像資源;layout目錄存放布局文件;values目錄存放一些特征值,colors.xml存放color顏色值,dimens.xml定義尺寸值,string.xml定義字符串的值,styles.xml定義樣式對(duì)象;xml文件夾存放任意xml文件,在運(yùn)行時(shí)可以通過Resources.getXML()讀?。籸aw是可以直接復(fù)制到設(shè)備中的任意文件,他們無需編譯。
res內(nèi)的內(nèi)容常用來做apk漢化等工作,因?yàn)樵谏厦娴拿枋鲋形覀兛梢钥吹剑诓簧婕斑壿嫷那闆r下,所有的字符串圖像等都在res目錄中被保存,只要我們替換掉那些非本國(guó)語(yǔ)言的字符圖像,就可以實(shí)現(xiàn)程序的漢化,不得不說這非常方便。
META-INF目錄
保存應(yīng)用的簽名信息,簽名信息可以驗(yàn)證APK文件的完整性。AndroidSDK在打包APK時(shí)會(huì)計(jì)算APK包中所有文件的完整性,并且把這些完整性保存到META-INF文件夾下,應(yīng)用程序在安裝的時(shí)候首先會(huì)根據(jù)META-INF文件夾校驗(yàn)APK的完整性,這樣就可以保證APK中的每一個(gè)文件都不能被篡改。以此來確保APK應(yīng)用程序不被惡意修改或者病毒感染,有利于確保Android應(yīng)用的完整性和系統(tǒng)的安全性。META-INF目錄下包含的文件有CERT.RSA,CERT.DSA,CERT.SF和MANIFEST.MF,其中CERT.RSA是開發(fā)者利用私鑰對(duì)APK進(jìn)行簽名的簽名文件,CERT.SF,MANIFEST.MF記錄了文件中文件的SHA-1哈希值。
在逆向完成后重新打包的過程中,我們需要對(duì)其進(jìn)行重新簽名,會(huì)對(duì)這里的內(nèi)容進(jìn)行修改
具體可以看這里:
- apk中的META-INF目錄
- What are the purposes of files in META-INF folder of an APK file?
- android中簽名原理和安全性分析之meta-inf文件講解
AndroidManifest.xml
是Android應(yīng)用程序的配置文件,是一個(gè)用來描述Android應(yīng)用“整體資訊”的設(shè)定文件,簡(jiǎn)單來說,相當(dāng)于Android應(yīng)用向Android系統(tǒng)“自我介紹”的配置文件,Android系統(tǒng)可以根據(jù)這個(gè)“自我介紹”完整地了解APK應(yīng)用程序的資訊,每個(gè)Android應(yīng)用程序都必須包含一個(gè)AndroidManifest.xml文件,且它的名字是固定的,不能修改。我們?cè)陂_發(fā)Android應(yīng)用程序的時(shí)候,一般都把代碼中的每一個(gè)Activity,Service,Provider和Receiver在AndroidManifest.xml中注冊(cè),只有這樣系統(tǒng)才能啟動(dòng)對(duì)應(yīng)的組件,另外這個(gè)文件還包含一些權(quán)限聲明以及使用的SDK版本信息等等。程序打包時(shí),會(huì)把AndroidManifest.xml進(jìn)行簡(jiǎn)單的編譯,便于Android系統(tǒng)識(shí)別,編譯之后的格式是AXML格式,如下圖所示:

在正常的應(yīng)用開發(fā)工程中,AndroidMainifest.xml里包括了應(yīng)用的配置和程序入口,我們可以在這里找到一些有用的信息。
具體可以看Android Developers
而逆向出來的AndroidMainifest.xml是axml,其結(jié)構(gòu)如上圖,具體內(nèi)容如下:
axml頭:其中的axml頭是固定標(biāo)識(shí)axml文件的,其值固定時(shí)0x00080003。
axml文件長(zhǎng)度:標(biāo)識(shí)axml文件的大小。
StringDataSegment:xml文件中所有字符串類型保存在此。
ResourceIdSegment:xml文件中聲明的資源文件ID保存于此。
XmlContentSegment:是xml的內(nèi)容段,按照xml文件中的結(jié)構(gòu)依次排開,保存xml的數(shù)據(jù)內(nèi)容。
classes.dex
這個(gè)是我們逆向時(shí)主要關(guān)注的內(nèi)容之一。傳統(tǒng)的Java程序,首先先把Java文件編譯成class文件,字節(jié)碼都保存在了class文件中,Java虛擬機(jī)可以通過解釋執(zhí)行這些class文件。而Dalvik虛擬機(jī)是在Java虛擬機(jī)進(jìn)行了優(yōu)化,執(zhí)行的是Dalvik字節(jié)碼,而這些Dalvik字節(jié)碼是由Java字節(jié)碼轉(zhuǎn)換而來,一般情況下,Android應(yīng)用在打包時(shí)通過AndroidSDK中的dx工具將Java字節(jié)碼轉(zhuǎn)換為Dalvik字節(jié)碼。dx工具可以對(duì)多個(gè)class文件進(jìn)行合并,重組,優(yōu)化,可以達(dá)到減小體積,縮短運(yùn)行時(shí)間的目的。dx工具的轉(zhuǎn)換過程如圖所示:

如圖,dx工具把每個(gè).class文件的每個(gè)區(qū)域的內(nèi)容進(jìn)行去重,重組,優(yōu)化重排后生成dex文件,生成的dex文件可以在Dalvik虛擬機(jī)執(zhí)行,且速度比較快。
這里有Google的官方檔案
resources.arsc
用來記錄資源文件和資源ID之間的映射關(guān)系,用來根據(jù)資源ID尋找資源。Android的開發(fā)是分模塊的,res目錄專門用來存放資源文件,當(dāng)在代碼中需要調(diào)用資源文件時(shí),只需要調(diào)用findviewbyId()就可以得到資源文件,每當(dāng)在res文件夾下放一個(gè)文件,aapt就會(huì)自動(dòng)生成對(duì)應(yīng)的ID保存在.R文件,我們調(diào)用這個(gè)ID就可以,但是只有這個(gè)ID還不夠,.R文件只是保證編譯程序不報(bào)錯(cuò),實(shí)際上在程序運(yùn)行時(shí),系統(tǒng)要根據(jù)ID去尋找對(duì)應(yīng)的資源路徑,而resources.arsc文件就是用來記錄這些ID和資源文件位置對(duì)應(yīng)關(guān)系的文件。
APK打包流程

具體的操作如下
- 使用 aapt(The Android Asset Packing Tool) 對(duì)資源文件進(jìn)行打包,生成 R.java 文件。
- 如果項(xiàng)目中使用到了 AIDL(Android Interface Definition Language)提供的服務(wù),則需要使用 AIDL 工具解析 AIDL 接口文件生成相應(yīng)的 Java 代碼。
- 使用 javac 將 R.java 和 AIDL 文件編譯為 .class 文件。
- 使用 dx 工具將 class 和第三方的 library 轉(zhuǎn)換為 dex 文件。
- 利用 apkbuilder 將第一步編譯后的資源、第四步生成的 .dex 文件,以及一些其它資源打包到 APK 文件中。
- 這一部主要是對(duì) APK 進(jìn)行簽名??梢苑譃閮煞N情況,如果我們是要發(fā)布 App,那就采用 RealeaseKeystore 簽名;反之,我們?nèi)绻皇窍胍獙?duì) App 進(jìn)行調(diào)試,那就使用 debug.keystore 簽名。
- 在發(fā)布正式版之前,我們需要將 APK 包中資源文件距離文件的起始偏移修改為 4 字節(jié)的整數(shù)倍數(shù),這樣,在之后運(yùn)行 App 的時(shí)候,速度會(huì)比較快。
APK安裝過程
Adroid的應(yīng)用安裝涉及到如下幾個(gè)目錄:
/data/app:存放用戶安裝的APK的目錄,安裝時(shí),把APK拷貝于此。
/data/data:應(yīng)用安裝完成后,在/data/data目錄下自動(dòng)生成和APK包名(packagename)一樣的文件夾,用于存放應(yīng)用程序的數(shù)據(jù)。
/data/dalvik-cache:存放APK的odex文件,便于應(yīng)用啟動(dòng)時(shí)直接執(zhí)行。
具體安裝過程如下:
安裝過程:復(fù)制apk安裝包到/data/app目錄下,解壓并掃描安裝包,向資源管理器注入apk資源,解析AndroidManifest文件,并在/data/data目錄下創(chuàng)建對(duì)應(yīng)的應(yīng)用數(shù)據(jù)目錄,,如果APK中有l(wèi)ib庫(kù),系統(tǒng)會(huì)判斷這些so庫(kù)的名字,查看是否以lib開頭,是否以.so結(jié)尾,再根據(jù)CPU的架構(gòu)解壓對(duì)應(yīng)的so庫(kù)到/data/data/{pkg}/lib下。然后針對(duì)dalvik/art環(huán)境優(yōu)化dex文件,保存到dalvik-cache目錄,將AndroidManifest文件解析出的組件、權(quán)限注冊(cè)到PackageManagerService,完成后發(fā)送廣播。
APK安裝的時(shí)候會(huì)把DEX文件解壓并且優(yōu)化位odex,odex的格式如圖所示:

odex在原來的dex文件頭添加了一些數(shù)據(jù),在文件尾部添加了程序運(yùn)行時(shí)需要的依賴庫(kù)和輔助數(shù)據(jù),使得程序運(yùn)行速度更快。
下面是一張整體的流程圖:

時(shí)序圖如下:

參考資料
Android應(yīng)用安裝流程分析 - solart
首發(fā)于個(gè)人博客Bismuth