首页 > 编程语言 >android链接器命名空间源码分析

android链接器命名空间源码分析

时间:2023-01-02 04:22:05浏览次数:38  
标签:name namespace library find 源码 so android ns 链接

二代壳加壳apk在实现函数抽取的时候需要选择合适的时机进行抽取代码的回填,一般选择在LoadMethod函数中即函数被加载前。所以需要通过inline hook libart.so的LoadMethod函数,前提是需要得到libart.so的模块基地址并得到LoadMethod函数的地址。如果直接在native中通过dlopen获取libart.so的handle是会报错的,而如果获取libc.so的handle是会成功的。获取libart.so错误的原因就是因为libart.so是系统私有库,而且没有在 /system/etc/public.libraries.txt/vendor/etc/public.libraries.txt 文件中声明。通过设置 adb shell setprop debug.ld.all dlerror,dlopen打开linker链接器调试日志查看报错信息。

链接器命名空间源码分析

这里apk是通过System.loadLibrary加载了一个so库libnamespacetest.so,并在so库中调用dlopen获取libc.so和libart.so的handle,无论是dlopen还是android_dlopen_ext最后都是调用do_dlopen函数,所以从do_dlopen开始分析。

do_dlopen

  • 调用find_containing_library根据solist获取dlopen调用地址对应的so文件的soinfo指针,这里就是libnamespacetest.so的soinfo指针。
  • 调用get_caller_namespace获取调用者对应的命名空间,根据之前的分析libnamespacetest.so是由apk通过System.loadLibrary加载的,所以其对应的命名空间就是apk默认类加载器映射的命名空间。
  • 调用find_library寻找待加载so文件的soinfo指针,如果找到了就获取其对应的handle信息,否则就返回失败。这里还可以得到一个信息,android早期版本中dlopen加载so文件返回的handle就是so文件对应的soinfo结构体指针,现在获取的handle并不是简单的soinfo结构体指针
//bionic/linker/linker.cpp
void* do_dlopen(const char* name, int flags,
                const android_dlextinfo* extinfo,
                const void* caller_addr) {
  std::string trace_prefix = std::string("dlopen: ") + (name == nullptr ? "(nullptr)" : name);
  ScopedTrace trace(trace_prefix.c_str());
  ScopedTrace loading_trace((trace_prefix + " - loading and linking").c_str());
  //获取caller_addr调用地址对应的so文件的soinfo指针
  soinfo* const caller = find_containing_library(caller_addr);    
  //获取caller调用者的命名空间
  android_namespace_t* ns = get_caller_namespace(caller);         

  LD_LOG(kLogDlopen,
         "dlopen(name=\"%s\", flags=0x%x, extinfo=%s, caller=\"%s\", caller_ns=%s@%p, targetSdkVersion=%i) ...",
         name,
         flags,
         android_dlextinfo_to_string(extinfo).c_str(),
         caller == nullptr ? "(null)" : caller->get_realpath(),
         ns == nullptr ? "(null)" : ns->get_name(),
         ns,
         get_application_target_sdk_version());

 .....
 //得到此so文件对应的soinfo指针
 soinfo* si = find_library(ns, translated_name, flags, extinfo, caller);
  loading_trace.End();

  if (si != nullptr) {
    //通过soinfo指针得到对应的handle
    void* handle = si->to_handle();
    LD_LOG(kLogDlopen,
           "... dlopen calling constructors: realpath=\"%s\", soname=\"%s\", handle=%p",
           si->get_realpath(), si->get_soname(), handle);
    si->call_constructors();
    failure_guard.Disable();
    LD_LOG(kLogDlopen,
           "... dlopen successful: realpath=\"%s\", soname=\"%s\", handle=%p",
           si->get_realpath(), si->get_soname(), handle);
    return handle;
  }

  return nullptr;
}

find_library_internal

find_library-->find_libraries-->find_library_internal,直接分析find_library_internal函数。

  • 调用find_loaded_library_by_soname()在caller命名空间和链接的命名空间中查找库是否已经加载。
  • 如果上一步未找到就调用load_library()尝试在caller命名空间中加载库。
  • 如果前两步都没成功就尝试调用find_library_in_linked_namespace()在caller命名空间的所有链接命名空间中搜索库,并尝试调用load_library在链接命名空间中加载库。
static bool find_library_internal(android_namespace_t* ns,
                                  LoadTask* task,
                                  ZipArchiveCache* zip_archive_cache,
                                  LoadTaskList* load_tasks,
                                  int rtld_flags) {
  //在caller命名空间和链接的命名空间中查找库是否已经加载
  soinfo* candidate;
  if (find_loaded_library_by_soname(ns, task->get_name(), true /* search_linked_namespaces */,
                                    &candidate)) {
    LD_LOG(kLogDlopen,
           "find_library_internal(ns=%s, task=%s): Already loaded (by soname): %s",
           ns->get_name(), task->get_name(), candidate->get_realpath());
    task->set_soinfo(candidate);
    return true;
  }

  //如果上一步未找到就调用load_library尝试在caller命名空间中加载库
  if (load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags,
                   true /* search_linked_namespaces */)) {
    return true;
  }

  //如果前两步都没成功就尝试在caller命名空间的所有链接命名空间中搜索库并尝试在链接命名空间中加载
  // if a library was not found - look into linked namespaces
  // preserve current dlerror in the case it fails.
  DlErrorRestorer dlerror_restorer;
  LD_LOG(kLogDlopen, "find_library_internal(ns=%s, task=%s): Trying %zu linked namespaces",
         ns->get_name(), task->get_name(), ns->linked_namespaces().size());
  for (auto& linked_namespace : ns->linked_namespaces()) {
    if (find_library_in_linked_namespace(linked_namespace, task)) {
      // Library is already loaded.
      if (task->get_soinfo() != nullptr) {
        return true;
      }

      if (load_library(linked_namespace.linked_namespace(), task, zip_archive_cache, load_tasks,
                       rtld_flags, false /* search_linked_namespaces */)) {
        LD_LOG(kLogDlopen, "find_library_internal(ns=%s, task=%s): Found in linked namespace %s",
               ns->get_name(), task->get_name(), linked_namespace.linked_namespace()->get_name());
        return true;
      }
    }
  }

  return false;
}

find_loaded_library_by_soname

  • 首先会判断加载的库名称(也就是dlopen传入的so名称)是否为绝对路径,如果是直接返回false。
  • 如果不是绝对路径就调用find_loaded_library_by_soname()在caller命名空间已经加载的so库列表中找是否有待加载的so库。
  • 如果上一步没找到就遍历caller命名空间所有链接的命名空间,并调用 android_namespace_link_t::is_accessible()进行权限判断。判断失败直接返回false,成功就调用find_loaded_library_by_soname在链接命名空间已经加载的so库列表中找是否有待加载的so库。
/ Returns true if library was found and false otherwise
static bool find_loaded_library_by_soname(android_namespace_t* ns,
                                         const char* name,
                                         bool search_linked_namespaces,
                                         soinfo** candidate) {
  *candidate = nullptr;
  //如果是绝对路径就直接返回false
  if (strchr(name, '/') != nullptr) {
    return false;
  }
  //在caller命名空间已经加载的so库列表中找是否有待加载的so库
  bool found = find_loaded_library_by_soname(ns, name, candidate);
  //如果没找到就遍历caller命名空间所有链接命名空间
  if (!found && search_linked_namespaces) {
    // if a library was not found - look into linked namespaces
    for (auto& link : ns->linked_namespaces()) {
      //首先判断权限
      if (!link.is_accessible(name)) {
        continue;
      }

      android_namespace_t* linked_ns = link.linked_namespace();
      //权限判断通过就在链接命名空间中已经加载的so库列表中找是否有待加载的so库
      if (find_loaded_library_by_soname(linked_ns, name, candidate)) {
        return true;
      }
    }
  }
  return found;
}

android_namespace_link_t::is_accessible()进行权限判断。

  • 先判断此链接的allow_all_shared_libs_ 是否为true,如果为true说明当前命名空间中无法加载的so库都可以在链接命名空间中搜索加载。
  • 如果allow_all_shared_libs_为false就需要进一步判断待加载的so库是否在此链接命名空间的shared_lib_sonames_列表中。
bool is_accessible(const char* soname) const {
  return allow_all_shared_libs_ || shared_lib_sonames_.find(soname) != shared_lib_sonames_.end();
}

load_library

static bool load_library(android_namespace_t* ns,
                         LoadTask* task,
                         ZipArchiveCache* zip_archive_cache,
                         LoadTaskList* load_tasks,
                         int rtld_flags,
                         bool search_linked_namespaces) {
  const char* name = task->get_name();
  soinfo* needed_by = task->get_needed_by();
  const android_dlextinfo* extinfo = task->get_extinfo();

  off64_t file_offset;
  std::string realpath;
  //判断extinfo是否为空,只有从java层调用System.loadLibrary来到者才extinfo才不为空
  if (extinfo != nullptr && (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) != 0) {
    file_offset = 0;
    if ((extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET) != 0) {
      file_offset = extinfo->library_fd_offset;
    }

    if (!realpath_fd(extinfo->library_fd, &realpath)) {
      if (!is_first_stage_init()) {
        PRINT(
            "warning: unable to get realpath for the library \"%s\" by extinfo->library_fd. "
            "Will use given name.",
            name);
      }
      realpath = name;
    }

    task->set_fd(extinfo->library_fd, false);
    task->set_file_offset(file_offset);
    return load_library(ns, task, load_tasks, rtld_flags, realpath, search_linked_namespaces);
  }
  //对于直接native调用dlopen而言先调用open_library把so文件载入内存。
  // Open the file.
  int fd = open_library(ns, zip_archive_cache, name, needed_by, &file_offset, &realpath);
  if (fd == -1) {
    DL_ERR("library \"%s\" not found", name);
    return false;
  }

  task->set_fd(fd, true);
  task->set_file_offset(file_offset);
  //调用重载的load_library加载so文件获取soinfo结构
  return load_library(ns, task, load_tasks, rtld_flags, realpath, search_linked_namespaces);
}

open_library会尝试从磁盘中加载so文件

  • 判断是否为绝对路径,是的话直接打开。
  • 尝试从LD_LIBRARY_PATH设置的路径中打开文件(ld_library_paths)
  • 尝试从default_library_paths中加载
static int open_library(android_namespace_t* ns,
                        ZipArchiveCache* zip_archive_cache,
                        const char* name, soinfo *needed_by,
                        off64_t* file_offset, std::string* realpath) {
  TRACE("[ opening %s from namespace %s ]", name, ns->get_name());
  //如果是绝对路径直接打开文件
  if (strchr(name, '/') != nullptr) {
    int fd = -1;

    if (strstr(name, kZipFileSeparator) != nullptr) {
      fd = open_library_in_zipfile(zip_archive_cache, name, file_offset, realpath);
    }

    if (fd == -1) {
      fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_CLOEXEC));
      if (fd != -1) {
        *file_offset = 0;
        if (!realpath_fd(fd, realpath)) {
          if (!is_first_stage_init()) {
            PRINT("warning: unable to get realpath for the library \"%s\". Will use given path.",
                  name);
          }
          *realpath = name;
        }
      }
    }

    return fd;
  }
  //尝试从LD_LIBRARY_PATH设置的路径中打开(ld_library_paths)
  // Otherwise we try LD_LIBRARY_PATH first, and fall back to the default library path
  int fd = open_library_on_paths(zip_archive_cache, name, file_offset, ns->get_ld_library_paths(), realpath);
  if (fd == -1 && needed_by != nullptr) {
    fd = open_library_on_paths(zip_archive_cache, name, file_offset, needed_by->get_dt_runpath(), realpath);
    // Check if the library is accessible
    if (fd != -1 && !ns->is_accessible(*realpath)) {
      close(fd);
      fd = -1;
    }
  }

  ......
  //尝试从default_library_paths中加载
  if (fd == -1) {
    fd = open_library_on_paths(zip_archive_cache, name, file_offset, ns->get_default_library_paths(), realpath);
  }
  return fd;
}

open_library如果成功加载文件后就调用重载的load_library。

  • 调用is_accessible进行权限检查,检查通过直接调用soinfo_alloc为so文件申请soinfo结构体。
  • is_accessible权限检查未通过调用is_greylisted判断是否在灰名单中,如果在就调用soinfo_alloc为so文件申请soinfo结构体。
  • is_accessible权限检查未通过,也不在灰名单中则此caller命名空间无权限加载此so文件,打印错误日志。(我们的apk就是在这出错的)
static bool load_library(android_namespace_t* ns,
                         LoadTask* task,
                         LoadTaskList* load_tasks,
                         int rtld_flags,
                         const std::string& realpath,
                         bool search_linked_namespaces) {
  off64_t file_offset = task->get_file_offset();
  const char* name = task->get_name();
  const android_dlextinfo* extinfo = task->get_extinfo();

  LD_LOG(kLogDlopen, "load_library(ns=%s, task=%s, flags=0x%x, realpath=%s)", ns->get_name(), name,
         rtld_flags, realpath.c_str());

  ...
  //文件不在临时文件系统,is_accessible权限检查没有通过
  if ((fs_stat.f_type != TMPFS_MAGIC) && (!ns->is_accessible(realpath))) {
   
    // TODO(dimitry) before O release: add a namespace attribute to have this enabled
    // only for classloader-namespaces
    const soinfo* needed_by = task->is_dt_needed() ? task->get_needed_by() : nullptr;
    //是否在灰名单中
    if (is_greylisted(ns, 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_documented_change(__ANDROID_API_N__,
                                  "private-api-enforced-for-api-level-24",
                                  "library \"%s\" (\"%s\") needed or dlopened by \"%s\" "
                                  "is not accessible by namespace \"%s\"",
                                  name, realpath.c_str(), sopath, ns->get_name());
        add_dlwarning(sopath, "unauthorized access to",  name);
      }
    } else {
      //如果权限检查未通过,也不在灰名单中则证明当前caller命名空间无权限加载此库文件。(app报错就是在此报错的)
      // 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());

      // do not print this if a library is in the list of shared libraries for linked namespaces
      if (!maybe_accessible_via_namespace_links(ns, 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;
    }
  }
  //如果上述检查都通过就为加载的so文件申请一个soinfo结构
  soinfo* si = soinfo_alloc(ns, realpath.c_str(), &file_stat, file_offset, rtld_flags);
  if (si == nullptr) {
    return false;
  }

  task->set_soinfo(si);
  ......
  return true;
}

is_accessible进行权限检查,依次进行如下检查,有一项检查通过则直接返回true。

  • 判断命名空间的is_isolated_是否为true,如果不是则证明不是严格隔离,权限检查通过。(这就是通常说的如果命名空间不是严格隔离的则可以加载任意绝对路径的库文件的原理)
  • 判断是否在白名单中
  • 判断是否在ld_library_paths中(LD_LIBRARY_PATH设置)
  • 判断是否在default_library_paths中
  • 判断是否在特权路径permitted_paths中
//bionic/linker/linker_namespaces.cpp
bool android_namespace_t::is_accessible(const std::string& file) {
  //判断命名空间的is_isolated_,即是否为严格隔离,如果不是则权限检查通过。
  if (!is_isolated_) {
    return true;
  }
  //判断是否在白名单中
  if (!whitelisted_libs_.empty()) {
    const char *lib_name = basename(file.c_str());
    if (std::find(whitelisted_libs_.begin(), whitelisted_libs_.end(),
                  lib_name) == whitelisted_libs_.end()) {
      return false;
    }
  }
  //判断是否在ld_library_paths中(LD_LIBRARY_PATH设置)
  for (const auto& dir : ld_library_paths_) {
    if (file_is_in_dir(file, dir)) {
      return true;
    }
  }
  //判断是否在default_library_paths中
  for (const auto& dir : default_library_paths_) {
    if (file_is_in_dir(file, dir)) {
      return true;
    }
  }
  //判断是否在特权路径permitted_paths中
  for (const auto& dir : permitted_paths_) {
    if (file_is_under_dir(file, dir)) {
      return true;
    }
  }
  return false;
}

find_library_in_linked_namespace

如果第一步和第二步都失败了就遍历caller命名空间的链接命名空间并调用find_library_in_linked_namespace。

  • 调用find_loaded_library_by_soname在链接命名空间中已经加载的so库列表中查找是否存在待加载so文件,如果时绝对路径直接返回false。(这此传入false所以不会调用链接命名空间的链接命名空间,这也是链接命名空间不具有传递性的原理)
  • 无论找没找到都调用is_accessible权限检查(检查 链接命名空间的allow_all_shared_libs_是否为True或者so文件是否在链接命名空间的共享库列表shared_lib_sonames_中。)
static bool find_library_in_linked_namespace(const android_namespace_link_t& namespace_link,
                                             LoadTask* task) {
  android_namespace_t* ns = namespace_link.linked_namespace();
  soinfo* candidate;
  bool loaded = false;
  std::string soname;
  //检查so库在链接命名空间中是否加载,传入false不会去检查链接命名空间的链接命名空间(这也是链接命名空间不具有传递性的原理)
  if (find_loaded_library_by_soname(ns, task->get_name(), false, &candidate)) {
    loaded = true;
    soname = candidate->get_soname();
  } else {
    soname = resolve_soname(task->get_name());
  }
  //进行权限检查
  if (!namespace_link.is_accessible(soname.c_str())) {
    // the library is not accessible via namespace_link
    LD_LOG(kLogDlopen,
           "find_library_in_linked_namespace(ns=%s, task=%s): Not accessible (soname=%s)",
           ns->get_name(), task->get_name(), soname.c_str());
    return false;
  }
  //如果已经加载就返回
  // if library is already loaded - return it
  if (loaded) {
    LD_LOG(kLogDlopen, "find_library_in_linked_namespace(ns=%s, task=%s): Already loaded",
           ns->get_name(), task->get_name());
    task->set_soinfo(candidate);
    return true;
  }

  // returning true with empty soinfo means that the library is okay to be
  // loaded in the namespace but has not yet been loaded there before.
  LD_LOG(kLogDlopen, "find_library_in_linked_namespace(ns=%s, task=%s): Ok to load", ns->get_name(),
         task->get_name());
  task->set_soinfo(nullptr);
  return true;
}

总结

再次查看apk加载libc.so和libart.so的日志信息。

  • dlopen打开libc.so会成功,调用流程为dlopen->find_libraries->find_library_internal,然后调用find_loaded_library_by_soname可以通过权限检查(libc.so在public.libraries.txt中)并在caller命名空间链接的defult命名空间中找到libc.so,返回找到的soinfo结构指针并返回对应的handle。

  • dlopen打开/apex/com.android.runtime/lib/libart.so会失败,调用流程为dlopen->find_libraries->find_library_internal,然后第一步调用find_loaded_library_by_soname因为是绝对路径直接返回false。第二步调用load_library->open_library,因为是绝对路径所以直接打开文件,然后判断权限没通过又不在灰名单中所以无权限打印错误日志。第三步调用find_library_in_linked_namespace->find_loaded_library_by_soname,因为是绝对路径所以直接返回进行权限检查失败打印日志。(这里因为find_library_in_linked_namespace->find_loaded_library_by_soname找没找到都会返回并进行权限检查,所以即便传入的是libart.so也不行,权限还是通过不了)

14:50:53.713  D  dlopen(name="libc.so", flags=0x0, extinfo=(null), caller="/data/app/com.reverccqin.namespacetest-pJvDUzHPZs4h76jUcSOgZA==/lib/arm/libnamespacetest.so", caller_ns=classloader-namespace@0xed7da330, targetSdkVersion=32) ...
14:50:53.714  D  find_libraries(ns=classloader-namespace): task=libc.so, is_dt_needed=0
14:50:53.714  D  find_library_internal(ns=classloader-namespace, task=libc.so): Already loaded (by soname): /apex/com.android.runtime/lib/bionic/libc.so
14:50:53.714  D  ... dlopen calling constructors: realpath="/apex/com.android.runtime/lib/bionic/libc.so", soname="libc.so", handle=0x22165f21
14:50:53.714  D  ... dlopen successful: realpath="/apex/com.android.runtime/lib/bionic/libc.so", soname="libc.so", handle=0x22165f21
14:51:09.223  D  dlopen(name="/apex/com.android.runtime/lib/libart.so", flags=0x0, extinfo=(null), caller="/data/app/com.reverccqin.namespacetest-pJvDUzHPZs4h76jUcSOgZA==/lib/arm/libnamespacetest.so", caller_ns=classloader-namespace@0xed7da330, targetSdkVersion=32) ...
14:51:09.223  D  find_libraries(ns=classloader-namespace): task=/apex/com.android.runtime/lib/libart.so, is_dt_needed=0
14:51:09.224  D  load_library(ns=classloader-namespace, task=/apex/com.android.runtime/lib/libart.so, flags=0x0, realpath=/apex/com.android.runtime/lib/libart.so)
14:51:09.224  E  library "/apex/com.android.runtime/lib/libart.so" ("/apex/com.android.runtime/lib/libart.so") needed or dlopened by "/data/app/com.reverccqin.namespacetest-pJvDUzHPZs4h76jUcSOgZA==/lib/arm/libnamespacetest.so" is not accessible for the namespace: [name="classloader-namespace", ld_library_paths="", default_library_paths="/data/app/com.reverccqin.namespacetest-pJvDUzHPZs4h76jUcSOgZA==/lib/arm:/data/app/com.reverccqin.namespacetest-pJvDUzHPZs4h76jUcSOgZA==/base.apk!/lib/armeabi-v7a", permitted_paths="/data:/mnt/expand:/data/data/com.reverccqin.namespacetest"]
14:51:09.224  D  find_library_internal(ns=classloader-namespace, task=/apex/com.android.runtime/lib/libart.so): Trying 3 linked namespaces
14:51:09.224  D  find_library_in_linked_namespace(ns=(default), task=/apex/com.android.runtime/lib/libart.so): Not accessible (soname=libart.so)
14:51:09.224  D  find_library_in_linked_namespace(ns=runtime, task=/apex/com.android.runtime/lib/libart.so): Not accessible (soname=libart.so)
14:51:09.224  D  find_library_in_linked_namespace(ns=sphal, task=/apex/com.android.runtime/lib/libart.so): Not accessible (soname=libart.so)
14:51:09.224  D  ... dlopen failed: library "/apex/com.android.runtime/lib/libart.so" needed or dlopened by "/data/app/com.reverccqin.namespacetest-pJvDUzHPZs4h76jUcSOgZA==/lib/arm/libnamespacetest.so" is not accessible for the namespace "classloader-namespace"
14:51:09.224  D  dlerror set to "dlopen failed: library "/apex/com.android.runtime/lib/libart.so" needed or dlopened by "/data/app/com.reverccqin.namespacetest-pJvDUzHPZs4h76jUcSOgZA==/lib/arm/libnamespacetest.so" is not accessible for the namespace "classloader-namespace""

整个调用链判断流程如下

dlopen()
        __loader_dlopen()
	find_containing_library(call_addr) 获取caller的soinfo结构(通过地址查询solist链表)
	get_caller_namepace(caller) 过去caller的命名空间
	find_library 获取soinfo结构体地址
		find_libraries()
		        	find_library_internal
				①. find_loaded_library_by_soname() 在caller命名空间和链接的命名空间中查找库是否已经加载
					判断路径,全路径直接返回false。
					先在caller的命名空间对应的so文件列表中找是否已经加载了此so文件是否已经加载,找到了就返回对应soinfo指针
					没找到再继续在所有链接的命名空间中查找此so文件是否加载,需要先调用is_accessible进行权限检查(检查 链接命名空间的allow_all_shared_libs_是否为True或者so文件是否在链接命名空间的共享库列表shared_lib_sonames_中。
					如果上一步的is_accessible权限检查通过就在链接命名空间对应的so文件列表中找此so文件是否已经加载,如果找到了就返回对应的soinfo指针。
 				②. load_library 如果没有在caller命名空间和链接的命名空间已经加载的so库列表中没有找到so文件就继续调用load_library,传入的命名空间是caller的,最后一个参数为true表示搜索链接命名空间。
					if(extinfo != NULL) load_library 如果是从java层调用的则extinfo不为空,此so文件已经被加载到内存了直接调用重载的load_library
					if(extinfo == NULL) open_library 
						如果是绝对路径直接打开文件。(所以再直接使用dlopen传入绝对路径是可以过掉第一层检测的)
						否则尝试从ld_library_path和default library_path路径中查找
						如果都没找到就会返回失败
					if(extinfo == NULL) load_library 如果是直接在native层调用的dlopen或者android_dlopen就需要先调用open_library加载此文件成功后,再调用重载的load_library
						先调用android_namespace_t::is_accessible判断此so文件是否在caller命名空间的访问路径中。(先判断此命名空间的统一访问控制权限is_isolated(可直接通过),再判断是否再白名单whitelisted,ld_library, default_library, permitted_path中
						android_namespace_t::is_accessible权限判断通过就调用soinfo_alloc申请soinfo结构体,并返回。
						android_namespace_t::is_accessible权限判断失败就继续判断是否再greylist灰名单也会调用soinfo_alloc申请soinfo结构体。(现在已经失效)
						android_namespace_t::is_accessible权限判断失败也不在greylist灰名单中就返回失败 无权限(我们就是在这错误的)
				③. 前两步都失败就去遍历caller命名空间所有的链接命名空间,然后以链接命名空间作为参数调用 find_library_in_linked_namespace() 
					find_loaded_library_by_soname在链接的命名空间中查找库是否已经加载,绝对路径直接返回。这次不会去查找链接命名空间自己的链接命名空间。
					找没找到都调用is_accessible权限检查(检查 链接命名空间的allow_all_shared_libs_是否为True或者so文件是否在链接命名空间的共享库列表shared_lib_sonames_中。(我们在此也错误了)
				如果find_library_in_linked_namespace成功但是so文件还没加载,调用load_library进行加载。

标签:name,namespace,library,find,源码,so,android,ns,链接
From: https://www.cnblogs.com/revercc/p/17019348.html

相关文章

  • android的链接器命名空间
    android7.0加入了对私有系统so库API调用的限制,android8.0引入了projectTreble实现框架与供应商解耦合,这都需要依赖链接器命名空间机制。这里主要关注的重点是链接器命名......
  • Android笔记--案例:登录界面以及登录逻辑
    登录界面的实现就是说,界面的绘制,并没有什么难度,只要控制好空间的分配就可以了登录的逻辑实现获取验证码、忘记密码的界面跳转、登录的实现:确认文本框的输入内......
  • Android笔记--案例:找回密码
    找回密码具体实现:登录成功:报告密码不同:报告验证码错误:代码相关:找回密码的界面很简单,不细说了,直接写就行找回密码的逻辑实现:下一次就去写数据存储啦!拜拜!......
  • Android笔记--数据存储之SharedPreferences
    SharedPreferences--轻量级存储工具(共享参数)其采用的存储结构是Key-Value的键值对方式SharedPreferences用法以及相关的简单案例记住密码的实现实现啦!那,就白天......
  • 超链接标签
    ​   <!DOCTYPEhtml><html><head><metacharset="UTF-8"><title></title></head><body>......
  • 超链接标签
    ​   <!DOCTYPEhtml><html><head><metacharset="UTF-8"><title></title></head><body>......
  • Android应用安全防护和逆向分析
    Android应用安全防护和逆向分析分享:android逆向小黄书链接:https://pan.baidu.com/s/1WpFuiRi2BR3P58veL0sH7A提取码:ghu0......
  • Android之Design Support Library整理
    1前言GoogleIO2015的DesignSupportLibrary的控件进行效果展示。代码:https://github.com/573842281/Android-Blog-Source就看天气这个应用已经集成了最新的效果,原作者......
  • Android实现导航菜单左右滑动效果
    今天给大家介绍在Android中实现顶部导航菜单左右滑动效果的二种解决方案。第一种解决方案: 在以前的一篇博文中我使用android-support-v4.jar实现了左右滑动指引效果,有兴趣......
  • 【kafka源码】ReassignPartitionsCommand分区副本重分配源码原理分析(附配套教学视频)
    日常运维、问题排查​​滴滴开源LogiKM一站式Kafka监控与管控平台​​​​阿B:石臻臻的杂货铺​​文章目录​​1.脚本的使用​​​​2.源码解析​​​​2.1`--generate`......