很久前寫的一篇文章,發出來以作紀念:)
Android中一個有趣的crash的日志分析
首先看看bugly平台中異常的統計信息,表面上是一個NullPointerException:
發生異常設備統計信息如下圖,有意思的是全部都是root過的機器:
接下來看跟蹤日志,在最下面可以看到這樣的日志,拋出了NullpointerException:
引起異常的是com.lishu.net.LishuNet$2類,從類名看顯然是某一個類的內部類。
第一個反應,當然是搜索一下應用的源代碼,看看是不是有com.lishu.net.LishuNet這樣的類,或者異常。如果有,那自然就是繼續分析代碼了。經過一番搜索,居然沒有找到,看來有點意思。
其次自然想到,應用集成了很多第三方SDK,或許是某個SDK里拋出的異常?不急,解壓出所有集成的.jar包,然后批量反編譯,導入工具中全局搜一下,有沒有這個字符串了。費一番周折發現,還是沒有這個字符串。越來越有意思了,代碼中沒有相關的類,SDK中也沒有,奇怪。
回來繼續翻看日志上下文,只有如下信息可用,看來是個http訪問超時異常:
有問題,這個異常和其他異常不太一樣,日志里居然連com.chebada包名都沒有顯示出來的。也許是統計平台出錯了,壓根不是我們應用導致的crash?
只有拋異常前打出來的一行日志,看起來有些關系
訪問這個網址看看,有時連不上,有時拒絕服務。源代碼里也沒有這樣的網址。
這個時候線索好像真的斷了,再搜索日志,看不出來和我們的應用有什么關聯。
還是沒有思路,再翻一下其他異常,發現異常列表中有些日志會打出來類似下面的內容,之后再訪問上圖中的網址(http://120.24.74.141:8080/lishu008AppManager/phone/LogicSimpleAction.action):
總結多個crash日志,發現每次都是先有這個js警告,再訪問120.24.74.141域名。
js又是/data/data目錄com.chebada包下的腳本,也許,是WebView的訪問觸發了相關異常,或許是url劫持?也不像。統計平台異常列表中有些並沒有報這個js警告,問過服務器相關同事,js也沒有訪問http://120.24.74.141:8080/lishu008AppManager這樣的網址。
不過所有crash日志都有訪問http的超時異常。
疑點都集中在http://120.24.74.141:8080/lishu008AppManager這里。
繼續萬能的百度搜索,lishu008AppManager露出一點端倪。有一個名叫“008神器”的安卓應用引起了我的注意,該下載鏈接中有lishu008AppManager字樣。打開主頁看看該應用能干什么,結果嚇一跳:
下載一個看看,運行需要root過的機器。 找了一台root過的機器,運行后按提示安裝框架、安裝模塊。期間需要重啟幾次。
需要的框架、模塊都安裝好了,再次運行,提示軟件到期。卸載重新下載一個破解版的“008神器”,運行后發現,能改的東西還真多,連手機串號也能改:
這個時候看看日志,發現了LishuNet的蹤跡:
等等,“008神器”的包名不是com.soft.apk008Tool嗎,怎么會報com.lishu.net.LishuNet錯誤呢?反編譯apk安裝包,原來,除了com.soft.apk008Tool,還有另一個包com.lishu.net這個包,其中有LishuNet這個類,LishuNet還有一個內部類com.lishu.net.LishuNet$2,繼承自Thread:
顯然在該線程中觸發了NullPointerException異常。往下跟跟,具體打印出異常的地方:
這個就是在連接http://120.24.74.141:8080失敗時,比如連接超時、服務器拒絕服務等異常情況下,打印出異常信息的地方了。原來只要連接出錯,就會打印出錯信息。
除了反編譯代碼中有com.lishu.net這個包,在 “008神器”安裝包的assets文件夾下還發現了Apk008Tool.apk這個安裝包,安裝后會多一個“008神器工具箱”的應用。再反編譯“008神器工具箱”,原來com.lishu.net是“008神器工具箱”的包名,而“008神器”中com.lishu.net包下的代碼,都是從Apk008Tool.apk來的,最有可能的就是,“008神器”和“008神器工具箱”是同一個作者開發的。
那是什么觸發了異常呢?各種嘗試,后來發現運行“008神器”,就會訪問網址http://120.24.74.141:8080/lishu008AppManager/phone/LogicSimpleAction.action,接着log中就能看到異常信息,過濾后顯示如下圖:
原來只要運行“008神器”就會報異常。繼續跟蹤com.lishu.net.LishuNet:
LishuNet對象調用postMessage()方法,在該方法中new一個LishuNet$2的對象,也就是新建了一個Thread線程,並開始運行。異常就是從這個線程里拋出來的。
那么,哪里調用了LishuNet的postMessage()方法呢?繼續搜索,原來是在Apk008Activity的onResume()中:
其他地方也有調:
稍微跟蹤一下代碼,會發現,在“008神器”啟動、啟動其他應用、網絡狀態變化、調用工具箱修改了系統屬性值時,驗證“008神器”的簽名時,等等,都會觸發這個線程,調用postMessage()方法,向http://120.24.74.141服務器發送特定數據。以啟動“008神器”應用發送的數據為例:
啟動時發送的數據是手機的IMEI、系統版本號、系統版本名稱。其他不一一列舉。
原來異常是和我們的應用沒有關系的,可以這樣驗證:卸載我們的應用,單獨運行“008神器”時,log中依然會不停的報com.lishu.net.LishuNet$2的這個異常。
Bugly中報這個異常,極有可能是因為,bugly統計平台的SDK在安卓系統log中恰巧同時檢測到了com.chebada,和com.lishu.net.LishuNet拋出的異常,就將兩者結合,視為com.chebada的bug了。這也可以解釋,為什么幾千條LishuNet$2拋出的異常,沒有統一的錯誤信息。后續有時間再反編譯看看bugly統計SDK的代碼,驗證一下。
至於解決方法,實現起來就簡單了,既然改不了框架層的東西,就在應用層修改。在應用啟動時判斷,如果存在“008神器”或“008神器工具箱”,就退出應用。OK,打完收工。
當然,如果使用008神器或者xposed框架接口,在系統服務層API屏蔽了008神器應用,那使用這種方式就不起作用了。
附:
“008神器”工作原理引起了我的好奇,各種改系統功能是怎么實現的呢?經過學習發現,是利用了xposed框架,而xposed框架是需要root權限才能正常運行的,這也解釋了為什么報這個crash的設備全部都是root過的了,沒有root權限,“008神器”就沒法正常工作。
Xposed框架是開源的,不過作者(也是Supersu的作者)好久沒有更新了,所以該框架只支持安卓4.0.3-4.4版本的系統,這個可以解釋統計平台設備版本分布情況:安卓系統4.4版本以上信息不會報這個異常。
有興趣的同學可以學習研究下這個框架,框架基本原理是修改了安卓系統的心臟zygote,等於在安卓的Java虛擬機上加了一個接口,這個接口為框架修改系統、提供系統級的服務提供了支持,當然,如果拿來搞破壞,其威力也是相當驚人的,可以在應用不知情的情況下,肆意獲取輸入的用戶名、密碼等敏感信息。下圖是我寫的框架Demo程序運行結果截圖,攔截了應用登錄頁面輸入的用戶名、密碼信息:
經本人實驗,不修改第三方應用、不經用戶同意,就可以在應用運行時動態攔截、修改應用的數據,並完全可以做到開啟后台線程,將用戶的數據發送至特定的服務器。