Android Native禁止使用系统私有库详解
系统私有库指的是,存放在android系统/system/lib/和/vendor/lib下面,但是Android NDK中没有公开API的lib库。
从Android N开始(SDK >= 24),通过dlopen
打开系统私有库,或者lib库中依赖系统私有库,都会产生异常,甚至可能导致app崩溃。具体可以阅读官方文档说明。
这个变更会有怎样的影响呢?
曾经的美好
在以前,在ndk层面,我们是可以使用一些hack的手段得到系统的私有api的。
比如,你想使用虚拟机中的一些内部符号,在N以下版本,你可以这么搞
void *handle = dlopen("libart.so", RTLD_NOW); void *originFunc = dlsym(handle, "_ZNK3art6Thread13DumpJavaStackERNSt3__113basic_ostreamIcNS1_11char_traitsIcEEEE");
这样你就能得到art::Thread::DumpJavaStack
的函数指针,然后愉快地调用它了。
晴天霹雳
但是到了N以后,
void *handle = dlopen("libart.so", RTLD_NOW); // 没问题,返回了handle指针。 void *originFunc = dlsym(handle, "_ZNK3art6Thread13DumpJavaStackERNSt3__113basic_ostreamIcNS1_11char_traitsIcEEEE"); // 失败!得到的originFunc为空!
这就很奇怪了,我们能够得到handle
指针,就说明libart.so
是找到了。但是为什么libart.so
中却没有找到art::Thread::DumpJavaStack
的符号呢?
看一下内存映射表,我们发现了一个有趣的东西
7de5d4d000-7de5d4e000 r-xp 00000000 fe:00 774 /system/fake-libs64/libart.so 7de5d4e000-7de5d4f000 r--p 00000000 fe:00 774 /system/fake-libs64/libart.so 7de5d4f000-7de5d50000 rw-p 00001000 fe:00 774 /system/fake-libs64/libart.so ... ... 7de6a04000-7de6feb000 r-xp 00000000 fe:00 1414 /system/lib64/libart.so 7de6feb000-7de6ffa000 r--p 005e6000 fe:00 1414 /system/lib64/libart.so 7de6ffa000-7de6ffd000 rw-p 005f5000 fe:00 1414 /system/lib64/libart.so
难怪,我们知道dlopen
参数为libart.so
的话,系统会先找到/system/fake-libs64/libart.so
,而不是/system/lib64/libart.so
。
而/system/fake-libs64/libart.so
又是什么鬼?从名字上看,就知道他是个假的libart。
在系统源码文件art/libart_fake/README.md
中,我们找到了对他的解释,
A fake libart made to satisfy some misbehaving apps that will attempt to link against libart.so.
这就是为了以防你们这些图谋不轨(misbehaving)的APP们做一些奇怪的事而专门设的套啊!
只要你自己的lib库依赖了libart.so
或者试图打开libart.so
,在linker查找libart.so
时,因为fake-libs路径被设置在了查找路径表的靠前处,就会先找到/system/fake-libs64/libart.so
,而不是真正的/system/lib64/libart.so
。
设置fake-libs代码:
@ frameworks/base/core/java/android/app/LoadedApk.java public static void makePaths(...) { ... // Add fake libs into the library search path if we target prior to N. if (aInfo.targetSdkVersion <= 23) { outLibPaths.add("/system/fake-libs" + (VMRuntime.is64BitAbi(aInfo.primaryCpuAbi) ? "64" : "")); } ... }
而这个/system/fake-libs64/libart.so
的内容基本上的空的(art/libart_fake/fake.cc
),所以在它里面当然什么符号都找不到啦~
霸王硬上弓
既然如此,那我们在dlopen
中直接指定lib的绝对路径总行了吧?像这样:
void *handle = dlopen("/system/lib64/libart.so", RTLD_NOW);
可是很遗憾,它报了一个错:
01-11 13:16:10.413 19869-19869/com.patch.demo E/linker: library "/system/lib64/libart.so" ("/system/lib64/libart.so") needed or dlopened by "/data/app/com.patch.demo-1/lib/arm64/libbcpatch.so" is not accessible for the namespace: [name="classloader-namespace", ld_library_paths="", default_library_paths="/data/app/com.patch.demo-1/lib/arm64:/system/fake-libs64:/data/app/com.patch.demo-1/base.apk!/lib/arm64-v8a", permitted_paths="/data:/mnt/expand:/data/data/com.patch.demo"]
也就是说,你被允许访问的路径(包含ld_library_paths、default_library_paths、permitted_paths)只有
/data/app/com.patch.demo-1/lib/arm64 /system/fake-libs64 /data/app/com.patch.demo-1/base.apk!/lib/arm64-v8a /data /mnt/expand /data/data/com.patch.demo
所以,试图访问/system/lib64/
下的libart.so
当然是不行的啦。
真是魔高一尺道高一丈啊。
至此,我们算是知道了Google封杀在ndk中访问系统私有库的方法。本质是在linker中加入一系列校验机制来做限制。linker作为最基础的lib库链接器,所有链接行为都会被限制住。
缓兵之计
不过,在Android N,你可以指定APP的sdk为API级别23或更低。那么,对于以下灰名单中的lib,仍然可以正常使用:
// TODO(dimitry): The grey-list is a workaround for http://b/26394120 --- // gradually remove libraries from this list until it is gone. static bool is_greylisted(const char* name, const soinfo* needed_by) { static const char* const kLibraryGreyList[] = { "libandroid_runtime.so", "libbinder.so", "libcrypto.so", "libcutils.so", "libexpat.so", "libgui.so", "libmedia.so", "libnativehelper.so", "libskia.so", "libssl.so", "libstagefright.so", "libsqlite.so", "libui.so", "libutils.so", "libvorbisidec.so", nullptr };
这样的话,每次使用dlopen或者链接以上lib都会打印出一个警告,然后仍然正常执行原有功能。
同时Google也声明了,在将来的版本会将这些lib的支持也一并移除。因此,这只是提供了一个让你尽快在代码中去除相关依赖的过渡期。
可见,不久的将来就无法愉快地使用系统的非公开符号了。
突出重围
那我们真的就没办法了吗?
也不是绝对的,Android限制的只是dlopen
这个途径,而我们访问内存是随心所欲的:)
方法就是,通过内存映射表找到libart.so
的真实起始位置:
7de6a04000-7de6feb000 r-xp 00000000 fe:00 1414 /system/lib64/libart.so 7de6feb000-7de6ffa000 r--p 005e6000 fe:00 1414 /system/lib64/libart.so 7de6ffa000-7de6ffd000 rw-p 005f5000 fe:00 1414 /system/lib64/libart.so
然后在加载地址起始位置手动解析libart.so
的elf格式,提取出所需符号的位置信息。相当于你自己实现linker原本的查找逻辑。
当然,这种遍历内存解析elf的实现是比较复杂的。因此,这一次Google算是封死了一大波底层hack的手段。
不过,网上仍然有很多绕过这个限制的方式,大家有兴趣的可以自己发掘一下。
也可以来这里与我们共同讨论技术
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
使用SAP OData offline库实现Android应用的离线(offline)模式
打开Android studio,在工程的build.gradle里加入下面的依赖,导入SAP OData offline库: implementation group:'com.sap.cloud.android', name:'offline-odata', version: sdkVersion 点击Sync now: 新建一个java文件:SAPServiceManager.java 使用的import如下: import android.content.Context; import android.util.Log; import android.widget.Toast; import com.sap.cloud.mobile.odata.core.AndroidSystem; import com.sap.cloud.mobile.odata.offline.OfflineODataDefiningQuery; import com.sap.cloud.mobile.odata.offline.OfflineODataException; import com.sap....
- 下一篇
2020年春招面试:全网最全Spring系列面试题129道(附答案解析)
前言关于Spring的知识总结了个思维导图分享给大家 1、不同版本的 Spring Framework 有哪些主要功能? 2、什么是 Spring Framework?Spring 是一个开源应用框架,旨在降低应用程序开发的复杂度。它是轻量级、松散耦合的。它具有分层体系结构,允许用户选择组件,同时还为 J2EE 应用程序开发提供了一个有凝聚力的框架。它可以集成其他框架,如 Structs、Hibernate、EJB 等,所以又称为框架的框架。 3、列举 Spring Framework 的优点。由于 Spring Frameworks 的分层架构,用户可以自由选择自己需要的组件。Spring Framework 支持 POJO(Plain Old Java Object) 编程,从而具备持续集成和可测试性。由于依赖注入和控制反转,JDBC 得以简化。它是开源免费的。 4、Spring Framework 有哪些不同的功能?轻量级 - Spring 在代码量和透明度方面都很轻便。IOC - 控制反转 AOP - 面向切面编程可以将应用业务逻辑和系统服务分离,以实现高内聚。容器 - Spr...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- 设置Eclipse缩进为4个空格,增强代码规范
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8编译安装MySQL8.0.19
- CentOS7,CentOS8安装Elasticsearch6.8.6
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS6,CentOS7官方镜像安装Oracle11G
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装