第三方app加載系統/system/lib下的庫--is not accessible for the namespace

第三方app想load /system/lib 下的so庫。報錯:is not accessible for the namespace

緣由:android限制了app加載so庫,從 7.1.2 源碼來看,在加載so庫的時候會檢查 加載者的 權限,對於部分常用的庫比如:libssl.so libsqlite.so libutils.so libstagefright.so libmedia.so  libbinder.so libandroid_runtime.so 等,不限制。

問題: 1.0 在androidstudio 上寫了個簡單的app,直接點run 安裝到連接的設備上。(作爲一個第三方app,在 android_studio上 minSdkVersion 14; tartgetSdkVersion 28;) 

2.0 在源碼上寫jni庫, android.mk編譯出 .so  push到設備system/lib64/   (具體環境 android 7.1.2 對應api 25)

#Android.mk for make test .so, 
ROOT_DIR := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_PATH := $(ROOT_DIR)


LOCAL_MODULE := libwangtest
LOCAL_SHARED_LIBRARIES += libc

#option 表示該模塊在所有模式下 user release ,都編譯
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := test_jni.c

LOCAL_LDLIBS += -llog 

include $(BUILD_SHARED_LIBRARY)
#define MY_LOG_TAG    "from-jni" // 這個是自定義的LOG的標識
#define LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,MY_LOG_TAG,__VA_ARGS__) // 定義LOGD類型
#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO,MY_LOG_TAG,__VA_ARGS__) // 定義LOGI類型
#define LOGW(...)  __android_log_print(ANDROID_LOG_WARN,MY_LOG_TAG,__VA_ARGS__) // 定義LOGW類型
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,MY_LOG_TAG,__VA_ARGS__) // 定義LOGE類型
#define LOGF(...)  __android_log_print(ANDROID_LOG_FATAL,MY_LOG_TAG,__VA_ARGS__) // 定義LOGF類型


#define TRACK(...)  __android_log_print(ANDROID_LOG_INFO,MY_LOG_TAG,"[%d %s]",__LINE__,__FUNCTION__);__android_log_print(ANDROID_LOG_INFO,MY_LOG_TAG,__VA_ARGS__)
JNIEXPORT jint JNICALL Java_com_example_wangxiancan_as_1test_TestA_NativeHello
  (JNIEnv *env, jobject cls)
 {
	  TRACK("Natieve Hello!");
	  return 0;
 }

3.0 app上加載了對應庫,

public class TestA {
    static {
        Log.i("TestA","start loadLibrary!");
        try {
            System.loadLibrary("wangtest");
        } catch (UnsatisfiedLinkError ule) {
            Log.e("TestA", "Can't load wangtest library: " + ule);
            System.exit(1);
        } catch (SecurityException se) {
            Log.e("TestA", "Encountered a security issue when loading wangtest lib    rary: " + se);
            System.exit(1);
        }
        Log.i("TestA","loadLibrary ok !");
    }
....略
}

4.0 把編譯的libwangtest.so push 到設備上/system/lib64/  運行app,報錯

2019-09-11 09:42:39.353 21260-21260/com.example.wangxiancan.as_test I/TestA: start loadLibrary!

2019-09-11 09:42:39.355 21260-21260/com.example.wangxiancan.as_test E/linker: library "/system/lib64/libwangtest.so" ("/system/lib64/libwangtest.so") needed or dlopened by "/system/lib64/libnativeloader.so" is not accessible for the namespace: [name="classloader-namespace", ld_library_paths="", default_library_paths="", permitted_paths="/data:/mnt/expand:/data/data/com.example.wangxiancan.as_test"]

2019-09-11 09:42:39.355 21260-21260/com.example.wangxiancan.as_test E/TestA: Can't load wangtest library: java.lang.UnsatisfiedLinkError: dlopen failed: library "/system/lib64/libwangtest.so" needed or dlopened by "/system/lib64/libnativeloader.so" is not accessible for the namespace "classloader-namespace"

解決辦法:

  1. 把需要的庫拷貝到 app的打包目錄一起打包到app。具體哪一個目錄,找個demo看看,還需注意 abi,不同的cpu架構都有不同的目錄名稱,arm64位,arm32位,還是x86, 或者mips. 如果確定只一種設備用,那麼只放一個就行。
  2. app安裝好後手動把需要的庫push到app私有目錄的lib下面。(方法1 ,在安裝app時會自動解壓,這裏算是安裝完之後手動拷貝)

具體的操作,還是結合實際的錯誤提示來分析,貼個自己的調試過程:

現在在第三方app加載剛編譯的so庫,該so庫已經push到 /system/lib64/目錄下。
運行app:
錯誤 1
2019-09-11 09:42:39.353 21260-21260/com.example.wangxiancan.as_test I/TestA: start loadLibrary!
2019-09-11 09:42:39.355 21260-21260/com.example.wangxiancan.as_test E/linker: library "/system/lib64/libwangtest.so" ("/system/lib64/libwangtest.so") needed or dlopened by "/system/lib64/libnativeloader.so" is not accessible for the namespace: [name="classloader-namespace", ld_library_paths="", default_library_paths="", permitted_paths="/data:/mnt/expand:/data/data/com.example.wangxiancan.as_test"]
2019-09-11 09:42:39.355 21260-21260/com.example.wangxiancan.as_test E/TestA: Can't load wangtest library: java.lang.UnsatisfiedLinkError: dlopen failed: library "/system/lib64/libwangtest.so" needed or dlopened by "/system/lib64/libnativeloader.so" is not accessible for the namespace "classloader-namespace"
	
"/system/lib64/libnativeloader.so" is not accessible for the namespace: 無法訪問。。。據說是android7 限制了普通app加載系統私有庫(一些共用的庫還是可以)
解決方法:既然無法加載system目錄下的庫,就把這個庫拷貝到app目錄下吧,(拷貝後爲實驗的準確性還是把system/lib目錄下的那個庫刪掉確保只有一個吧。不然可能還是去system/lib下找到了這個庫,結果加載不成功,不會再去app目錄找了,關於這個查找的順序,應該是先app私有目錄再system目錄的)既然有錯誤提示,不妨利用一下,直接把/system/lib64 下剛剛報錯的庫刪除,這樣在運行就會報錯提示找不到這個庫。
錯誤2
019-09-11 10:29:14.621 6205-6205/? I/TestA: start loadLibrary!
2019-09-11 10:29:14.622 6205-6205/? E/TestA: Can't load wangtest library: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.wangxiancan.as_test-1/base.apk"],nativeLibraryDirectories=[/data/app/com.example.wangxiancan.as_test-1/lib/arm64, /system/lib64, /vendor/lib64]]] couldn't find "libwangtest.so"
提示查找的路徑是 app私有目錄下的 lib/arm64/
哪我們就把測試用的so拷貝到這個目錄。(沒有arm64目錄就自己mkdir 一個)

再運行:
錯誤3
019-09-11 10:30:41.310 7529-7529/com.example.wangxiancan.as_test I/TestA: start loadLibrary!
2019-09-11 10:30:41.312 7529-7529/com.example.wangxiancan.as_test E/TestA: Can't load wangtest library: java.lang.UnsatisfiedLinkError: dlopen failed: library "libc++.so" not found
還需要libc++? 可以在設備上看到 /system/lib64/下面是存在libc++.so的,既然又報錯找不到,也一併把這個/system/lib64/libc++.so 拷貝一份到 app私有目錄 */lib/arm64/

再運行:成功加載並調用
2019-09-11 10:32:29.630 8109-8109/? I/TestA: start loadLibrary!
2019-09-11 10:32:29.634 8109-8109/? I/TestA: loadLibrary ok !
2019-09-11 10:32:29.635 8109-8109/? I/from-jni: [19 Java_com_example_wangxiancan_as_1test_TestA_NativeHello]
2019-09-11 10:32:29.635 8109-8109/? I/from-jni: Natieve Hello!

繼續分析爲何限制?如何限制?看android源碼。源碼工程中查找 is not accessible for the namespace,在/bionic/linker.cpp 中有加載庫的函數。加載的時候先判斷對應庫是否存在,然後檢查“限制條件”

if (!ns->is_accessible(realpath)) {//判斷是否可以訪問,文件是否存在
    // TODO(dimitry): workaround for http://b/26394120 - the grey-list
    const soinfo* needed_by = task->is_dt_needed() ? task->get_needed_by() : nullptr;
    if (is_greylisted(name, needed_by)) {//這裏有一個灰色表,表裏面列舉了部分庫文件,如果
//要加載的庫是在這個表裏面或者加載庫的是"系統"用戶,就通過檢查,否則拒絕加載,被限制。
      // print warning only if needed by non-system library
      if (needed_by == nullptr || !is_system_library(needed_by->get_realpath())) {
        const soinfo* needed_or_dlopened_by = task->get_needed_by();
        const char* sopath = needed_or_dlopened_by == nullptr ? "(unknown)" :
                                                      needed_or_dlopened_by->get_realpath();
        DL_WARN("library \"%s\" (\"%s\") needed or dlopened by \"%s\" is not accessible for the namespace \"%s\""
                " - the access is temporarily granted as a workaround for http://b/26394120, note that the access"
                " will be removed in future releases of Android.",
                name, realpath.c_str(), sopath, ns->get_name());
        add_dlwarning(sopath, "unauthorized access to",  name);
      }
    } else {
      // do not load libraries if they are not accessible for the specified namespace.
      const char* needed_or_dlopened_by = task->get_needed_by() == nullptr ?
                                          "(unknown)" :
                                          task->get_needed_by()->get_realpath();

      DL_ERR("library \"%s\" needed or dlopened by \"%s\" is not accessible for the namespace \"%s\"",
             name, needed_or_dlopened_by, ns->get_name());

      PRINT("library \"%s\" (\"%s\") needed or dlopened by \"%s\" is not accessible for the"
            " namespace: [name=\"%s\", ld_library_paths=\"%s\", default_library_paths=\"%s\","
            " permitted_paths=\"%s\"]",
            name, realpath.c_str(),
            needed_or_dlopened_by,
            ns->get_name(),
            android::base::Join(ns->get_ld_library_paths(), ':').c_str(),
            android::base::Join(ns->get_default_library_paths(), ':').c_str(),
            android::base::Join(ns->get_permitted_paths(), ':').c_str());
      return false;
    }
  }

所以這個函數 is_greylisted() 是關鍵的條件,其中就

列舉出了可以被第三方app加載的 /system/ *.so  

// 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
  };

  // limit greylisting to apps targeting sdk version 23 and below
  if (get_application_target_sdk_version() > 23) {
    return false;
  }

  // if the library needed by a system library - implicitly assume it
  // is greylisted

  if (needed_by != nullptr && is_system_library(needed_by->get_realpath())) {
    return true;
  }

  // if this is an absolute path - make sure it points to /system/lib(64)
  if (name[0] == '/' && dirname(name) == kSystemLibDir) {
    // and reduce the path to basename
    name = basename(name);
  }

  for (size_t i = 0; kLibraryGreyList[i] != nullptr; ++i) {
    if (strcmp(name, kLibraryGreyList[i]) == 0) {
      return true;
    }
  }

  return false;
}

 

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