一步一步走进Linux HOOK API(七)

来源:互联网 发布:大学生网络文明调查 编辑:程序博客网 时间:2024/06/05 05:20

http://blog.csdn.net/zhejiang9/article/details/8470891


我又来啦,今天是本系列介绍ELF文件的最后一篇教程.跟随大家一起了解了ELF文件的大致结构.整个结构其实是很明朗的,根据ELF,程序头,节头.从节头里提取不同的类型,到不同的节表内去获取不同的信息,今天这里主要介绍重定位表.也是常用的节表之一.

有了符号名和动态库的名字操作系统就可以为我们引入函数了,但在我们的程序中是谁会用这些外部函数呢,系统解析出来的函数地址应该给谁呢?这就是重定位表的功劳了!
重定位表的类型有两种SHT_RELSHT_RELA,我们只谈SHT_REL.

[cpp] view plaincopy
  1. typedef struct  
  2. {  
  3.   Elf32_Addr r_offset; /* Address */  
  4.   Elf32_Word r_info; /* Relocation type and symbol index */  
  5. } Elf32_Rel;  

重定位表其实很简单,它的r_offset成员给出了需要重定位内容的地址,而它的r_info字段给出了两条信息,一条是与此重定位内容相关的符号,一条是重定位的类型,elf.h中分别有

[cpp] view plaincopy
  1. #define ELF32_R_SYM(val) ((val) >> 8)  
  2. #define ELF32_R_TYPE(val) ((val) & 0xff)  
  3. #define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff))  

从成员r_info中获取符号信息和重定位信息,符号信息就是一个符号表的索引,32位下占用这个成员的高24,剩余的8位就是重定位类型了.符号信息就是一个符号表中的索引.

i386上从外部引入的动态函数重定位类型是R_386_JMP_SLOT.由这个类型的名称可以看出动态连接在Linux上是处理器密切相关的东西.刚说r_info字段包含了一个符号表中的索引,对于从外部引入的动态函数来说那个符号表就是动态符号表,它在节头结构中的类型值为SHT_DYNSYMr_offset字段是一个地址,加载之初被r_offset指向的内容——也就是一个外部符号的地址——并不正确,操作系统就根据重定位信息引用的符号找到那个地址,然后修改r_offset所指向的内容。这部分我们在第五讲中已经得到证实,发现GOT表内的地址并不是真正的函数地址入口.这就是因为linux"懒模式"机制.

下面给出代码:

[cpp] view plaincopy
  1. #include "readRel.h"  
  2. void display_rel(Elf32_Ehdr *ehdr,Elf32_Shdr *shdr)  
  3. {  
  4.     Elf32_Rel *dyn = (Elf32_Rel *)((char*)ehdr + shdr->sh_offset);  
  5.     int relSize = shdr->sh_size / shdr->sh_entsize;  
  6.     char  *symName = (char*)(((Elf32_Shdr *)((char*)ehdr + ehdr->e_shoff + shdr->sh_link * sizeof(Elf32_Shdr)))->sh_offset   
  7.                     + (char*)ehdr);  
  8.     printf("rel = 0x%x\n",(char*)dyn);  
  9.     printf("relSize = 0x%x\n",(char*)relSize);  
  10.     printf("symName = 0x%x\n",(char*)symName);  
  11.     printf("Relocation section '.rel.dyn' at offset 0x%x contains %d entries:\n",dyn,relSize);  
  12.     int i = 0;  
  13.     printf("%-10s%-10s%-10s%s\n","Offset","Info","Type","Sym.Name");  
  14.     for(i = 0;i < relSize;i++){  
  15.         printf("%-10x",dyn->r_offset);  
  16.         printf("%-10x",dyn->r_info);  
  17.         printf("%-10d",ELF32_R_TYPE(dyn->r_info));  
  18.         printf("%d",ELF32_R_SYM(dyn->r_info));   //注意此处并非字符串的名字,而是指向符号表的索引  
  19.         printf("\n");  
  20.         dyn++;  
  21.     }  
  22. }  
  23. void displayRel(Elf32_Ehdr *ehdr,Elf32_Shdr *shdr)  
  24. {  
  25.     int py = ehdr->e_shstrndx * sizeof(Elf32_Shdr);    
  26.     Elf32_Shdr *symtab = (Elf32_Shdr *)((char*)shdr + py);  
  27.     char *szShdrName = (char*)(symtab->sh_offset + (char*)ehdr);  
  28.     int i = 0;  
  29.     for(i = 0; i < ehdr->e_shnum; i++){  
  30.         if(shdr->sh_type == SHT_REL){  
  31.             display_rel(ehdr,shdr);  
  32.         }  
  33.         shdr++;  
  34.     }  
  35. }  


前面我们讲了,怎么手动通过GDB拦截printf参数,下一节,我们将通过程序来拦截printf的参数,使之永远都输入我们给定的字符串.

尽情期待...谢谢~~~~

我也是菜鸟.一起努力学习.

再见


原创粉丝点击