版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、基于Android的ELF PLT/GOT符号重定向过程及ELF Hook实现by引言写这篇技术文的原因,主要有两个:码农 2014.10.27其一是发现网上大部分描述PLT/GOT符号重定向过程的文章都是针对x86的,比如Redirecting functions in shared ELF libraries就写得非常不错。虽然其过程跟ARM非常类似,但由于CPU体系不同,指令实现差异非常大;其二是网上大部分关于ELF文件格式的介绍,都是基于视图(Linking View),库在加载的过程中,linker视图是基于节(Section)对ELF进行的。然而动态只关注ELF中的段(Segmen
2、t)信息。因此ELF中的节信息被完全篡改或者甚至删除 掉,并不会影响linker的加载过程,这样做可以防止静态分析工具(比如IDA,readelf等)对其进行分析,一般加过壳的ELF文件都会有这方面的处理。对于这种ELF文件,如果要实现hook功能,则必须要基于执行视图(Execution View)进行符号准备在往下阅读之前,请先确保对ELF文件格式和ARM汇编有个大概了解,参考指引:;ELF 文件格式分析; ARM文档;准备工具:readelf(NDK包含) objdump(NDK包含) IDA Pro 6.4或以上 Android真机或者模拟器符号重定向在ARM上,常见的重定向类型,主要
3、有三种,分别是R_ARM_JUMP_SLOT、R_ARM_ABS32和R_ARM_GLOB_DAT,而则需要同时处理好这三种重定向类型。要hook elf函数,例子先看示例代码这段代码分别以三种不同的方式调用strlen,分别是全局函数指针、局部函数指针以及直接调用,下而我们针对这个例子,分别对三种调用分析进行分析。先通过readelf,我们查看一下重定向表,如下所示:typedef int (*strlen_fun)(const char *); strlen_fun global_strlen1 = (strlen_fun)strlen; strlen_fun global_strlen2
4、 = (strlen_fun)strlen;#define SHOW(x) LOGI(%s is %d, #x, x)extern C jint _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 len
5、1 = 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;在.rel.plt和.rel.dyn两个section中,发现一共出现了4个strlen,先把它们的关键信息记录下来,后面分析会非常有用。它们分别是“.rel.dyn 000
6、0AF08 R_ARM_GLOB_DAT.rel.dyn 0000B004 R_ARM_ABS32.rel.dyn 0000B008 R_ARM_ABS32.rel.plt 0000AFDC R_ARM_JUMP_SLOT在代码中,一共调用了6次strlen,但为什么只出现了4次呢?另外,它们之间又是如何对Relocation section .rel.dyn at offset 0 x2a48 contains 17 entries: OffsetInfoTypeSym.Value Sym. Name0000ade0 00000017 R_ARM_RELATIVE0000af00 00000
7、017 R_ARM_RELATIVE0000af0c 00000017 R_ARM_RELATIVE0000af10 00000017 R_ARM_RELATIVE0000af18 00000017 R_ARM_RELATIVE0000af1c 00000017 R_ARM_RELATIVE0000af20 00000017 R_ARM_RELATIVE0000af24 00000017 R_ARM_RELATIVE0000af28 00000017 R_ARM_RELATIVE0000af30 00000017 R_ARM_RELATIVE0000aefc 00003215 R_ARM_GL
8、OB_DAT00000000 stack_chk_guard0000af04 00003715 R_ARM_GLOB_DAT00000000 page_size0000af08 00004e15 R_ARM_GLOB_DAT00000000strlen0000b004 00004e02 R_ARM_ABS3200000000strlen0000b008 00004e02 R_ARM_ABS3200000000strlen0000af14 00006615 R_ARM_GLOB_DAT00000000 gnu_Unwind_Find_exid0000af2c 00007415 R_ARM_GLO
9、B_DAT00000000 cxa_call_unexpected.Relocation section .rel.plt at offset 0 x2ad0 contains 48 entries: OffsetInfoTypeSym.Value Sym. Name0000af40 00000216 R_ARM_JUMP_SLOT00000000 cxa_atexit0000af44 00000116 R_ARM_JUMP_SLOT00000000 cxa_finalize0000af48 00001716 R_ARM_JUMP_SLOT00000000memcpy.0000afd4 000
10、04c16 R_ARM_JUMP_SLOT00000000fgets0000afd8 00004d16 R_ARM_JUMP_SLOT00000000fclose0000afdc 00004e16 R_ARM_JUMP_SLOT00000000strlen0000afe0 00004f16 R_ARM_JUMP_SLOT00000000strncmp.应的呢,带着这些问题去分析汇编代码。把编译出来的so拖到IDA,令:看到示例代码的指.text:000050BCEXPORT_exle_allhookinone_HookUtils_elfhook.text:000050BC_exle_allho
11、okinone_HookUtils_elfhook.text:000050BC.text:000050BC var_40= -0 x40.text:000050BC var_38= -0 x38.text:000050BC var_34= -0 x34.text:000050BC s= -0 x2C.text:000050BC var_28= -0 x28.text:000050BC var_24= -0 x24.text:000050BC var_20= -0 x20.text:000050BC var_1C= -0 x1C.text:000050BC var_18= -0 x18.text
12、:000050BC var_14= -0 x14.text:000050BC var_10= -0 x10.text:000050BC var_C= -0 xC.text:000050BC.text:000050BCPUSHR4,LR.text:000050BESUBSP, SP, #0 x38.text:000050C0STRR0, SP,#0 x40+var_34.text:000050C2STRR1, SP,#0 x40+var_38.text:000050C4LDRR4, =(_GLOBAL_OFFSET_TABLE_ - 0 x50CA).text:000050C6ADDR4, PC
13、 ;_GLOBAL_OFFSET_TABLE_.text:000050C8LDRR3, =(aoworld - 0 x50CE).text:000050CAADDR3, PC ; oworld.text:000050CCSTRR3, SP,#0 x40+s.text:000050CELDRR3, =(strlen_ptr - 0 xAF34).text:000050D0LDRR3, R4,R3 ; imp_strlen.text:000050D2STRR3, SP,#0 x40+var_28.text:000050D4LDRR3, =(strlen_ptr - 0 xAF34).text:00
14、0050D6LDRR3, R4,R3 ; imp_strlen.text:000050D8STRR3, SP,#0 x40+var_24.text:000050DALDRR3, =(global_strlen1_ptr - 0 xAF34).text:000050DCLDRR3, R4,R3 ; global_strlen1.text:000050DELDRR3, R3.text:000050E0LDRR2, SP,#0 x40+s.text:000050E2MOVSR0, R2.text:000050E4BLXR3.text:000050E6MOVSR3, R0.text:000050E8S
15、TRR3, SP,#0 x40+var_20.text:000050EALDRR3, =(global_strlen2_ptr - 0 xAF34).text:000050ECLDRR3, R4,R3 ; global_strlen2.text:000050EELDRR3, R3先把几个重要的地址找出来,它们分别是GLOBAL_OFFSET_TABLE: 0 x0000AF34strlen_ptr: 0 x0000AF08 imp_strlen: 0 x0000B0C8 global_strlen1_ptr: 0 x0000AF0C global_strlen1: 0 x0000B004 gl
16、obal_strlen2_ptr: 0 x0000AF10 global_strlen2: 0 x0000B008全局函数指针调用外部函数global_strlen1和global_strlen2的调用,对应0 x000050E4和0 x000050F4两处的BLX指令,通过计算最终R3的值分别是*global_strlen1和*global_strlen2,而global_strlen1和global_strlen2的值正好对应位于.rel.dyn的两个R_ARM_ABS32的重定位项,因此得出结论:通过全局函数指针的方式调用外部函数,它的重定位类型是R_ARM_ABS32,并且位于.rel
17、.dyn节区。.text:000050F0LDRR2, SP,#0 x40+s.text:000050F2MOVSR0, R2.text:000050F4BLXR3.text:000050F6MOVSR3, R0.text:000050F8STRR3, SP,#0 x40+var_1C.text:000050FALDRR2, SP,#0 x40+s.text:000050FCLDRR3, SP,#0 x40+var_28.text:000050FEMOVSR0, R2.text:00005100BLXR3.text:00005102MOVSR3, R0.text:00005104STRR3,
18、SP,#0 x40+var_18.text:00005106LDRR2, SP,#0 x40+s.text:00005108LDRR3, SP,#0 x40+var_24.text:0000510AMOVSR0, R2.text:0000510CBLXR3.text:0000510EMOVSR3, R0.text:00005110STRR3, SP,#0 x40+var_14.text:00005112LDRR3, SP,#0 x40+s.text:00005114MOVSR0, R3 ; s.text:00005116BLXstrlen.text:0000511AMOVSR3, R0.tex
19、t:0000511CSTRR3, SP,#0 x40+var_10.text:0000511ELDRR3, SP,#0 x40+s.text:00005120MOVSR0, R3 ; s.text:00005122BLXstrlen.text:00005126MOVSR3, R0.text:000051CAADDSP, SP, #0 x38.text:000051CCPOPR4,PC.text:000051CC ; End of function_exle_allhookinone_HookUtils_elfhook我们只分析global_strlen1的调用过程,首先定位到global_st
20、rlen1_ptr(0 x0000AF0C),该地址位于.got节区,GLOBAL_OFFSET_TABLE的上方。然后再通过global_strlen1_ptr定位到 0 x0000B004(位于.data节区),最后再通过0 x0000B004定位到最终的函数地址,因此 R_ARM_ABS32重定位项的Oset指向最终调用函数地址的地址(也就是函数指针的指针),整个重定位过程是先位到.got,再从.got定位到.date。下面是.got段区的16进制表示片段:最后发现0 x0000B0C8地址片的指令全为0,当动态链接时,linker会覆盖0 x0000B004地址的值,指向strlen的
21、真正地址(而不是现在的0 x0000B0C8,有点绕)。局部函数指针调用外部函数local_strlen1和local_strlen2的调用,对应0 x00005100和0 x0000510C两处的BLX指令,通过计算最终R3的值都是*strlen_prt,即0 x0000AF08,正好对应位于.rel.dyn中的 R_ARM_GLOB_DAT重定位项,因此我们得出结论:通过局部函数指针方式调用外部函数,它的重定位类型是R_ARM_GLOB_DAT,并且位于.re.dyn节区。我们只分析local_strlen1的调用过程,首先是定位到strlen_prt(0 x0000AF08),该地址位
22、于.got节区,GLOBAL_OFFSET_TABLE的上方,然后再通过strlen_prt,定位到 0 x0000B0C8,跟上面分析的结果居然一样,因此R_ARM_GLOB_DAT的重定项Oset指向最终调用函数地址的地址(也就是函数指针的指针),下面是.got段区的16进制表示片段:需要注意的是,0 x000050D8的指令“STR R3, SP,#0 x40+var_24”,这里已经把函数的真实地址保存到堆栈了,因此哪怕我们修改了GOT表也不会影响堆栈的值,因此这种重定位类型无法通过修改地址进行hook。0000AF08 C8 B0 00 00 04 B0 00 00 08 B0 00
23、 00 DC B0 00 000000AF18 B4 87 00 00 F4 84 00 00 60 5B 00 00 58 5B 00 000000AF28 50 5B 00 00 EC B0 00 00 FC 8C 00 00 00 00 00 00.0000B0C8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000000B0D8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00.0000AF0C 04 B0 00 00 08 B0 00 00 DC B0 00 00 B4 87 00 00000
24、0AF1C F4 84 00 00 60 5B 00 00 58 5B 00 00 50 5B 00 000000AF2C EC B0 00 00 FC 8C 00 00 00 00 00 00 00 00 00 00.0000B004 C8 B0 00 00 C8 B0 00 00 ? ? ? ? ? ? ? ?0000B014 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?0000B024 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00.0000B0C8 00 00 00 00 00 00 00 00 00 00 00 00
25、00 00 00 000000B0D8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00.直接调用外部函数最后看看strlen的直接调用,对应0 x0000511A和0 x00005122两处的BLX指令,最后它们都指向.plt节区指令,如下所示:最后,PC指向*strlen_ptr_0,即strlen_ptr_0的地址0 x0000AFDC,该地址位于.got节区,而 0 x0000AFDC地址值的正好是0 x0000B0C8,多么熟悉的身影。因此得到结论,直接调用外部函数,它的重定位类型是R_ARM_JUMP_SLOT,并且位于.re.plt节
26、区,其Oset指向最终调用函数地址的地址(也就是函数指针的指针)。整个过程是先到.plt,再到.got,最后才定位到真正的函数地址。关于这部分的分析,发现IDA和objdump的反编译结果有些差异,下面是通过objdump到的汇编指令:见到afdc处的地址,指向的是0 x00002c50,而0 x00002c50正好是PLT0,指令如下:执行2c5c处指令后,最终pc指向0 x0000af3c,正好是GLOBAL_OFFSET_TABLE + 8,即00002c50 :2c50: e52de004 push lr; (str lr, sp, #-4!)2c54: e59fe004 ldr lr
27、, pc, #4 ; 2c60 2c58: e08fe00e add lr, pc, lr2c5c: e5bef008 ldr pc, lr, #8! 2c60: 000082d4 ldrdeq r8, r0, -r400002e38 :2e38: e28fc600 add ip, pc, #0, 122e3c: e28cca08 add ip, ip, #8, 20 ; 0 x8000 2e40: e5bcf19c ldr pc, ip, #412! ; 0 x19c.afd8: 00002c50 andeq r2, r0, r0, asr ip afdc: 00002c50 andeq r
28、2, r0, r0, asr ip afe0: 00002c50 andeq r2, r0, r0, asr ip afe4: 00002c50 andeq r2, r0, r0, asr ip.plt:00002E38ADRR12, 0 x2E40.plt:00002E3CADDR12, R12, #0 x8000.plt:00002E40LDRPC, R12,#(strlen_ptr_0 - 0 xAE40)! ; imp_strlen.0000AFDC C8 B0 00 00 CC B0 00 00 D0 B0 00 00 D4 B0 00 000000AFEC D8 B0 00 00
29、DC B0 00 00 E0 B0 00 00 E4 B0 00 000000AFFC E8 B0 00 00 00 00 00 00 C8 B0 00 00 C8 B0 00 00.GOT2,我们看到0 x0000af3c处:结果发现GOT2里指向的函数地址居然是0,这是因为android上的符号绑定并不支持lazy绑 定,所以当so被加载时,linker会预先把GOTn(n=2)的所对应的函数都提前找出来,因此这里GOT2的代码实际上不会被执行,因此在目前的Android上,并不存在完整的PLT/GOT链接过程。猜想这主要是出于稳定性考虑的。总结虽然IDA和obudump两个工具反编译得出
30、的指令在PLTGOT过程中有些差别,但对于Android而言,其实这个差异不会造成影响,因为Android上不支持lazy绑定。同时我们得出一个非常重要的结论:R_ARM_ABS32、R_ARM_GLOB_DAT和R_ARM_JUMP_SLOT的重定位项虽然在代码中用法不一样,但其oset都是指向一个函数的指针的指针,这个对于我们下面进行 elfhook非常有用。基于执行视图解析ELFRedirecting functions in shared ELF libraries这篇文章所提供的例子,就是基于链接视图对ELF进行解析的,与基于执行视图进行解析相比,后面的逻辑基本是一样的,关键是要通过
31、 segment找到.dynsym、.dynstr、.rel.plt和rel.dyn,以及它们的项数。首次通过Program Header Table找到类型为PT_DYNAMIC的段,该的内容其实对应.dynamic,这段的内容对应Elf32_Dyn类型的数组,其结构体如下所示:通过遍历这个数组,我们可以找到所有的需要的信息,我把它们的对应关系列出来:DT_HASH - .hashDT_SYMTAB & DT_SYMENT - .dynsym DT_STRTAB & DT_STRSZ - .dynstrPLTREL(决定REL还是RELA) &(DT_REL | DT_RELA) & (DT
32、_RELSZ | DT_RELASZ ) & (DT_RELENT | DT_RELAENT ) - .rel.dynDT_JMPREL & DT_PLTRELSZ & (DT_RELENT | DT_RELAENT) - .rel.pltFINI_ARRAY & FINI_ARRAYSZ - .fini_array/* Dynamic structure */ typedef struct Elf32_Sword d_tag;/* controls meaning of d_val */ union Elf32_Word d_val; /* Multiple meanings - see d
33、_tag */ Elf32_Addr d_ptr; /* program virtual address */ d_un; Elf32_Dyn;0000AF3C 00 00 00 00 28 B0 00 00 24 B0 00 00 2C B0 00 000000AF4C 30 B0 00 00 34 B0 00 00 38 B0 00 00 3C B0 00 00INIT_ARRAY & INIT_ARRAYSZ - .init_array这是查找的相关代码:void gefInfoBySegmentView(ElfInfo &info, consfHandle *handle) info.
34、handle = handle;info.elf_base = (u8_t *) handle-base;info.ehdr = reret_casf32_Ehdr *(info.elf_base);/ may be wronginfo.shdr = reret_casf32_Shdr *(info.elf_base + info.ehdr-e_shoff);info.phdr = reret_casf32_Phdr *(info.elf_base + info.ehdr-e_phoff);info.shstr = NULL;Elf32_Phdr *dynamic = NULL; Elf32_
35、Word size = 0;getSegmentInfo(info, PT_DYNAMIC, &dynamic, &size, &info.dyn); if(!dynamic)LOGE(- couldt find PT_DYNAMIC segment); exit(-1);info.dynsz = size / sizeof(Elf32_Dyn);Elf32_Dyn *dyn = info.dyn;for(i=0; id_tag)case DT_SYMTAB:info.sym = reret_casf32_Sym *(info.elf_base + dyn-d_un.d_ptr);break;
36、case DT_STRTAB:info.symstr = reret_cast(info.elf_base + dyn-d_un.d_ptr);break;case DT_REL:info.reldyn = reret_casf32_Rel *(info.elf_base + dyn-d_un.d_ptr);break;case DT_RELSZ:info.reldynsz = dyn-d_un.d_val / sizeof(Elf32_Rel); break;然而,有一个值我无法通过通过PT_DYNAMIC段得到的,那就是.dynsym的项数,我最后通过变通的方法得到的。由于.dynsym和
37、.dynstr两个节区是相邻的,因此它们两个地址相减,即可得到的.dynsym总长度,再除了sizeof(Elf32_Sym)即方法,请跟我说说。ELF Hook到.dynsym的项数,如果你有更好的有了上面的介绍之后,写个ELF Hook就很简单的,我把关键代码贴出来:#define R_ARM_ABS32 0 x02 #define R_ARM_GLOB_DAT 0 x15 #define R_ARM_JUMP_SLOT 0 x16elfHook(const char *soname, const char *symbol, void *replace_func, void*old_fun
38、c)assert(old_func); assert(replace_func); assert(symbol);ElfHandle* handle = openElfBySoname(soname); ElfInfo info;case DT_JMPREL:info.relplt = reret_casf32_Rel *(info.elf_base + dyn-d_un.d_ptr);break;case DT_PLTRELSZ:info.relpltsz = dyn-d_un.d_val / sizeof(Elf32_Rel); break;case DT_HASH:u32_t *rawd
39、ata = reret_cast(info.elf_base + dyn-d_un.d_ptr);info.nbucket = rawdata0; info.nchain = rawdata1; info.bucket = rawdata + 2;info.chain = info.bucket + info.nbucket; break;/because .dynsym is next to .dynstr, so we can caculate the symsz simplyinfo.symsz = (u32_t)info.symstr - (u32_t)info.sym)/sizeof
40、(Elf32_Sym);最后是测试的代码:typedef(*strlen_fun)(const char *);gefInfoBySegmentView(info, handle); Elf32_Sym *sym = NULL;symidx = 0;findSymByName(info, symbol, &sym, &symidx); if(!sym)LOGE(- Could not find symbol %s, symbol); goto fails;elseLOGI(+ sym %p, symidx %d., sym, symidx);for (i = 0; i info.relpltsz; i+) Elf32_Rel& rel = info.relplti;if (ELF32_R_SYM(rel.r_info) = symidx & ELF32_R_TYPE(rel.r_info) = R_ARM_JUMP_SLOT) void *addr = (void *) (info.elf_base + rel.r_offset); if (replaceFunc(addr, replace_func, old_func)goto fails;/only once break;for (i = 0; i info.reldynsz; i+) Elf32_Rel& rel = in
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024版样机试制合作协议
- 2024年出租车租车合同模板
- 怀孕期间夫妻分手协议
- 城市轨道交通工程建筑信息模型(BIM)分类与编码标准
- 建筑行业标准脚手架租赁合同
- 2024项目投资合同撰写要点
- 购买混凝土合同范本
- 生物中图版自主训练:第三单元第一章第一节遗传物质的发现
- 简单的赔偿协议书2024年
- 2024届毕业生就业协议书样本
- 2023年上海机场集团有限公司校园招聘笔试题库及答案解析
- 镜头的角度和方位课件
- 污水处理常用药剂简介知识讲解课件
- 五年级上册英语课件-Unit 1《My future》第1课时牛津上海版(三起) (共28张PPT)
- 光交接箱施工规范方案
- 气温和降水学案
- 普及人民代表大会制度知识竞赛试题库(1000题和答案)
- 国家电网公司施工项目部标准化管理手册(2021年版)线路工程分册
- 《汽车低压线束设计规范》
- 工程项目增加签证单
- 被一部电影感动记韩国电影《鸣梁海战》观后感
评论
0/150
提交评论