目前Android 应用加固可以分为dex加固和Native加固,Native 加固的保护对象为 Native 层的 SO 文件,使用加壳、反调试、混淆、VM 等手段增加SO文件的反编译难度。目前最主流的 SO 文件保护方案还是加壳技术, 在SO文件加壳和脱壳的攻防技术领域,最重要的基础的便是对于 Linker 即装载链接机制的理解。具体的SO装载链接过程是指什么?是否有实例介绍?

1 收藏


直接登录
最新评论
  • YAQ 移动应用安全 2016/11/29

    整体流程说明
    1 do_dlopen
    调用 dl_open 后,中间经过 dlopen_ext, 到达第一个主要函数 do_dlopen:

    soinfo* do_dlopen(const char* name, int flags, const Android_dlextinfo* extinfo) {

    protect_data(PROT_READ | PROT_WRITE);

    soinfo* si = find_library(name, flags, extinfo); // 查找 SO

    if (si != NULL) {

    si->CallConstructors(); // 调用 SO 的 init 函数

    }

    protect_data(PROT_READ);

    return si;

    }

    do_dlopen 调用了两个重要的函数,第一个是find_library, 第二个是 soinfo 的成员函数 CallConstructors,find_library 函数是 SO 装载链接的后续函数, 完成 SO 的装载链接后, 通过 CallConstructors 调用 SO 的初始化函数。

    2 find_library_internal
    find_library 直接调用了 find_library_internal,下面直接看 find_library_internal函数:

    static soinfo* find_library_internal(const char* name, int dlflags, const Android_dlextinfo* extinfo) {

    if (name == NULL) {

    return somain;

    }

    soinfo* si = find_loaded_library_by_name(name);  // 判断 SO 是否已经加载

    if (si == NULL) {

    TRACE(“[ ‘%s’ has not been found by name.  Trying harder…]”, name);

    si = load_library(name, dlflags, extinfo);     // 继续 SO 的加载流程

    }

    if (si != NULL && (si->flags & FLAG_LINKED) == 0) {

    DL_ERR(“recursive link to \”%s\””, si->name);

    return NULL;

    }

    return si;

    }

    find_library_internal 首先通过 find_loaded_library_by_name 函数判断目标 SO 是否已经加载,如果已经加载则直接返回对应的soinfo指针,没有加载的话则调用 load_library 继续加载流程,下面看 load_library 函数。

    3 load_library

    static soinfo* load_library(const char* name, int dlflags, const Android_dlextinfo* extinfo) {

    int fd = -1;

    // Open the file.

    fd = open_library(name);                // 打开 SO 文件,获得文件描述符 fd

     

    ElfReader elf_reader(name, fd);         // 创建 ElfReader 对象

    // Read the ELF header and load the segments.

    if (!elf_reader.Load(extinfo)) {        // 使用 ElfReader 的 Load 方法,完成 SO 装载

    return NULL;

    }

     

    soinfo* si = soinfo_alloc(SEARCH_NAME(name), &file_stat);  // 为 SO 分配新的 soinfo 结构

    if (si == NULL) {

    return NULL;

    }

    si->base = elf_reader.load_start();  // 根据装载结果,更新 soinfo 的成员变量

    si->size = elf_reader.load_size();

    si->load_bias = elf_reader.load_bias();

    si->phnum = elf_reader.phdr_count();

    si->phdr = elf_reader.loaded_phdr();

    if (!soinfo_link_image(si, extinfo)) {  // 调用 soinfo_link_image 完成 SO 的链接过程

    soinfo_free(si);

    return NULL;

    }

    return si;

    }

    load_library 函数呈现了 SO 装载链接的整个流程,主要有3步:

    1装载:创建ElfReader对象,通过 ElfReader 对象的 Load 方法将 SO 文件装载到内存

    2分配soinfo:调用 soinfo_alloc 函数为 SO 分配新的 soinfo 结构,并按照装载结果更新相应的成员变量

    3链接: 调用 soinfo_link_image 完成 SO 的链接

    通过前面的分析,可以看到, load_library 函数中包含了 SO 装载链接的主要过程, 后文主要通过分析 ElfReader 类和 soinfo_link_image 函数, 来分别介绍 SO 的装载和链接过程。

    内容采自《AndroidLinker与SO加壳技术之上篇》