基于Android的ELF PLT/GOT符号重定向过程及ELF Hook实现

转自:http://blog.csdn.net/l173864930/article/details/40507359

引言

写这篇技术文的原因,主要有两个:

  • 其一是发现网上大部分描述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_SLOTR_ARM_ABS32R_ARM_GLOB_DAT,而我们要hook elf函数,则需要同时处理好这三种重定向类型。

例子

先看示例代码

[cpp] view plaincopy

  1. typedef int (*strlen_fun)(const char *);
  2. strlen_fun global_strlen1 = (strlen_fun)strlen;
  3. strlen_fun global_strlen2 = (strlen_fun)strlen;
  4. #define SHOW(x) LOGI("%s is %d", #x, x)
  5. extern "C" jint Java_com_example_allhookinone_HookUtils_elfhook(JNIEnv *env, jobject thiz){
  6. const char *str = "helloworld";
  7. strlen_fun local_strlen1 = (strlen_fun)strlen;
  8. strlen_fun local_strlen2 = (strlen_fun)strlen;
  9. int len0 = global_strlen1(str);
  10. int len1 = global_strlen2(str);
  11. int len2 = local_strlen1(str);
  12. int len3 = local_strlen2(str);
  13. int len4 = strlen(str);
  14. int len5 = strlen(str);
  15. SHOW(len0);
  16. SHOW(len1);
  17. SHOW(len2);
  18. SHOW(len3);
  19. SHOW(len4);
  20. SHOW(len5);
  21. return 0;
  22. }

这段代码分别以三种不同的方式调用strlen,分别是全局函数指针、局部函数指针以及直接调用,下而我们针对这个例子,分别对三种调用分析进行分析。

先通过readelf,我们查看一下重定向表,如下所示:

[plain] view plaincopy

  1. Relocation section ‘.rel.dyn‘ at offset 0x2a48 contains 17 entries:
  2. Offset     Info    Type            Sym.Value  Sym. Name
  3. 0000ade0  00000017 R_ARM_RELATIVE
  4. 0000af00  00000017 R_ARM_RELATIVE
  5. 0000af0c  00000017 R_ARM_RELATIVE
  6. 0000af10  00000017 R_ARM_RELATIVE
  7. 0000af18  00000017 R_ARM_RELATIVE
  8. 0000af1c  00000017 R_ARM_RELATIVE
  9. 0000af20  00000017 R_ARM_RELATIVE
  10. 0000af24  00000017 R_ARM_RELATIVE
  11. 0000af28  00000017 R_ARM_RELATIVE
  12. 0000af30  00000017 R_ARM_RELATIVE
  13. 0000aefc  00003215 R_ARM_GLOB_DAT    00000000   __stack_chk_guard
  14. 0000af04  00003715 R_ARM_GLOB_DAT    00000000   __page_size
  15. 0000af08  00004e15 R_ARM_GLOB_DAT    00000000   strlen
  16. 0000b004  00004e02 R_ARM_ABS32       00000000   strlen
  17. 0000b008  00004e02 R_ARM_ABS32       00000000   strlen
  18. 0000af14  00006615 R_ARM_GLOB_DAT    00000000   __gnu_Unwind_Find_exid
  19. 0000af2c  00007415 R_ARM_GLOB_DAT    00000000   __cxa_call_unexpected
  20. ...
  21. ...
  22. Relocation section ‘.rel.plt‘ at offset 0x2ad0 contains 48 entries:
  23. Offset     Info    Type            Sym.Value  Sym. Name
  24. 0000af40  00000216 R_ARM_JUMP_SLOT   00000000   __cxa_atexit
  25. 0000af44  00000116 R_ARM_JUMP_SLOT   00000000   __cxa_finalize
  26. 0000af48  00001716 R_ARM_JUMP_SLOT   00000000   memcpy
  27. ...
  28. 0000afd4  00004c16 R_ARM_JUMP_SLOT   00000000   fgets
  29. 0000afd8  00004d16 R_ARM_JUMP_SLOT   00000000   fclose
  30. 0000afdc  00004e16 R_ARM_JUMP_SLOT   00000000   strlen
  31. 0000afe0  00004f16 R_ARM_JUMP_SLOT   00000000   strncmp
  32. ...
  33. ...

在.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 0000AFDC R_ARM_JUMP_SLOT

在代码中,我们一共调用了6次strlen,但为什么只出现了4次呢?另外,它们之间又是如何对应的呢,带着这些问题去分析汇编代码。把编译出来的so拖到IDA,我们看到示例代码的指令:

[plain] view plaincopy

  1. .text:000050BC                 EXPORT Java_com_example_allhookinone_HookUtils_elfhook
  2. .text:000050BC Java_com_example_allhookinone_HookUtils_elfhook
  3. .text:000050BC
  4. .text:000050BC var_40          = -0x40
  5. .text:000050BC var_38          = -0x38
  6. .text:000050BC var_34          = -0x34
  7. .text:000050BC s               = -0x2C
  8. .text:000050BC var_28          = -0x28
  9. .text:000050BC var_24          = -0x24
  10. .text:000050BC var_20          = -0x20
  11. .text:000050BC var_1C          = -0x1C
  12. .text:000050BC var_18          = -0x18
  13. .text:000050BC var_14          = -0x14
  14. .text:000050BC var_10          = -0x10
  15. .text:000050BC var_C           = -0xC
  16. .text:000050BC
  17. .text:000050BC                 PUSH            {R4,LR}
  18. .text:000050BE                 SUB             SP, SP, #0x38
  19. .text:000050C0                 STR             R0, [SP,#0x40+var_34]
  20. .text:000050C2                 STR             R1, [SP,#0x40+var_38]
  21. .text:000050C4                 LDR             R4, =(_GLOBAL_OFFSET_TABLE_ - 0x50CA)
  22. .text:000050C6                 ADD             R4, PC ; _GLOBAL_OFFSET_TABLE_
  23. .text:000050C8                 LDR             R3, =(aHelloworld - 0x50CE)
  24. .text:000050CA                 ADD             R3, PC  ; "helloworld"
  25. .text:000050CC                 STR             R3, [SP,#0x40+s]
  26. .text:000050CE                 LDR             R3, =(strlen_ptr - 0xAF34)
  27. .text:000050D0                 LDR             R3, [R4,R3] ; __imp_strlen
  28. .text:000050D2                 STR             R3, [SP,#0x40+var_28]
  29. .text:000050D4                 LDR             R3, =(strlen_ptr - 0xAF34)
  30. .text:000050D6                 LDR             R3, [R4,R3] ; __imp_strlen
  31. .text:000050D8                 STR             R3, [SP,#0x40+var_24]
  32. .text:000050DA                 LDR             R3, =(global_strlen1_ptr - 0xAF34)
  33. .text:000050DC                 LDR             R3, [R4,R3] ; global_strlen1
  34. .text:000050DE                 LDR             R3, [R3]
  35. .text:000050E0                 LDR             R2, [SP,#0x40+s]
  36. .text:000050E2                 MOVS            R0, R2
  37. .text:000050E4                 BLX             R3
  38. .text:000050E6                 MOVS            R3, R0
  39. .text:000050E8                 STR             R3, [SP,#0x40+var_20]
  40. .text:000050EA                 LDR             R3, =(global_strlen2_ptr - 0xAF34)
  41. .text:000050EC                 LDR             R3, [R4,R3] ; global_strlen2
  42. .text:000050EE                 LDR             R3, [R3]
  43. .text:000050F0                 LDR             R2, [SP,#0x40+s]
  44. .text:000050F2                 MOVS            R0, R2
  45. .text:000050F4                 BLX             R3
  46. .text:000050F6                 MOVS            R3, R0
  47. .text:000050F8                 STR             R3, [SP,#0x40+var_1C]
  48. .text:000050FA                 LDR             R2, [SP,#0x40+s]
  49. .text:000050FC                 LDR             R3, [SP,#0x40+var_28]
  50. .text:000050FE                 MOVS            R0, R2
  51. .text:00005100                 BLX             R3
  52. .text:00005102                 MOVS            R3, R0
  53. .text:00005104                 STR             R3, [SP,#0x40+var_18]
  54. .text:00005106                 LDR             R2, [SP,#0x40+s]
  55. .text:00005108                 LDR             R3, [SP,#0x40+var_24]
  56. .text:0000510A                 MOVS            R0, R2
  57. .text:0000510C                 BLX             R3
  58. .text:0000510E                 MOVS            R3, R0
  59. .text:00005110                 STR             R3, [SP,#0x40+var_14]
  60. .text:00005112                 LDR             R3, [SP,#0x40+s]
  61. .text:00005114                 MOVS            R0, R3  ; s
  62. .text:00005116                 BLX             strlen
  63. .text:0000511A                 MOVS            R3, R0
  64. .text:0000511C                 STR             R3, [SP,#0x40+var_10]
  65. .text:0000511E                 LDR             R3, [SP,#0x40+s]
  66. .text:00005120                 MOVS            R0, R3  ; s
  67. .text:00005122                 BLX             strlen
  68. .text:00005126                 MOVS            R3, R0
  69. ...
  70. ...
  71. .text:000051CA                 ADD             SP, SP, #0x38
  72. .text:000051CC                 POP             {R4,PC}
  73. .text:000051CC ; End of function Java_com_example_allhookinone_HookUtils_elfhook

先把几个重要的地址找出来,它们分别是

  • GLOBAL_OFFSET_TABLE: 0x0000AF34
  • strlen_ptr: 0x0000AF08
  • __imp_strlen: 0x0000B0C8
  • global_strlen1_ptr: 0x0000AF0C
  • global_strlen1: 0x0000B004
  • global_strlen2_ptr: 0x0000AF10
  • global_strlen2: 0x0000B008

全局函数指针调用外部函数

global_strlen1和global_strlen2的调用,对应0x000050E4和0x000050F4两处的BLX指令,通过计算最终R3的值分别是*global_strlen1和*global_strlen2,而global_strlen1和global_strlen2的值正好对应位于.rel.dyn的两个R_ARM_ABS32的重定位项,因此我们得出结论:通过全局函数指针的方式调用外部函数,它的重定位类型是R_ARM_ABS32,并且位于.rel.dyn节区

我们只分析global_strlen1的调用过程,首先定位到global_strlen1_ptr(0x0000AF0C),该地址位于.got节区,GLOBAL_OFFSET_TABLE的上方。然后再通过global_strlen1_ptr定位到0x0000B004(位于.data节区),最后再通过0x0000B004定位到最终的函数地址,因此R_ARM_ABS32重定位项的Offset指向最终调用函数地址的地址(也就是函数指针的指针),整个重定位过程是先位到.got,再从.got定位到.date。下面是.got段区的16进制表示片段:

[plain] view plaincopy

  1. ...
  2. 0000AF0C  04 B0 00 00 08 B0 00 00  DC B0 00 00 B4 87 00 00
  3. 0000AF1C  F4 84 00 00 60 5B 00 00  58 5B 00 00 50 5B 00 00
  4. 0000AF2C  EC B0 00 00 FC 8C 00 00  00 00 00 00 00 00 00 00
  5. ...
  6. 0000B004  C8 B0 00 00 C8 B0 00 00  ?? ?? ?? ?? ?? ?? ?? ??
  7. 0000B014  ?? ?? ?? ?? ?? ?? ?? ??  ?? ?? ?? ?? ?? ?? ?? ??
  8. 0000B024  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
  9. ...
  10. 0000B0C8  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
  11. 0000B0D8  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
  12. ...

最后发现0x0000B0C8地址片的指令全为0,当动态链接时,linker会覆盖0x0000B004地址的值,指向strlen的真正地址(而不是现在的0x0000B0C8,有点绕)。

局部函数指针调用外部函数

local_strlen1和local_strlen2的调用,对应0x00005100和0x0000510C两处的BLX指令,通过计算最终R3的值都是*strlen_prt,即0x0000AF08,正好对应位于.rel.dyn中的R_ARM_GLOB_DAT重定位项,因此我们得出结论:通过局部函数指针方式调用外部函数,它的重定位类型是R_ARM_GLOB_DAT,并且位于.re.dyn节区

我们只分析local_strlen1的调用过程,首先是定位到strlen_prt(0x0000AF08),该地址位于.got节区,GLOBAL_OFFSET_TABLE的上方,然后再通过strlen_prt,定位到0x0000B0C8,跟上面分析的结果居然一样,因此R_ARM_GLOB_DAT的重定项Offset指向最终调用函数地址的地址(也就是函数指针的指针),下面是.got段区的16进制表示片段:

[plain] view plaincopy

  1. 0000AF08  C8 B0 00 00 04 B0 00 00  08 B0 00 00 DC B0 00 00
  2. 0000AF18  B4 87 00 00 F4 84 00 00  60 5B 00 00 58 5B 00 00
  3. 0000AF28  50 5B 00 00 EC B0 00 00  FC 8C 00 00 00 00 00 00
  4. ...
  5. 0000B0C8  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
  6. 0000B0D8  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
  7. ...

需要注意的是,0x000050D8的指令“STR R3, [SP,#0x40+var_24]”,这里已经把函数的真实地址保存到堆栈了,因此哪怕我们修改了GOT表也不会影响堆栈的值,因此这种重定位类型无法通过修改地址进行hook

直接调用外部函数

最后看看strlen的直接调用,对应0x0000511A和0x00005122两处的BLX指令,最后它们都指向.plt节区指令,如下所示:

.plt:00002E38                 ADR             R12, 0x2E40
.plt:00002E3C                 ADD             R12, R12, #0x8000
.plt:00002E40                 LDR             PC, [R12,#(strlen_ptr_0 - 0xAE40)]! ; __imp_strlen
...
0000AFDC  C8 B0 00 00 CC B0 00 00  D0 B0 00 00 D4 B0 00 00 
0000AFEC  D8 B0 00 00 DC B0 00 00  E0 B0 00 00 E4 B0 00 00 
0000AFFC  E8 B0 00 00 00 00 00 00  C8 B0 00 00 C8 B0 00 00 
...

最后,PC指向*strlen_ptr_0,即strlen_ptr_0的地址0x0000AFDC,该地址位于.got节区,而0x0000AFDC地址值的正好是0x0000B0C8,多么熟悉的身影。因此得到结论,直接调用外部函数,它的重定位类型是R_ARM_JUMP_SLOT,并且位于.re.plt节区,其Offset指向最终调用函数地址的地址(也就是函数指针的指针)。整个过程是先到.plt,再到.got,最后才定位到真正的函数地址。

关于这部分的分析,发现IDA和objdump的反编译结果有些差异,下面是通过objdump到的汇编指令:

[plain] view plaincopy

  1. 00002e38 <[email protected]>:
  2. 2e38:   e28fc600    add ip, pc, #0, 12
  3. 2e3c:   e28cca08    add ip, ip, #8, 20  ; 0x8000
  4. 2e40:   e5bcf19c    ldr pc, [ip, #412]! ; 0x19c
  5. ...
  6. ...
  7. afd8:   00002c50    andeq   r2, r0, r0, asr ip
  8. afdc:   00002c50    andeq   r2, r0, r0, asr ip
  9. afe0:   00002c50    andeq   r2, r0, r0, asr ip
  10. afe4:   00002c50    andeq   r2, r0, r0, asr ip

见到afdc处的地址,指向的是0x00002c50,而0x00002c50正好是PLT[0],指令如下:

[plain] view plaincopy

  1. 00002c50 <[email protected]>:
  2. 2c50:   e52de004    push    {lr}        ; (str lr, [sp, #-4]!)
  3. 2c54:   e59fe004    ldr lr, [pc, #4]    ; 2c60 <[email protected]>
  4. 2c58:   e08fe00e    add lr, pc, lr
  5. 2c5c:   e5bef008    ldr pc, [lr, #8]!
  6. 2c60:   000082d4    ldrdeq  r8, [r0], -r4

执行2c5c处指令后,最终pc指向0x0000af3c,正好是GLOBAL_OFFSET_TABLE + 8,即GOT[2],我们看到0x0000af3c处:

[plain] view plaincopy

  1. 0000AF3C  00 00 00 00 28 B0 00 00  24 B0 00 00 2C B0 00 00
  2. 0000AF4C  30 B0 00 00 34 B0 00 00  38 B0 00 00 3C B0 00 00
结果发现GOT[2]里指向的函数地址居然是0,这是因为android上的符号绑定并不支持lazy绑定,所以当so被加载时,linker会预先把GOT[n](n>=2)的所对应的函数都提前找出来,因此这里GOT[2]的代码实际上不会被执行,因此在目前的Android上,并不存在完整的PLT/GOT链接过程。猜想这主要是出于稳定性考虑的。

总结

虽然IDA和obudump两个工具反编译得出的指令在PLT\GOT过程中有些差别,但对于Android而言,其实这个差异不会造成影响,因为Android上不支持lazy绑定。同时我们得出一个非常重要的结论:R_ARM_ABS32、R_ARM_GLOB_DAT和R_ARM_JUMP_SLOT的重定位项虽然在代码中用法不一样,但其offset都是指向一个函数的指针的指针,这个对于我们下面进行elfhook非常有用。

基于执行视图解析ELF

《Redirecting functions in shared ELF libraries》这篇文章所提供的例子,就是基于链接视图对ELF进行解析的,与基于执行视图进行解析相比,后面的逻辑基本是一样的,关键是要通过segment找到.dynsym、.dynstr、.rel.plt和rel.dyn,以及它们的项数。

首次通过Program Header Table找到类型为PT_DYNAMIC的段,该的内容其实对应.dynamic,这段的内容对应Elf32_Dyn类型的数组,其结构体如下所示:

[cpp] view plaincopy

  1. /* Dynamic structure */
  2. typedef struct {
  3. Elf32_Sword d_tag;      /* controls meaning of d_val */
  4. union {
  5. Elf32_Word  d_val;  /* Multiple meanings - see d_tag */
  6. Elf32_Addr  d_ptr;  /* program virtual address */
  7. } d_un;
  8. } Elf32_Dyn;

通过遍历这个数组,我们可以找到所有的需要的信息,我把它们的对应关系列出来:

  • DT_HASH -> .hash
  • DT_SYMTAB & DT_SYMENT -> .dynsym
  • DT_STRTAB & DT_STRSZ -> .dynstr
  • PLTREL(决定REL还是RELA) &(DT_REL | DT_RELA) & (DT_RELSZ | DT_RELASZ ) & (DT_RELENT | DT_RELAENT ) -> .rel.dyn
  • DT_JMPREL & DT_PLTRELSZ & (DT_RELENT | DT_RELAENT) -> .rel.plt
  • FINI_ARRAY & FINI_ARRAYSZ -> .fini_array
  • INIT_ARRAY & INIT_ARRAYSZ -> .init_array

这是查找的相关代码:

[cpp] view plaincopy

  1. void getElfInfoBySegmentView(ElfInfo &info, const ElfHandle *handle){
  2. info.handle = handle;
  3. info.elf_base = (uint8_t *) handle->base;
  4. info.ehdr = reinterpret_cast<Elf32_Ehdr *>(info.elf_base);
  5. // may be wrong
  6. info.shdr = reinterpret_cast<Elf32_Shdr *>(info.elf_base + info.ehdr->e_shoff);
  7. info.phdr = reinterpret_cast<Elf32_Phdr *>(info.elf_base + info.ehdr->e_phoff);
  8. info.shstr = NULL;
  9. Elf32_Phdr *dynamic = NULL;
  10. Elf32_Word size = 0;
  11. getSegmentInfo(info, PT_DYNAMIC, &dynamic, &size, &info.dyn);
  12. if(!dynamic){
  13. LOGE("[-] could‘t find PT_DYNAMIC segment");
  14. exit(-1);
  15. }
  16. info.dynsz = size / sizeof(Elf32_Dyn);
  17. Elf32_Dyn *dyn = info.dyn;
  18. for(int i=0; i<info.dynsz; i++, dyn++){
  19. switch(dyn->d_tag){
  20. case DT_SYMTAB:
  21. info.sym = reinterpret_cast<Elf32_Sym *>(info.elf_base + dyn->d_un.d_ptr);
  22. break;
  23. case DT_STRTAB:
  24. info.symstr = reinterpret_cast<const char *>(info.elf_base + dyn->d_un.d_ptr);
  25. break;
  26. case DT_REL:
  27. info.reldyn = reinterpret_cast<Elf32_Rel *>(info.elf_base + dyn->d_un.d_ptr);
  28. break;
  29. case DT_RELSZ:
  30. info.reldynsz = dyn->d_un.d_val / sizeof(Elf32_Rel);
  31. break;
  32. case DT_JMPREL:
  33. info.relplt = reinterpret_cast<Elf32_Rel *>(info.elf_base + dyn->d_un.d_ptr);
  34. break;
  35. case DT_PLTRELSZ:
  36. info.relpltsz = dyn->d_un.d_val / sizeof(Elf32_Rel);
  37. break;
  38. case DT_HASH:
  39. uint32_t *rawdata = reinterpret_cast<uint32_t *>(info.elf_base + dyn->d_un.d_ptr);
  40. info.nbucket = rawdata[0];
  41. info.nchain = rawdata[1];
  42. info.bucket = rawdata + 2;
  43. info.chain = info.bucket + info.nbucket;
  44. break;
  45. }
  46. }
  47. //because .dynsym is next to .dynstr, so we can caculate the symsz simply
  48. info.symsz = ((uint32_t)info.symstr - (uint32_t)info.sym)/sizeof(Elf32_Sym);
  49. }

然而,有一个值我无法通过通过PT_DYNAMIC段得到的,那就是.dynsym的项数,我最后通过变通的方法得到的。由于.dynsym和.dynstr两个节区是相邻的,因此它们两个地址相减,即可得到的.dynsym总长度,再除了sizeof(Elf32_Sym)即可得到.dynsym的项数,如果你有更好的方法,请跟我说说。

ELF Hook

有了上面的介绍之后,写个ELF Hook就很简单的,我把关键代码贴出来:

[cpp] view plaincopy

  1. #define R_ARM_ABS32 0x02
  2. #define R_ARM_GLOB_DAT 0x15
  3. #define R_ARM_JUMP_SLOT 0x16
  4. int elfHook(const char *soname, const char *symbol, void *replace_func, void **old_func){
  5. assert(old_func);
  6. assert(replace_func);
  7. assert(symbol);
  8. ElfHandle* handle = openElfBySoname(soname);
  9. ElfInfo info;
  10. getElfInfoBySegmentView(info, handle);
  11. Elf32_Sym *sym = NULL;
  12. int symidx = 0;
  13. findSymByName(info, symbol, &sym, &symidx);
  14. if(!sym){
  15. LOGE("[-] Could not find symbol %s", symbol);
  16. goto fails;
  17. }else{
  18. LOGI("[+] sym %p, symidx %d.", sym, symidx);
  19. }
  20. for (int i = 0; i < info.relpltsz; i++) {
  21. Elf32_Rel& rel = info.relplt[i];
  22. if (ELF32_R_SYM(rel.r_info) == symidx && ELF32_R_TYPE(rel.r_info) == R_ARM_JUMP_SLOT) {
  23. void *addr = (void *) (info.elf_base + rel.r_offset);
  24. if (replaceFunc(addr, replace_func, old_func))
  25. goto fails;
  26. //only once
  27. break;
  28. }
  29. }
  30. for (int i = 0; i < info.reldynsz; i++) {
  31. Elf32_Rel& rel = info.reldyn[i];
  32. if (ELF32_R_SYM(rel.r_info) == symidx &&
  33. (ELF32_R_TYPE(rel.r_info) == R_ARM_ABS32
  34. || ELF32_R_TYPE(rel.r_info) == R_ARM_GLOB_DAT)) {
  35. void *addr          = (void *) (info.elf_base + rel.r_offset);
  36. if (replaceFunc(addr, replace_func, old_func))
  37. goto fails;
  38. }
  39. }
  40. fails:
  41. closeElfBySoname(handle);
  42. return 0;
  43. }

最后是测试的代码:

[cpp] view plaincopy

  1. typedef int (*strlen_fun)(const char *);
  2. strlen_fun old_strlen = NULL;
  3. size_t my_strlen(const char *str){
  4. LOGI("strlen was called.");
  5. int len = old_strlen(str);
  6. return len * 2;
  7. }
  8. strlen_fun global_strlen1 = (strlen_fun)strlen;
  9. strlen_fun global_strlen2 = (strlen_fun)strlen;
  10. #define SHOW(x) LOGI("%s is %d", #x, x)
  11. extern "C" jint Java_com_example_allhookinone_HookUtils_elfhook(JNIEnv *env, jobject thiz){
  12. const char *str = "helloworld";
  13. strlen_fun local_strlen1 = (strlen_fun)strlen;
  14. strlen_fun local_strlen2 = (strlen_fun)strlen;
  15. int len0 = global_strlen1(str);
  16. int len1 = global_strlen2(str);
  17. int len2 = local_strlen1(str);
  18. int len3 = local_strlen2(str);
  19. int len4 = strlen(str);
  20. int len5 = strlen(str);
  21. LOGI("hook before:");
  22. SHOW(len0);
  23. SHOW(len1);
  24. SHOW(len2);
  25. SHOW(len3);
  26. SHOW(len4);
  27. SHOW(len5);
  28. elfHook("libonehook.so", "strlen", (void *)my_strlen, (void **)&old_strlen);
  29. len0 = global_strlen1(str);
  30. len1 = global_strlen2(str);
  31. len2 = local_strlen1(str);
  32. len3 = local_strlen2(str);
  33. len4 = strlen(str);
  34. len5 = strlen(str);
  35. LOGI("hook after:");
  36. SHOW(len0);
  37. SHOW(len1);
  38. SHOW(len2);
  39. SHOW(len3);
  40. SHOW(len4);
  41. SHOW(len5);
  42. return 0;
  43. }

从打印结果可以发现,local_strlen1和local_strlen2正所上面所说,并没有受影响,但如果函数再次被调用,则生效了,原因不解析了。测试结果就不发了,留给你们试吧。

GitHup地址

完整代码,见https://github.com/boyliang/AllHookInOne.git

时间: 2024-10-13 01:59:59

基于Android的ELF PLT/GOT符号重定向过程及ELF Hook实现的相关文章

ELF格式文件符号表全解析及readelf命令使用方法

http://blog.csdn.net/edonlii/article/details/8779075 1. 读取ELF文件头: $ readelf -h signELF Header:  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00  Class:                                                  ELF64  Data:                            

基于Android设备的 Kali Linux渗透测试教程(内部资料)

试读样章下载地址:http://pan.baidu.com/s/1hqgPu3U 前  言 Kali Linux是业内最知名的安全渗透测试专用操作系统.它的前身就是业界知名的BackTrack操作系统.BackTrack在2013年停止更新,转为Kali Linux.Kali Linux集成了海量渗透测试.攻击等专用工具.通过系统更新,用户可以快速获取最新的各类工具.所以,Kali Linux是渗透测试专业人员的不二选择. 由于渗透目标多样化,使用传统形式的电脑PC或者笔记本并不是很方便.而智能

基于Android平台的i-jetty网站智能农业监控系统

基于android平台i-jetty网站的智能农业监控系统 摘要:传统的监控系统,一般是基于PC的有线通信传输,其有很多不足之处,如功耗较高.布线成本高.难度大,适应性差,可扩展性不强,增加新的通信线路需要再次布线施工,而且维护起来也比较麻烦,一旦线路出问题,需要繁琐的检查.而嵌入式Web监控系统是基于物联网技术,其无线通信技术具有成本低廉.适应性强.扩展性强.信息安全.使用维护简单等优点. 智能农业中,种植大棚是通过大棚内安装温湿度以及光照传感器,来对农作物的环境参数进行实时采集,由Web监控

一个基于Android系统的新闻客户端(一)

一.整体概述 在服务器端,通过对凤凰网的抓取存入数据库,客户端通过向服务器发送请求得到新闻. 服务端用WCF,宿主为window服务,客户端为Java写的安卓程序. 二.客户端 我在eclipse里新建了个Android项目,命名为MyNewClient,eclipse自动生成二个xml布局文件,如图: 其中,fragment_main.xml是新版的布局文件,暂时不会用,把它删掉. 新建xml文件,命名为activity_foot.xml,在这里我们要做app的底部,先上代码: <?xml v

基于 Android NDK 的学习之旅----- C调用Java

http://www.cnblogs.com/luxiaofeng54/archive/2011/08/17/2142000.html 基于 Android NDK 的学习之旅----- C调用Java许多成熟的C引擎要移植到Android 平台上使用 , 一般都会 提供 一些接口, 让Android sdk 和 jdk 实现. 下文将会介绍 C 如何 通过 JNI 层调用 Java 的静态和非静态方法. 1.主要流程 1.  新建一个测试类TestProvider.java a)       

【转】基于 Android NDK 的学习之旅-----数据传输(引用数据类型)

原文网址:http://www.cnblogs.com/luxiaofeng54/archive/2011/08/20/2147086.html 基于 Android NDK 的学习之旅-----数据传输二(引用数据类型)(附源码) 基于 Android NDK 的学习之旅-----数据传输(引用数据类型) 接着上篇文章继续讲.主要关于引用类型的数据传输,本文将介绍字符串传输和自定义对象的传输. 1.主要流程 1.  String 字符串传输 a)         上层定义一个native的方法

【源代码】基于Android和蓝牙的单片机温度採集系统

如需转载请标明出处:http://blog.csdn.net/itas109 QQ技术交流群:129518033 STC89C52单片机通过HC-06蓝牙模块与Android手机通信实例- 基于Android和蓝牙的单片机温度採集系统 整个project下载:http://download.csdn.net/detail/itas109/7539057 当中包含. 1.下位机电路原理图 2.下位机採集温度.控制发送.自己主动纠错代码 3.Android端接收温度并显示代码 文件截图 这个是我当年

基于 Android 的 3D 视频样本代码

作者:Mark Liu 下载样本代码 简介 在Android 中,创建一个能够播放视频剪辑的应用非常简单:创建一个采用 3D 图形平面的游戏应用也非常简单.但是,创建一个能够在 3D 图形对象上播放视频的应用却不容易.本文介绍了我为应对该挑战创建的应用.该应用可在 3D 平面上渲染视频,并支持用户以交互的方式在视频平面上播放. 该应用需要解决三大实施问题: 如何构建代码以支持用户在播放视频时变更 3D 平面? 虽然 Android 中默认的 MediaPlayer 配备了全面的播放操作,但是难以

Android开发自学笔记(基于Android Studio1.3.1)—1.环境搭建

一.引言    本套学习笔记的开发环境是Windows 10 专业版和Android Studio 的最新版1.3.1. Android Studio 是一个Android开发环境,基于IntelliJ IDEA. 类似 Eclipse ADT,Android Studio 提供了集成的 Android 开发工具用于开发和调试.    笔者没有怎么使用过Eclipse做Android学习,但是基于Android Studio类似于VS的项目架构,还是偏爱Android Studio这个IDE,没