Android App 反組譯流程(上)
最近接觸到 App 資安檢測,其中的測試項目還蠻多的,細節可以看鐵人賽文章的分享:
這邊想探討的是關於混淆的章節,混淆是為了被反組譯時,不要很容易的就被看出來程式碼的邏輯是怎麼執行的,或是防止被篡改程式的邏輯。
在 Android 上關於啟用混淆的使用,大家應該都不陌生,在 build.gradle 內,build type release 的minifyEnablede
設定成true
,然後將不要混淆要保留的部分(class、function、name… 等) 填入 proguard-rule.pro ,等 build 完後測試 App 行為是否正常,基本上整個混淆就完成了。
那混淆完程式會長什麼樣子呢?或是想知道 proguard-rule 這樣設定,就真的沒有被混淆了,亦或者想知道其他的 App 有沒有做混淆 XD
在要反組譯前,需要有 APK,APK 的取得相比 iPA 來說蠻容易的,有各種的 APK 市集,拿 open source 專案來 build、或是從 Google play 下載後,透過 adb pull 指令將 APK 拿出來。這邊我就拿我的 12 th 鐵人賽的專案來示範,因為有原本的程式碼,也可以設定 debug、release build 後來分析差別。
APK 本身也是一種壓縮格式,是基於 ZIP 檔案格式,因此有一個 APK 後,可以將副檔名改成 zip,這時候就可以在電腦上用解壓縮的方式解開,可以看到裡面會有一些資料夾和檔案,先來看一下 debug 版的 App (沒有混淆的):
結構其實和開發時還蠻類似的,參考 wiki 上的說明
- META-INF 資料夾:儲存著該應用程式的憑證和授權資訊
- AndroidManifest.xml:一個傳統的Android清單檔案
- classes.dex:classes檔案通過DEX編譯後的檔案格式
- res:APK所需要的資原始檔夾
- resources.arsc:程式的語言檔案,就是開發時的 string xml
但直接點開來,會發現很多都是亂碼,不能閱讀,這時候就要使用另外的工具來幫忙了- Apktool !
照著頁面上的教學,下載 Apktool 的 jar 檔,將 Apktool 和 Apk 都放在同一個資料夾後,接著執行指令:
java -jar apktool.jar d -r app-debug.apk
可以看到解完的資料夾內容和直接用 zip 解開的類似,但點開來發現是英文,不是亂碼了。
關於 smali 是什麼,可以參考這篇文章的說明:
關於 Smali ,在 Android 官網中並無相關介紹,它應該出自
JesusFreke
的開源項目 smali。smali/baksmali
是針對dalvik
使用的dex
格式的彙編/反彙編器。它的語法基於Jasmin's/dedexer
,支持dex
格式的所有功能(註釋,調試信息,行信息等等)。因此我們可以認為 smali 和 Dalvik 字節碼文件是等價的。事實上,Apktool
也正是調用這個工程生成的 jar 包來進行反編譯生成 smali 代碼的。對生成的 smali 代碼進行修改之後再重打包,就可以修改 apk 中的邏輯了。因此,能閱讀 smali 代碼對我們進行 android 逆向十分重要。
Smali 檔案打開來會長這樣,是可以大致看得出來是什麼檔案,但還是沒有這麼容易看懂,可以對比原本的程式碼。關於 Smali 檔案一些的細節和代表得意思,之後再來研究一下。
再來是下載 Jadx,這個工具可以將 dex 轉換回 Java,切換到裡面的 bin 資料夾下執行 ,就可以開啟 GUI 的介面,選擇要看的 smali 檔案,就可以發現已經轉換成 Java 格式了。
./jadx-gui
在選擇檔案的時候發現一次只能選擇一個檔案,如果整個專案裡面有很多個檔案,要一個一個選就有點麻煩,有找到一個比較方便的腳本。
裡面整理了 Jadx 和其他工具,並把上述過程: Apktool 轉換成 smali,再轉換成 Java 檔案,都寫在腳本裡面了,下載後執行:
sh decompile-apk.sh app-debug.apk
可以選要用的工具,就選擇第一個,打開 jadx-gui 就可以看到檔案都顯示出來了。
再來是要將解開的檔案包回去 APK 了,用 Apktool 解開的 smali 檔案,透過 Apktool 再包回去,執行這個指令,就可以在將檔案編譯成 APK。
java -jar apktool.jar b app-debug -o app-debug.apk
不過這個 APK 是沒法安裝到手機上的,因為 APK 要安裝到手機上一定要經過 Sign 的流程,確保這個 Apk 的來源是同一個開發者,就如同 App 有新版要更新,上傳到 Google play,要確認 App 是用同一把 key Sign 的,不然就辦法更新了,所以一旦把 key 弄丟,這個 App 就 GG 了 XD,沒辦法再更新。
接著就是要產生一把自己的 key 了,這邊可以參考官方的教學:
- 產生一把 Key:
keytool -genkey -v -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias my-alias
2.接著使用 zipalign:
zipalign -v -p 4 app-debug.apk-unsigned.apk app-debug-unsigned-aligned.apk
3.接著就可以用剛剛產生的 Key,來 Sign Apk 了:
apksigner sign --ks my-release-key.jks --out app-debug-signed.apk app-debug-unsigned-aligned.apk
4.可以用這個指令,來檢查 Apk 是否有 Sign:
apksigner verify app-debug-signed.apk
看一看就大概了解,不是在 Google Play 商店下載的 App,雖然名稱和 Package name 都是一樣的,但有可能是被加料的,已經被重新 Sign 過,有些單純的加入中文的字串,就變成漢化的版本。但也有可能加了一些其他危害資安的程式碼進去了,因此官方才強烈建議透過 Google Play 下載 App,不要裝網路來路不明的 APK,雖然說時不時也會看到 Google Play 上的 App 有資安問題,大家趕快刪除之類的新聞 XD
到這邊就把流程走過一遍了,將 APK 打開、查看、再包回來 APK,但其實還有蠻多細節的可以探討的,還沒用 release 版有混淆的 App 來測試,還有修修改打開的 App ,這些就留到下一篇好了 XD。