加載so異常總結:System.loadLibrary

加載so異常總結:System.loadLibrary

 

程序引力關注

2018.12.11 23:58:08字數 1,271閱讀 2,402

安卓開發中可能會遇到加載so的場景,但可能會遇到加載異常的情況。筆者對自己遇到的此類情況稍作總結。

問題背景

有一段網絡請求的業務代碼原本是在一個應用中的,爲了實現複用,將這段業務代碼封裝成一個模塊(module),然後編譯成aar給另一個應用使用。但結果發現,原應用中這段業務代碼是正常的,但是移植到另一個應用就出現了異常。

分析思路

分析此類問題的思路就是重點考察差異點。因爲業務代碼是相同的,但是在其中一個應用正常,在另一個應用異常,則需要考慮兩個應用的差異點。最初考慮到的差異點或者可能的原因有:

  • 包名:兩個應用,最基本的不同就是包名的不同。它的不同可能會帶來異常。
  • 簽名:不同的應用可能使用不同的簽名,簽名可能會引起業務認證或者鑑權不通過。
  • 權限:應用不同,擁有的權限也不同
  • 其他方面:其他未考慮到的差異點。

最初的思路即爲這幾個點。在分析問題時可以暫時確認這幾個方向。若沒有更爲準確的思路或者靈感,最靠譜的手段還是查看日誌。

分析問題最基本的手段:查看日誌

儘管考慮了多個差異點,分析了可能的原因。但最實在的方式還是查看異常日誌,進而爲定位問題縮小範圍。通過日誌,定位到異常日誌爲:

dlopen failed: library "xx.so" needed or dlopened by "/system/lib64/libnativeloader.so" is not accessible for the namespace "classloader-namespace"

發現是加載so異常導致的網絡請求異常。

在查看日誌方面,筆者有一個小技巧,就是如果報異常或錯誤的代碼是自己工程中的,或者工程中的代碼可以捕獲異常或錯誤,可以將這些異常或錯誤改寫成對應的基類,並將其對象的信息打印出來。

例如,筆者工程裏此處代碼的錯誤類型爲:

catch(UnsatisfiedLinkError e){
    Log.e(TAG,"Load faild.");
}

對其修改,可變爲:

catch(Error e){
    Log.e(TAG,"Load faild." + e.getMessage());
    Log.e(TAG,"Load faild." + e.getCause());
}

改爲基類後,可以避免其他異常或錯誤被漏掉,並且可以將錯誤或異常信息打印出來。

加載so異常分析

從前面的日誌可以看出,是加載so的異常。但該異常似乎與包名、簽名等關係不大。通過查閱相關材料,發現可能是權限的問題,即無法加載到制定的so。若無法獲取權限,可以將目標so添加進白名單,讓其變成公有的。

在下面的幾個目錄中,可能都存在public.libraries.txt文件,這個文件中的so表示是公有的。任何應用都可以加載。

/etc/public.libraries.txt
/system/etc/public.libraries.txt
/vendor/etc/public.libraries.txt

這些不同的文件,對應了不同路徑下的so。
使用adb remout後,可以將其pull出來,將需要加載的so添加到文件的末尾,再push回去。

部分網友的辦法是將該so取出來,放到工程中,也能解決無法訪問的問題。

修改以後,發現打印的錯誤變了。實際上,如果不像上文中介紹的那樣將代碼中的錯誤類型改成基類,這裏隨着錯誤類型的更改,可能都無法查看到具體的錯誤日誌,但是業務還是異常。所以建議最好先將catch中的錯誤或異常修改成基類。

混淆導致so無法加載

由於錯誤日誌變更,查看其內容發現,so註冊失敗:

Faild to register native method xxx

到了這一步,也可以得知可能不是簽名或包名的問題,因爲最初的註冊都不成功。
仔細觀察日誌,發現其中有被混淆的代碼,爲此查看原應用中此部分代碼,發現此模塊的代碼都沒有混淆。只有另一個應用對整體使用了混淆。

但是心中不免有疑慮,編譯的aar又沒有混淆,放到新的應用中,也能被混淆嗎?
懷着試一試的態度,將新應用中的混淆關閉。最後發現業務代碼居然正常了。

爲此可以總結得到如下結論:
就算模塊編譯的aar沒有被混淆,但是被放到另一個應用中,如果不添加例外,那麼aar中的代碼依然可能被混淆,進而讓so加載時,找不到對應方法,進而失敗。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章