赞
踩
Android应用的so库文件的加固一直存在,也比较常见,特地花时间整理了一下Android so库文件加固方面的知识。本篇文章主要是对看雪论坛《简单的so脱壳器》这篇文章的思路和代码的分析,很久之前就阅读过这篇文章但是一直没有时间来详细分析它的代码,最近比较有空来分析一下这篇文章中提到的Android
so脱壳器的代码udog,github下载地址为:https://github.com/devilogic/udog,Android so加固的一般手法就是去除掉外壳Android so库文件的 ELF 链接视图 相关的信息,例如区节头表的偏移、区节头表的项数、区节头表名称字符串表的序号等,这样处理以后将Android so加固的外壳so库文件拖到IDA中去分析的时候,直接提示区节头表无效的错误,IDA工具不能对Android
so加固的外壳so库文件进行分析,达到抗IDA工具静态分析的目的。
Android so加固中被保护的Android so库文件是由外壳Android so库文件在.init段或者.init_array段的构造函数里自定linker进行内存加载和解密的,被保护的Android so库文件自定义内存加载、映射完成以后将外壳Android so库文件的soinfo*(dlopen函数返回的)修改为被保护Android so库文件的soinfo*,这样被保护的Android so库文件的内存加载就成功了并且就可以被调用了。尽管被加固保护的Android
so库文件被加密保护起来了,但是该Android so库文件还是会在内存中进行解密出来,因此我们可以在被加固保护的Android so库文件内存解密时进行内存dump处理,然后对dump出来的Android so库文件进行ELF文件格式的调整和修复以及section区节头表的重建,就可以实现被保护的Android so库文件的脱壳了。
简单so脱壳器这篇文章中提到的so脱壳器udog的代码比较简单,之前以为udog的代码比较复杂,后来整理了一下作者玩命的代码发现很多代码都是废弃的,核心关键的代码部分不是很复杂但是对于学习Android so加固的脱壳很有作用,也是Android so加固脱壳和内存dump后修复的第一步,玩命版主主要实现了被加固Android so库文件的内存dump和ELF文件格式部分参数的修复处理,对于脱壳后ELF文件的section区节头表等的重建并没有实现。
udog代码的入口 main函数 在linker.cpp文件中如下图所示:
在linker.cpp文件中,main函数工作流程是: 先对用户输入的命令行参数进行解析处理得到 用户参数解析结果描述结构体options_t,然后根据用户输入的命令参数解析的结果options_t 进行Android so的脱壳相关的操作。udog脱壳器中帮助命令行在文件options.cpp中实现,如下图所示:
用户输入的命令行参数解析结果保存结构体options_t中,如下图代码所示:
// 保存用户输入命令行参数的解析结果
struct options_t {
bool call_dt_init;
bool call_dt_init_array;
bool call_dt_finit;
bool call_dt_finit_array;
bool load_pre_libs;
bool load_needed_libs;
bool load;
bool not_relocal; /* 不对重定位表进行修复 */
bool make_sectabs; /* 重建elf文件的区节头表 */
bool dump;
bool help;
bool version;
bool debug;
bool check;
bool clear_entry;
int debuglevel;
unsigned xct_offset;
unsigned xct_size;
char dump_file[128];
char target_file[128];
};
对用户输入的命令行参数进行解析处理的操作在函数handle_arguments中实现,如下图代码所示:
// main函数在linker.cpp类
int main(int argc, char* argv[]) {
// 解析用户输入的命令行参数
g_opts = handle_arguments(argc, argv);// 解析用户输入的命令行参数
struct options_t* handle_arguments(int argc, char* argv[]) {
// 保存命令行参数解析后的结果
static struct options_t opts;
// 清零
memset(&opts, 0, sizeof(opts));
// 默认参数的设置
opts.call_dt_init = true;
opts.call_dt_init_array = true;
opts.call_dt_finit = true;
opts.call_dt_finit_array = true;
opts.load_pre_libs = true;
opts.load_needed_libs = true;
int opt;
int longidx;
int dump = 0, help = 0, version = 0,
debug = 0, check = 0, xcto = 0,
xcts = 0, clear_entry = 0;
if (argc == 1) {
return NULL;
}
// 输入参数选项的解析顺序和规则
// 该数据结构包括了所有要定义的短选项,每一个选项都只用单个字母表示。
// 如果该选项需要参数,则其后跟一个冒号
co
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。