Linker——安卓10

admin 2024年10月31日21:08:04评论15 views字数 13540阅读45分8秒阅读模式

system.loadLibrary

    加载 SO 时候 我们经常用到 system.loadLibrary

走了/libcore/ojluni/src/main/java/java/lang/Runtime.java

    @CallerSensitive    public static void loadLibrary(String libname) {        Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);    }    void loadLibrary0(Class<?> fromClass, String libname) {        ClassLoader classLoader = ClassLoader.getClassLoader(fromClass);        loadLibrary0(classLoader, fromClass, libname);    }

    可以看到调用了loadLibrary0,在这里获取了classloader

    private synchronized void loadLibrary0(ClassLoader loader, Class<?> callerClass, String libname) {        if (libname.indexOf((int)File.separatorChar) != -1) {            throw new UnsatisfiedLinkError(    "Directory separator should not appear in library name: " + libname);        }        String libraryName = libname;        // Android-note: BootClassLoader doesn't implement findLibrary(). http://b/111850480        // Android's class.getClassLoader() can return BootClassLoader where the RI would        // have returned null; therefore we treat BootClassLoader the same as null here.        if (loader != null && !(loader instanceof BootClassLoader)) {            String filename = loader.findLibrary(libraryName);            if (filename == null) {                // It's not necessarily true that the ClassLoader used                // System.mapLibraryName, but the default setup does, and it's                // misleading to say we didn't find "libMyLibrary.so" when we                // actually searched for "liblibMyLibrary.so.so".                throw new UnsatisfiedLinkError(loader + " couldn't find "" +                                               System.mapLibraryName(libraryName) + """);            }            String error = nativeLoad(filename, loader);            if (error != null) {                throw new UnsatisfiedLinkError(error);            }            return;        }        // We know some apps use mLibPaths directly, potentially assuming it's not null.        // Initialize it here to make sure apps see a non-null value.        getLibPaths();        String filename = System.mapLibraryName(libraryName);        String error = nativeLoad(filename, loader, callerClass);        if (error != null) {            throw new UnsatisfiedLinkError(error);        }    }......    private static native String nativeLoad(String filename, ClassLoader loader, Class<?> caller);

    classloader 没啥说的,没记错的话 就是pathclassloader

可以看到前面基本就是判断和对path进行赋值,关键基本就一行代码    

     String error = nativeLoad(filename, loader, callerClass);

/libcore/ojluni/src/main/native/Runtime.c 进行了注册

JNIEXPORT jstring JNICALLRuntime_nativeLoad(JNIEnv* env, jclass ignored, jstring javaFilename,                   jobject javaLoader, jclass caller){    return JVM_NativeLoad(env, javaFilename, javaLoader, caller);}  NATIVE_METHOD(Runtime, nativeLoad,                "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Class;)"                    "Ljava/lang/String;"),

    JVM_NativeLoad 注册在 /art/openjdkjvm/OpenjdkJvm.cc

JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,                                 jstring javaFilename,                                 jobject javaLoader,                                 jclass caller) {  ScopedUtfChars filename(env, javaFilename);  if (filename.c_str() == nullptr) {    return nullptr;  }  std::string error_msg;  {    art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();    bool success = vm->LoadNativeLibrary(env,                                         filename.c_str(),                                         javaLoader,                                         caller,                                         &error_msg);    if (success) {      return nullptr;    }  }  // Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF.  env->ExceptionClear();  return env->NewStringUTF(error_msg.c_str());}

    其实就调用 LoadNativeLibrary 方法在文件 art/runtime/jni/java_vm_ext.cc

    代码很长就不贴了,前面判断了一下so是否已加载,然后就获取handle

 const char* path_str = path.empty() ? nullptr : path.c_str();  bool needs_native_bridge = false;  char* nativeloader_error_msg = nullptr;  void* handle = android::OpenNativeLibrary(      env,      runtime_->GetTargetSdkVersion(),      path_str,      class_loader,      (caller_location.empty() ? nullptr : caller_location.c_str()),      library_path.get(),      &needs_native_bridge,      &nativeloader_error_msg);

    这个OpenNativeLibrary 在 /system/core/libnativeloader/native_loader.cpp

637void* OpenNativeLibrary(JNIEnv* env,638                        int32_t target_sdk_version,639                        const char* path,640                        jobject class_loader,641                        jstring library_path,642                        bool* needs_native_bridge,643                        std::string* error_msg) {644#if defined(__ANDROID__)    ....     return OpenNativeLibraryInNamespace(ns, path, needs_native_bridge, error_msg);

    这里就调用了 OpenNativeLibraryInNamespace

#if defined(__ANDROID__)void* OpenNativeLibraryInNamespace(NativeLoaderNamespace* ns, const char* path,                                   bool* needs_native_bridge, char** error_msg) {  if (ns->is_android_namespace()) {    android_dlextinfo extinfo;    extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;    extinfo.library_namespace = ns->get_android_ns();    void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo);    if (handle == nullptr) {      *error_msg = strdup(dlerror());    }    *needs_native_bridge = false;    return handle;  } else {    void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns->get_native_bridge_ns());    if (handle == nullptr) {      *error_msg = strdup(NativeBridgeGetError());    }    *needs_native_bridge = true;    return handle;  }}

    实际上就调用了android_dlopen_ext,这玩意儿在 /bionic/libdl/libdl.cpp

__attribute__((__weak__))void* android_dlopen_ext(const char* filename, int flag, const android_dlextinfo* extinfo) {  const void* caller_addr = __builtin_return_address(0);  return __loader_android_dlopen_ext(filename, flag, extinfo, caller_addr);}__attribute__((__weak__, visibility("default")))void* __loader_android_dlopen_ext(const char* filename,                                  int flag,                                  const android_dlextinfo* extinfo,                                  const void* caller_addr);

    调用了 __loader_android_dlopen_ext,然后__loader_android_dlopen_ext定义在 bionic/linker/dlfcn.cpp

void* __loader_android_dlopen_ext(const char* filename,                           int flags,                           const android_dlextinfo* extinfo,                           const void* caller_addr) {  return dlopen_ext(filename, flags, extinfo, caller_addr);}

    到这里就与linker联系起来了

linker

获取Soinfo

static void* dlopen_ext(const char* filename,                        int flags,                        const android_dlextinfo* extinfo,                        const void* caller_addr) {  ScopedPthreadMutexLocker locker(&g_dl_mutex);  g_linker_logger.ResetState();  void* result = do_dlopen(filename, flags, extinfo, caller_addr);  if (result == nullptr) {    __bionic_format_dlerror("dlopen failed", linker_get_error_buffer());    return nullptr;  }  return result;}

    dlopen_ext 里面实际上就调用了 do_dlopen ,这就是重点了

    do_dlopen 的路径在 bionic/linker/linker.cpp

    方法体的代码很长,我们直接看几个返回值,只有一个return handle; 其他全是 return nullptr;,所以直接看这部分代码

void* do_dlopen(const char* name, int flags,                const android_dlextinfo* extinfo,                const void* caller_addr) {                ......  soinfo* si = find_library(ns, translated_name, flags, extinfo, caller);  loading_trace.End();  if (si != nullptr) {    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;                }

    soinfo* si = find_library(ns, translated_name, flags, extinfo, caller);

    find_library 这个方法的返回值就是我们需要的 soinfo,这里可以获取so的各种相关信息

static soinfo* find_library(android_namespace_t* ns,                            const char* name, int rtld_flags,                            const android_dlextinfo* extinfo,                            soinfo* needed_by) {  soinfo* si = nullptr;  if (name == nullptr) {    si = solist_get_somain();  } else if (!find_libraries(ns,                             needed_by,                             &name,                             1,                             &si,                             nullptr,                             0,                             rtld_flags,                             extinfo,                             false /* add_as_children */,                             true /* search_linked_namespaces */)) {    if (si != nullptr) {      soinfo_unload(si);    }    return nullptr;  }  si->increment_ref_count();  return si;}

    这里 soinfo* si 在find_libraries (多了个s)中进行了赋值,soinfo_unload中对链接什么的进行了判断处理

    find_libraries 关键代码:

  for (size_t i = 0; i<load_tasks.size(); ++i) {    LoadTask* task = load_tasks[i];    soinfo* needed_by = task->get_needed_by();    bool is_dt_needed = needed_by != nullptr && (needed_by != start_with || add_as_children);    task->set_extinfo(is_dt_needed ? nullptr : extinfo);    task->set_dt_needed(is_dt_needed);    LD_LOG(kLogDlopen, "find_libraries(ns=%s): task=%s, is_dt_needed=%d", ns->get_name(),           task->get_name(), is_dt_needed);    // Note: start from the namespace that is stored in the LoadTask. This namespace    // is different from the current namespace when the LoadTask is for a transitive    // dependency and the lib that created the LoadTask is not found in the    // current namespace but in one of the linked namespace.    if (!find_library_internal(const_cast<android_namespace_t*>(task->get_start_from()),                               task,                               &zip_archive_cache,                               &load_tasks,                               rtld_flags,                               search_linked_namespaces || is_dt_needed)) {      return false;    }    soinfo* si = task->get_soinfo();

    find_libraries 中先对so的依赖关系进行处理,并初始化soinfo,然后在 find_library_internal 来定位并准备加载库。在指定命名空间以及已链接命名空间中搜索库

maps可以检测so的原因

    find_library_internal 的关键代码

  if (load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags, search_linked_namespaces)) {    return true;  }    ........    for (auto& linked_namespace : ns->linked_namespaces()) {      if (find_library_in_linked_namespace(linked_namespace, task)) {        if (task->get_soinfo() == nullptr) {          // try to load the library - once namespace boundary is crossed          // we need to load a library within separate load_group          // to avoid using symbols from foreign namespace while.          //          // However, actual linking is deferred until when the global group          // is fully identified and is applied to all namespaces.          // Otherwise, the libs in the linked namespace won't get symbols from          // the global group.          if (load_library(linked_namespace.linked_namespace(), task, zip_archive_cache, load_tasks, rtld_flags, false)) {            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;          }        } else {          // lib is already loaded          return true;        }      }    }

    AOSP中还很贴心的给了注释 // lib is already loaded,所以这里主要就是调用 load_library

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;  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);  }  // 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);  return load_library(ns, task, load_tasks, rtld_flags, realpath, search_linked_namespaces);}

    最后都是调用 load_library 的重载

    先使用 open_library 打开文件拿到fd,然后进入重载方法

load_library 关键代码:

static bool load_library(android_namespace_t* ns,                         LoadTask* task,                         LoadTaskList* load_tasks,                         int rtld_flags,                         const std::string& realpath,                         bool search_linked_namespaces) {  soinfo* si = soinfo_alloc(ns, realpath.c_str(), &file_stat, file_offset, rtld_flags);  if (si == nullptr) {    return false;  }  task->set_soinfo(si);    // Read the ELF header and some of the segments.  if (!task->read(realpath.c_str(), file_stat.st_size)) {    soinfo_free(si);    task->set_soinfo(nullptr);    return false;  }  // find and set DT_RUNPATH and dt_soname  // Note that these field values are temporary and are  // going to be overwritten on soinfo::prelink_image  // with values from PT_LOAD segments.  const ElfReader& elf_reader = task->get_elf_reader();  for (const ElfW(Dyn)* d = elf_reader.dynamic(); d->d_tag != DT_NULL; ++d) {    if (d->d_tag == DT_RUNPATH) {      si->set_dt_runpath(elf_reader.get_string(d->d_un.d_val));    }    if (d->d_tag == DT_SONAME) {      si->set_soname(elf_reader.get_string(d->d_un.d_val));    }  }                         }

    在这个方法里很明显就能看到获取soinfo 和 soinfo_free 初始化soinfo的地方,然后进行了 ElfReader 读取elf 相关信息

    find_libraries 方法中的find_library_internal 执行完之后,注释写的明明白白

// Step 2: Load libraries in random order (see b/24047022)

在这里进行库的加载

  // Step 2: Load libraries in random order (see b/24047022)  LoadTaskList load_list;  for (auto&& task : load_tasks) {    soinfo* si = task->get_soinfo();    auto pred = [&](const LoadTask* t) {      return t->get_soinfo() == si;    };    if (!si->is_linked() &&        std::find_if(load_list.begin(), load_list.end(), pred) == load_list.end() ) {      load_list.push_back(task);    }  }  bool reserved_address_recursive = false;  if (extinfo) {    reserved_address_recursive = extinfo->flags & ANDROID_DLEXT_RESERVED_ADDRESS_RECURSIVE;  }  if (!reserved_address_recursive) {    // Shuffle the load order in the normal case, but not if we are loading all    // the libraries to a reserved address range.    shuffle(&load_list);  }  // Set up address space parameters.  address_space_params extinfo_params, default_params;  size_t relro_fd_offset = 0;  if (extinfo) {    if (extinfo->flags & ANDROID_DLEXT_RESERVED_ADDRESS) {      extinfo_params.start_addr = extinfo->reserved_addr;      extinfo_params.reserved_size = extinfo->reserved_size;      extinfo_params.must_use_address = true;    } else if (extinfo->flags & ANDROID_DLEXT_RESERVED_ADDRESS_HINT) {      extinfo_params.start_addr = extinfo->reserved_addr;      extinfo_params.reserved_size = extinfo->reserved_size;    }  }  for (auto&& task : load_list) {    address_space_params* address_space =        (reserved_address_recursive || !task->is_dt_needed()) ? &extinfo_params : &default_params;    if (!task->load(address_space)) {      return false;    }  }

    判断库是否被链接过,然后调用 task->load(address_space) 进行加载

  bool load(address_space_params* address_space) {    ElfReader& elf_reader = get_elf_reader();    if (!elf_reader.Load(address_space)) {      return false;    }    si_->base = elf_reader.load_start();    si_->size = elf_reader.load_size();    si_->set_mapped_by_caller(elf_reader.is_mapped_by_caller());    si_->load_bias = elf_reader.load_bias();    si_->phnum = elf_reader.phdr_count();    si_->phdr = elf_reader.loaded_phdr();    return true;  }

    在这里可以看到,调用了 elf_reader.Load(address_space),然后将加载后再内存中的地址等信息进行回填elf_reader.Load 在 bionic/linker/linker_phdr.cpp 中

bool ElfReader::Load(address_space_params* address_space) {  CHECK(did_read_);  if (did_load_) {    return true;  }  if (ReserveAddressSpace(address_space) && LoadSegments() && FindPhdr()) {    did_load_ = true;  }  return did_load_;}

ReserveAddressSpace(address_space) && LoadSegments() && FindPhdr()

这几个函数就有大问题了

特别是 LoadSegments

      void* seg_addr = mmap64(reinterpret_cast<void*>(seg_page_start),                            file_length,                            prot,                            MAP_FIXED|MAP_PRIVATE,                            fd_,                            file_offset_ + file_page_start);      if (seg_addr == MAP_FAILED) {        DL_ERR("couldn't map "%s" segment %zd: %s", name_.c_str(), i, strerror(errno));        return false;      }    }    // if the segment is writable, and does not end on a page boundary,    // zero-fill it until the page limit.    if ((phdr->p_flags & PF_W) != 0 && PAGE_OFFSET(seg_file_end) > 0) {      memset(reinterpret_cast<void*>(seg_file_end), 0, PAGE_SIZE - PAGE_OFFSET(seg_file_end));    }

    这里可以看到他使用mmap64 将文件映射到匿名空间中,所以这里改一下就可以实现maps里so路径的隐藏。

原文始发于微信公众号(逆向成长日记):Linker——安卓10

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年10月31日21:08:04
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Linker——安卓10https://cn-sec.com/archives/3339837.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息