APK文件解析AXML-層層深入APK文件解析之一
APK解析是很久以前想完成的一件事,但是因為一些事情擱下了。
當時使用Iteedee的代碼在200多個APK文件中有將近四分之一的文件是無法成功解析AXML的。Iteedee下稱I
?因此,本文的代碼基于I的代碼修改,效果基本接近于APKTOOL(https://code.google.com/p/android-apktool/)
毋庸置疑的是,在解析的時候,會遇到各種奇葩的編碼問題,首先會遇到文件的壓縮解壓縮碼流問題,不得不說SharpZip的性能還是可以的,注意APK使用的Zip壓縮格式是非固實壓縮,雖然壓縮性能(Performance of Compression ,BenchMark)降低了,但是對于文件尋址來說還是很方便的。
常用的APK解析讀取APK信息的方法有很多種:
1、AAPT工具、2、apkTool工具、3、AXMLPrinter2工具、4、自帶ZIP解壓引擎純字節流解碼分析、5、其它...
這里要講的就是第四種,是基于開源庫SharpZipLib的。
??下面言歸正傳,談談個中遇到的一些問題:??
1、關于怎么解壓zip可以看這個問題的某層回答:
http://bbs.csdn.net/topics/390421494
注意GZip和Zip不一樣,Gzip在HTTP請求中比較常見
使用到的sharp庫到如下網站去找
http://icsharpcode.github.io/SharpZipLib/
2、zip版本問題(788):
大部分文件的解壓版本都是0x14,而個別文件出現0x0314,因此拋出了異常,具體原因無法考究,解決方案是在SharpZipLib源碼中加入代碼過濾掉,影響應該不大:
if (extractVersion==788) { extractVersion &= 0xff; }
記得備案,免得那天遇到問題了不知道這一茬。
3、文本編碼問題:
從I那兒下載的代碼經過測試后還是存在不少問題的,這里說的文本編碼就是一個典型的例子。
I使用的是UTF8編碼格式進行轉碼
通過 http://tool.chinaz.com/Tools/URLEncode.aspx 查了下"趕集"兩字的UTF8的碼流形式為:%e8%b5%b6%e9%9b%86 .
然而,把Parser之前的AndroidManifest.xml在程序里dump出來發現實際上是這樣的:
那么問題就來了,通過http://tool.chinaz.com/Tools/Unicode.aspx 查了一下unicode碼流(unicode的編碼據說沒有一個統一的標準,所以才出現了UTF8\16\32等編碼),給出的結果是:\u8d76\u96c6\u7b80\u6613\u5730\u56fe。
原因真的就找到了嗎,有時候人真的是聰明反被聰明誤啊,博主考慮了大小端的問題,于是字節換序、去00字節、重返UTF8(為什么再使用UTF8也是基于很多原因)各種方法試過皆無果之后,探究了下Unicode編碼在c#中實現的具體碼流:
byte[] bytes = Encoding.Unicode.GetBytes(textBox1.Text); for (int ii = 0; ii < bytes.Length; ii++) { textBox2.Text += string.Format("{0:x2} ", bytes[ii]); }
結果如下:
顯然字序跟AXML中的是一模一樣,注意00字節,可見AXML和c#中的unicode都使用了雙字節對齊的(這不廢話么),那么修復了編碼Bug之后的某個子函數是這樣的:
private String compXmlStringAt(byte[] arr, int strOff) { int strLen = arr[strOff + 1] << 8 & 0xff00 | arr[strOff] & 0xff; byte[] chars = new byte[strLen<<=1 ]; for (int ii = 0; ii < strLen ; ii++) { chars[ii] = arr[strOff + 2 + ii]; } return Encoding.Unicode.GetString(chars);//Change to Unicode of Encoding by Parser7 }
如此而來,就成功支持了中文/多語言了。然而讓我郁悶的是,通篇的注釋都在說Unicode,只字未提UTF8,代碼的原作者居然赫然在目地寫著錯誤的代碼,令人費解的注釋
4、TEXT事件類型的處理:
I寫的代碼,只對startTag之類的事件做了處理,然而XmlPullParser使用了五類事件,但是I對未知事件的處理是直接break了,這樣會造成manifest等element未關閉的問題,嚴重的致命/FATOL ERROR。
由于此處對TEXT內容并不關心,所以使用對offset簡單處理防止死循環的方法跳過。后面有空了再來細細研究這個東西。有需要的朋友可以一起交流下。
XmlPullParser的源碼我估計可能就在這個網站http://www.xmlpull.org/ ,找到的記得發我一份,先謝謝了。
相關資料:
http://blog.csdn.net/andyhuabing/article/details/8036340
5、size=0問題導致無法正確讀到androidmanifest.xml:
I使用了如下的方法遍歷每一個壓縮包中的文件
while ((item = zip.GetNextEntry()) != null) {...}
在某個APK的解析中發現出現size=0導致未能成功讀取androidmanifest.xml文件。
跟蹤了size讀寫器所有的過程后,發現在ZipFile構造函數中已經能夠獲得所有的ZipEntry,而I的代碼這里調用GetNextEntry方法顯然不僅是畫蛇添足而且還引入了Bug,廢話少說,使用迭代器輕松搞定:
PZipFile = new ZipFile(fsStream); foreach (ZipEntry entry in PZipFile ) { ... }
小結:
至此,已經能成功解析1021個文件的AXML,但還有35個文件不能成功解析。
經檢驗,有21個文件已經損壞,但還有14個文件可以成功打開壓縮包,具體原因還在分析中。
6、XmlException拋出的EntityName解析錯誤:并指出在35行51列
I的代碼似乎沒有實現將decompressXML之后的result轉為XMLDocument,這里是我自己加的代碼,但是卻出現這樣的問題。
dump出解析的xml內容后發現在指定位置出現了xml非法字符'&',將非法字符轉義處理就行了。
下面就是很直接、赤裸裸的代碼了:
public static string HandleInvalidCharAtXMLDoc(string filename) { return filename.Replace("<", "<").Replace(">", ">").Replace("&", "&").Replace("'", "'").Replace("\"", """); }
7、壓縮包簽名頭問題:
apktool解不開,winrar能看到目錄結構,從檔中拖拽文件貌似可以解壓AXML,但是會提示文件損壞,將解壓出來的AXML文件放入成功解析的APK后能正確解析出AXML明文內容。
經過一些實驗后,發現dump出來的字節流跟能解析出來的字節流不一樣,說明可能是entry索引出了問題,或者內容被破壞了。根據上文描述,明顯問題出在entry索引問題,或者是其它問題(不是788等版本問題)