加载so异常总结:System.loadLibrary

加载so异常总结:System.loadLibrary

 

程序引力关注

2018.12.11 23:58:08字数 1,271阅读 2,407

安卓开发中可能会遇到加载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加载时,找不到对应方法,进而失败。

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