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

时间:2022-01-25 15:46:38

一步一步走进Linux HOOK API() 

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

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

typedef struct
{
  Elf32_Addr r_offset; /* Address */
  Elf32_Word r_info; /* Relocation type and symbol index */
} Elf32_Rel;

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

#define ELF32_R_SYM(val) ((val) >> 8)
#define ELF32_R_TYPE(val) ((val) & 0xff)
#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"懒模式"机制.

下面给出代码:

#include "readRel.h"
void display_rel(Elf32_Ehdr *ehdr,Elf32_Shdr *shdr)
{
	Elf32_Rel *dyn = (Elf32_Rel *)((char*)ehdr + shdr->sh_offset);
	int relSize = shdr->sh_size / shdr->sh_entsize;
	char  *symName = (char*)(((Elf32_Shdr *)((char*)ehdr + ehdr->e_shoff + shdr->sh_link * sizeof(Elf32_Shdr)))->sh_offset 
					+ (char*)ehdr);
	printf("rel = 0x%x\n",(char*)dyn);
	printf("relSize = 0x%x\n",(char*)relSize);
	printf("symName = 0x%x\n",(char*)symName);
	printf("Relocation section '.rel.dyn' at offset 0x%x contains %d entries:\n",dyn,relSize);
	int i = 0;
	printf("%-10s%-10s%-10s%s\n","Offset","Info","Type","Sym.Name");
	for(i = 0;i < relSize;i++){
		printf("%-10x",dyn->r_offset);
		printf("%-10x",dyn->r_info);
		printf("%-10d",ELF32_R_TYPE(dyn->r_info));
		printf("%d",ELF32_R_SYM(dyn->r_info));   //注意此处并非字符串的名字,而是指向符号表的索引
		printf("\n");
		dyn++;
	}
}
void displayRel(Elf32_Ehdr *ehdr,Elf32_Shdr *shdr)
{
	int py = ehdr->e_shstrndx * sizeof(Elf32_Shdr);	
	Elf32_Shdr *symtab = (Elf32_Shdr *)((char*)shdr + py);
	char *szShdrName = (char*)(symtab->sh_offset + (char*)ehdr);
	int i = 0;
	for(i = 0; i < ehdr->e_shnum; i++){
		if(shdr->sh_type == SHT_REL){
			display_rel(ehdr,shdr);
		}
		shdr++;
	}
}


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

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

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

再见