引言
写这篇技术文的原因,主要有两个:
- 其一是发现网上大部分描述PLT/GOT符号重定向过程的文章都是针对x86的,比如《Redirecting functions in shared ELF libraries》就写得非常不错。虽然其过程跟ARM非常类似,但由于CPU体系不同,指令实现差异非常大;
- 其二是网上大部分关于ELF文件格式的介绍,都是基于链接视图(Linking View),链接视图是基于节(Section)对ELF进行解析的。然而动态链接库在加载的过程中,linker只关注ELF中的段(Segment)信息。因此ELF中的节信息被完全篡改或者甚至删除掉,并不会影响linker的加载过程,这样做可以防止静态分析工具(比如IDA,readelf等)对其进行分析,一般加过壳的ELF文件都会有这方面的处理。对于这种ELF文件,如果要实现hook功能,则必须要基于执行视图(Execution View)进行符号解析;
准备
在往下阅读之前,请先确保对ELF文件格式和ARM汇编有个大概了解,参考指引:
准备工具:
- readelf(NDK包含)
- objdump(NDK包含)
- IDA Pro 6.4或以上
- Android真机或者模拟器
符号重定向
在ARM上,常见的重定向类型,主要有三种,分别是R_ARM_JUMP_SLOT、R_ARM_ABS32和R_ARM_GLOB_DAT,而我们要hook elf函数,则需要同时处理好这三种重定向类型。
例子
先看示例代码
typedef int (*strlen_fun)(const char *);
strlen_fun global_strlen1 = (strlen_fun)strlen;
strlen_fun global_strlen2 = (strlen_fun)strlen;
#define SHOW(x) LOGI("%s is %d", #x, x)
extern "C" jint Java_com_example_allhookinone_HookUtils_elfhook(JNIEnv *env, jobject thiz){
const char *str = "helloworld";
strlen_fun local_strlen1 = (strlen_fun)strlen;
strlen_fun local_strlen2 = (strlen_fun)strlen;
int len0 = global_strlen1(str);
int len1 = global_strlen2(str);
int len2 = local_strlen1(str);
int len3 = local_strlen2(str);
int len4 = strlen(str);
int len5 = strlen(str);
SHOW(len0);
SHOW(len1);
SHOW(len2);
SHOW(len3);
SHOW(len4);
SHOW(len5);
return 0;
}
这段代码分别以三种不同的方式调用strlen,分别是全局函数指针、局部函数指针以及直接调用,下而我们针对这个例子,分别对三种调用分析进行分析。
先通过readelf,我们查看一下重定向表,如下所示:
Relocation section '.rel.dyn' at offset 0x2a48 contains 17 entries:
Offset Info Type Sym.Value Sym. Name
0000ade0 00000017 R_ARM_RELATIVE
0000af00 00000017 R_ARM_RELATIVE
0000af0c 00000017 R_ARM_RELATIVE
0000af10 00000017 R_ARM_RELATIVE
0000af18 00000017 R_ARM_RELATIVE
0000af1c 00000017 R_ARM_RELATIVE
0000af20 00000017 R_ARM_RELATIVE
0000af24 00000017 R_ARM_RELATIVE
0000af28 00000017 R_ARM_RELATIVE
0000af30 00000017 R_ARM_RELATIVE
0000aefc 00003215 R_ARM_GLOB_DAT 00000000 __stack_chk_guard
0000af04 00003715 R_ARM_GLOB_DAT 00000000 __page_size
0000af08 00004e15 R_ARM_GLOB_DAT 00000000 strlen
0000b004 00004e02 R_ARM_ABS32 00000000 strlen
0000b008 00004e02 R_ARM_ABS32 00000000 strlen
0000af14 00006615 R_ARM_GLOB_DAT 00000000 __gnu_Unwind_Find_exid
0000af2c 00007415 R_ARM_GLOB_DAT 00000000 __cxa_call_unexpected
...
...
Relocation section '.rel.plt' at offset 0x2ad0 contains 48 entries:
Offset Info Type Sym.Value Sym. Name
0000af40 00000216 R_ARM_JUMP_SLOT 00000000 __cxa_atexit
0000af44 00000116 R_ARM_JUMP_SLOT 00000000 __cxa_finalize
0000af48 00001716 R_ARM_JUMP_SLOT 00000000 memcpy
...
0000afd4 00004c16 R_ARM_JUMP_SLOT 00000000 fgets
0000afd8 00004d16 R_ARM_JUMP_SLOT 00000000 fclose
0000afdc 00004e16 R_ARM_JUMP_SLOT 00000000 strlen
0000afe0 00004f16 R_ARM_JUMP_SLOT 00000000 strncmp
...
...
在.rel.plt和.rel.dyn两个section中,我们发现一共出现了4个strlen,我们先把它们的关键信息记录下来,后面分析会非常有用。它们分别是
.rel.dyn 0000AF08 R_ARM_GLOB_DAT
.rel.dyn 0000B004 R_ARM_ABS32.rel.dyn 0000B008 R_ARM_ABS32.rel.plt 0000