你們都知道,Android Studio
開發工具自帶了Analyze Apk
,能夠很方便的分析Apk文件。具體位於菜單build >> Analyze APK...
路徑下,點擊後能夠選擇目標路徑下的Apk文件,甚至能夠直接將目標APK文件直接拖拽到Android Studio
中,不到幾秒中時間,立刻就會生成對應的分析結果。html
例如,微信Apk分析結果是這樣的:
java
又如,支付寶Apk分析結果: android
瞬間感受本身很niubility,有沒有?git
有時候,咱們也常常用它來分析本身的Apk,例如,生成的安裝包到底長什麼樣子,裏面的資源/代碼構成,Manifest
中配置是否如預期,又或者方法數,等等。可是,一次忽然的機會,發現本身開發的Ap分析不了,一直處於Parsing Manifest
狀態。es6
一臉懵逼,有木有? github
這個問題曾經困惑了我很多時間,以前也沒有具體去研究過。如今又遇到了。瞬間想到魯迅說的一句話:安全
技術路上,會遇到不少看似莫名其妙的問題。
細心探究,解決了,就是成長。
無視它並避讓過去,看似繞過了問題,
實際上失去了一次很好的技術歷練的機會。
而且下次極可能還會遇到相似的,甚至同樣的問題,
長期看將是困難和停滯。
複製代碼
既然先輩都這樣說了,那,硬着頭皮解一下?bash
如今給人的感受是Analyze APK
執行過程當中直接停住了,後者長時間一直在分析。但無論怎樣,畢竟是在AS中的操做,先查一下對應的AS日誌,看看有沒Parsing Manifest
或相關的日誌信息,能夠起到幫助的。微信
Help >> Show Log in Finder
,打開日誌,對應時間點看了又看,沒找到Parsing Manifest
直接相關的,不過,找到了控件顯示先關的日誌:app
2019-08-08 19:21:25,323 [entQueue-0] INFO - ools.idea.apk.viewer.ApkEditor - Disposing ApkEditor with ApkViewPanel: com.android.tools.idea.apk.viewer.ApkViewPanel@7a608115
2019-08-08 19:21:25,323 [entQueue-0] INFO - s.idea.apk.viewer.ApkViewPanel - Cleared Archive on ApkViewPanel: com.android.tools.idea.apk.viewer.ApkViewPanel@7a608115
複製代碼
從日誌裏面能夠看出來,AS中對應的Analyze Apk
相關的類名有ApkEditor
、ApkViewPanel
,包名是com.android.tools.idea.apk.viewer
。AS日誌部分的有效信息只有這麼多了。
AS日誌中沒有,那有沒有可能存在有效的信息輸出在了系統其它的地方?因而,直接花點時間,系統全局搜索下。
/ grep -rnl "Parsing Manifest" *
複製代碼
輸出信息中有一些警告信息之類的,最終在輸出信息中找到了個相關的:
Applications/Android Studio.app/Contents/plugins/android/lib/android.jar
複製代碼
看目錄名,大概是AS插件中對應的Android相關的lib工具包。找到對應位置,用JD-GUI打開對應的jar文件,具體看下一下。
Parsing Manifest
,的確能夠定位到具體的
ApkViewPanel
類,且包名與上面AS日誌中都能對的上,但字節碼反解成java過程當中有內部錯誤。嘗試着用用jadx打開,由於android.jar包還挺大,時間比較長,、最終雖然
ApkViewPanel
部份內容能夠顯示,但內部依然有部份內部錯誤沒法顯示,且
Parsing Manifest
不能直接顯示。
不過不要緊,咱們試着去找找源碼看看。搜索對應包名:com.android.tools.idea.apk.viewer
,選擇java類別,很快,咱們找到了對應的源碼位置。
正好,AS就是JetBrains主導的產品,Perfect!
Parsing Manifest
的位置:
setAppInfo()
方法中,將對應的控件內容從原來的Parsing Manifest
改爲了對應的包名和版本號等信息。
顯然,在代碼myNameComponent.append("Parsing Manifest");
與setAppInfo(result);
之間,程序出了問題。
apkParser
對象對應的
ApkParser
類,還有
Archives
類。繼續跟蹤
ApkParser
類,發現其主要也是一個外殼性質的類,
apkParser.constructTreeStructure()
方法主要流程來到以下所示位置:
ArchiveTreeStructure
類,仍是以前的
Archives
類,這兩個關鍵線索上的類都不是在這個項目中。根據代碼文件中的
import
導入,很快,咱們發現,線索被定向到了
com.android.tools.apk.analyzer
包中。
從包名上來看,com.android.tools.apk.analyzer
應該是Android Tools
中帶的一個工具。來到項目iml
文件,咱們發現與之相關的構件。其中,組名是:com.android.tools.apkparser
,構件名是:apkanalyzer
。
至此,咱們先總結下問題的緣由。
AS中自帶的Analyze APK,其實是經過集成了插件實現,而插件內部,又經過調用了 Android Tools中的名叫apkanalyzer
的工具實現的分析。所以,想要追溯出現問題的緣由,咱們須要再去對應追蹤下apkanalyzer
。
若是熟悉Android Tools,咱們對應去tools目錄下找找,很快便能找到apkanalyzer
。及時不熟悉,不知道目錄位置也不要緊,打不了全局搜索下。
終於,對應的工具本體出如今咱們面前。
實際上,若是對Google Developer比較熟悉,或者直接在上面搜索下,也能直接在Analyze APK
頁面上找到核心信息,直接指向工具本體—apkanalyzer
。
developer.android.com/studio/buil…
啊哈,饒了半天,原來官方文檔上直接有啊,哭了,有木有?
一樣的,經過反編譯工具查看apkanalyzer.jar
代碼終究不太方便,且內部也有很多INNER ERROR
。因而,咱們繼續去GitHub上找找。
GitHub上搜索到的apkanalyzer
相關的零零散散,好像都是我的的,不太官方,也不符合咱們的預期。怎麼辦呢?
源碼不夠,Google Source來湊!
直接Google Source搜索可能的關鍵字,立刻獲得告終果。
顯然,這正是咱們須要的。
但此時,若是直接源碼跟蹤下去,仍是有難度的。
apkanalyzer
做爲一個工具,是獨立的。在實際使用時能夠直接脫離AS環境,Google Developer官網上也有專門的篇幅進行了介紹。
developer.android.com/studio/comm…
實際使用時,咱們經過不一樣的命令行命令及參數,能夠獲得咱們指望的結果,如用來分析APK基本屬性,Manifest,dex或資源等。
由此,咱們能夠多試幾個,反正AS中Analyze APK
最終用的也是它。在必定的命令上,結果確定是同樣的。也就是說,經過命令行直接執行apkanalyzer
,確定也會有問題,但有個好處時,命令行執行每每都能拋出對應的錯誤日誌。
有了進一步的錯誤日誌提示,就有了異常棧和關鍵性的真正的錯誤緣由信息。
那咱們就試一試吧。
➜ bin apkanalyzer -h apk file-size Corn-dev-debug.apk
46.9MB
➜ bin apkanalyzer apk summary Corn-dev-debug.apk
com.corn 10300 10.3.0.0
➜ bin apkanalyzer manifest print Corn-dev-debug.apk
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="10300"
android:versionName="10.3.0.0"
package="com.mymoney"
platformBuildVersionCode="27"
platformBuildVersionName="8.1.0">
<uses-sdk
android:minSdkVersion="19"
android:targetSdkVersion="26" />
<uses-permission
android:name="android.permission.GET_ACCOUNTS"
android:maxSdkVersion="22" />
...
...
...
複製代碼
說明直接分析Manifest文件都是沒有問題的。
➜ bin apkanalyzer dex list Corn-dev-debug.apk
classes7.dex
classes6.dex
classes5.dex
classes4.dex
classes3.dex
classes2.dex
classes.dex
複製代碼
➜ bin apkanalyzer resources configs --type drawable Corn-dev-debug.apk
anydpi-v21
anydpi-v26
default
watch-v20
v21
v23
ldpi-v4
mdpi-v4
ldrtl-mdpi-v17
hdpi-v4
ldrtl-hdpi-v17
xhdpi-v4
ldrtl-xhdpi-v17
xxhdpi-v4
ldrtl-xxhdpi-v17
xxxhdpi-v4
ldrtl-xxxhdpi-v17
複製代碼
➜ bin apkanalyzer files list Corn-dev-debug.apk
Exception in thread "main" java.util.zip.ZipError: invalid END header (bad central directory offset)
at com.sun.nio.zipfs.ZipFileSystem.zerror(ZipFileSystem.java:1605)
at com.sun.nio.zipfs.ZipFileSystem.initCEN(ZipFileSystem.java:1045)
at com.sun.nio.zipfs.ZipFileSystem.<init>(ZipFileSystem.java:130)
at com.sun.nio.zipfs.ZipFileSystemProvider.newFileSystem(ZipFileSystemProvider.java:117)
at java.nio.file.FileSystems.newFileSystem(FileSystems.java:326)
at java.nio.file.FileSystems.newFileSystem(FileSystems.java:276)
at com.android.utils.FileUtils.createZipFilesystem(FileUtils.java:538)
at com.android.tools.apk.analyzer.Archives.openInnerZip(Archives.java:48)
at com.android.tools.apk.analyzer.ArchiveTreeStructure.create(ArchiveTreeStructure.java:100)
at com.android.tools.apk.analyzer.ArchiveTreeStructure.create(ArchiveTreeStructure.java:65)
at com.android.tools.apk.analyzer.ApkAnalyzerImpl.filesList(ApkAnalyzerImpl.java:803)
at com.android.tools.apk.analyzer.ApkAnalyzerCli$Action$6.execute(ApkAnalyzerCli.java:430)
at com.android.tools.apk.analyzer.ApkAnalyzerCli.run(ApkAnalyzerCli.java:163)
at com.android.tools.apk.analyzer.ApkAnalyzerCli.main(ApkAnalyzerCli.java:130)
複製代碼
終於,在用命令顯示Apk內全部文件列表的時候出現了問題。而且有對應的調用棧信息拋出。
從調用棧中咱們發現,命令行的調用方式,是經過ApkAnalyzerCli
中的main
方法去接收命令參數的。在ApkAnalyzer.jar
同級的目錄中,咱們發現了有對應的ApkAnalyzerCli.jar
,其做用,就是基於ApkAnalyzer.jar
基礎上封裝的一個Client
,以方便程序被外部調用執行,如經過命令行的方式等。
而且,忽然間發現,此處的棧信息與以前GitHub上JetBrains/android項目中分析到的源碼位置相同~!!
at com.android.tools.apk.analyzer.ArchiveTreeStructure.create(ArchiveTreeStructure.java:100)
複製代碼
看來,這就是真實的緣由所在了。
ArchiveTreeStructure
主要做用是分析apk文件中的檔案文件樹形結構,且從最終拋出的錯誤信息能夠看出:Apk包中zip文件出現的問題,zip文件頭部信息無效。
java.util.zip.ZipError: invalid END header (bad central directory offset)
複製代碼
抓住這一關鍵點,那就好辦了。直接搜索整個項目中的.zip
文件,發現還真有很多。而且存在於assets目錄下。主要存放的是一些資源。
直接解壓縮.zip文件,發現有問題,果真,此處有問題的.zip文件致使apkanalyzer在分析Apk過程當中,分析到這些.zip文件出現了問題。
.zip格式顯然是不符合.zip規範的,那麼,具體是什麼問題呢?
查找到項目使用到這些文件的代碼位置。
淚奔了,有木有?!
到如今位置,總體邏輯已經很清晰了。項目中由於存在移除了表示zip格式的頭字節的zip文件,致使在使用Android Studio Analyze APK
分析Apk時,出現程序錯誤,從而只顯示Parsing Manifest
。
究竟對不對呢,能夠以簡單方式覈驗下。
1,經過移除此類有問題的.zip文件,從新打包,發現可使用Android Studio Analyze APK
進行分析了,直接使用apkanalyzer
命令行分析時,也木有問題。
2,使用二進制修改工具,將這類有問題的.zip文件對應的四個字節的頭信息給補上。
此處推薦使用 Hex Friend 工具,能夠直接以十六進制修改對應的二進制文件內容。
504B0304
,用來表示的是對應的zip格式。
因而,咱們手動補上試試。保存後,發現.zip文件能夠經過系統工具解壓了。
替換後再次打包驗證,發現能夠Android Studio Analyze APK
能夠正常分析,apkanalyzer
能夠直接使用。
項目中之因此此處將zip文件頭四個字節去除存放,當時主要考慮是安全性問題。而後經過代碼的形式在拷貝過程當中,去補上對應的字節信息,至關於進行了修正。
如今既想不影響原有邏輯,同時又能愉快的使用Android Studio Analyze APK
進行分析,怎麼辦呢?
經過分析,咱們發現,apkanalyzer
拋出的異常,是在對zip文件進行分析的時候出現的,既然.zip文件自己,文件頭標識.zip格式的字節已經被去除,那程序是如何識別其爲.zip文件的呢,大機率是直接經過文件後綴了。發現文件後綴爲.zip,就直接按照zip文件格式進行了處理。
對應的調用棧上,咱們沒有找到Archives
類的openInnerArchive
方法,這應該是對應的jar包版本問題。不過不要緊,沿着對應的調用過程分析,咱們找到了以下的邏輯。
顯然,此處的判斷邏輯與猜想一致,直接是按照文件名稱中的格式後綴去匹配的。
那換一種思路,若是這類文件自己,不是zip後綴,程序極可能就不會執行到zip文件的分析判斷上,是否是就能夠了呢?
說幹就幹,直接將項目中此類非規範化的zip文件格式替換,例如替換成一種本身隨意想的格式,就叫.tfc
吧,而後程序中作好可能的邏輯修正。
試試。果真,再次打包,分析,OK,徹底木有問題!
其實從反面來想,若是咱們不想別人經過apkanalyzer
來分析咱們的Apk,能夠經過此類技巧,直接放一個不符合規範的zip文件在assets目錄中。例如本文中的將zip文件頭四個字節去除。
若是本身想分析本身的Apk,加對應的文件移除便可。這樣在必定程度上,防防一些開發者,提升一下安全的門檻,仍是能夠的。
是否是有點,尬?
實際上,咱們發現不少App在提升反編譯門檻時,都採起的相似的思路,經過分析對應的反編譯工具的源碼,在可能對應有邏輯漏洞的地方去設防,使得反編譯程序運行到對應位置出現錯誤,以此達到反編譯失敗目的。
如針對ApkTool反編譯工具使用的一些常見方法等。
有時候,在內部項目或不太嚴格場景下的技術實現時,直接對文件後綴進行格式校驗,也許也就能夠了。可是,在一些通用的框架、工具或有外部交互及嚴格需求場景的狀況下,對文件格式的斷定,不該該只是簡單的判斷文件格式後綴。由於文件格式後綴是很容易被任意修改的,並不具備事實上的文件格式約束力。在文件格式後綴判斷的基礎上,必要時,增長上對文件事實上的格式判斷邏輯,是頗有必要的。在必定程度上來講,本例中發現的問題,其實也偏偏是apkanalyzer
工具自身隱藏的一個bug。
apkanalyzer
是Android開發過程當中,用來分析Apk中頗有用的一個工具,常常被用到。由於其集成在AS中後,足夠簡單輕便,且能必定程度上知足咱們分析Apk的須要。
項目在不斷的迭代,維護和開發,項目中的技術問題也會不時出現,對每個技術問題,其實都是一次很好的技術歷練機會。認真分析,不斷探因,最終會終有所獲。
通常的產品開團隊,面對這類問題,每每習慣的選擇繞開,由於在相對繁忙的需求開發過程當中,無暇此顧。其實,對整個項目組來講,長期來看,這是一個嚴重的問題。這類技術性問題,就像廚房中的小強,若是發現了不一一解決之,最終會致使不斷的繁衍,終有一日,屋裏的主人,會被細菌病毒侵襲。
與其積重難返,不如時時解決之。
end~