Android Linker与SO加壳技术
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Android Linker与SO加壳技术
1. 前言
Android 系统安全愈发重要,像传统pc安全的可执行文件加固一
样,应用加固是Android系统安全中非常重要的一环。目前
Android 应用加固可以分为dex加固和Native加固,Native 加
固的保护对象为Native 层的SO 文件,使用加壳、反调试、混
淆、VM 等手段增加SO文件的反编译难度。目前最主流的SO 文
件保护方案还是加壳技术,在SO文件加壳和脱壳的攻防技术领
域,最重要的基础的便是对于Linker 即装载链接机制的理解。对
于非安全方向开发者,深刻理解系统的装载与链接机制也是进阶的
必要条件。
本文详细分析了Linker 对SO 文件的装载和链接过程,最后对SO 加壳的关键技术进行了简要的介绍。
对于Linker 的学习,还应该包括Linker 自举、可执行文件的加载等技术,但是限于本人的技术水平,本文的讨论范围限定在SO 文件的加载,也就是在调用dlopen("libxx.SO")之后,Linker 的处理过程。
本文基于Android 5.0 AOSP 源码,仅针对ARM 平台,为了增强可读性,文中列举的源码均经过删减,去除了其他CPU 架构的相关源码以及错误处理。
P.S. :阅读本文的读者需要对ELF 文件结构有一定的了解。
PP.S.:腾讯御安全,一站式解决安全问题,有android app 安全加固需求的同学欢迎联系我们,官方网站:
2. SO 的装载与链接
2.1 整体流程说明
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
文件装载到内存