如果获取库函数的临时变量?

inline-hook的本质是加跳转指令,在x86下实现比较简单,一般用push $addr; ret即可

这条指定的含义是: 将指定的地址压栈,然后返回到该地址,从而实现hook


然后在指定函数内的hook,需要维持堆栈平衡,因为假设hooked之后想要跳回函数执行,但当前的上下文已经改变,这时候若跳回去,可能会引发crash

这里假设有下面这样的代码:

#include "test.h"
#include <stdio.h>
#include <dlfcn.h>

#define libc_path "/lib/i386-linux-gnu/libc.so.6"

static int do_add(int a, int b) {
	printf("in do_addr, a = %d, b = %d\n",a, b);
	return a + b;
}

static void *g_handle;

int add(int a, int b) {
	puts("-----------------add-----------------");
	void *handle = dlopen(libc_path, RTLD_LAZY);
	if (handle != NULL) {
		g_handle = handle;
	}
	printf("in add handle = %p\n",(void *)handle);
	long addr;
	__asm__("movl %%esp,%0"::"g"(addr));
	printf("esp = 0x%lx\n",addr);

	printf("hello,world\n");
	int ret =  do_add(a, b);
	printf("on ret = %d\n", ret);
	return ret;
}

假设我们比较关心handle的值,想将它导出到外面使用。默认在C的语义下,你是没办法获取到该指针的,但是inline-hook可以。

我们的思路是,找到dlopen调用之后的指令: call dlopen@plt

将其强制跳转到指定函数:比如test。 然后从eax中拿出返回值,然后将上下文保存下来,再跳回到原先中断的指令,具体test方法的代码如下:

.text
.global test
test:
	pushl %eax
	pushl %ebx
	movl %eax,%ebx
	movl %ebx,handle
	pusha
	pushf
	call copy
	popf
	popa
	popl %ebx
	popl %eax
	jmp *next_call_dlopen_addr

那么如何找到call dlopen@这个地址呢?

我们需要解析ELF文件结构了,很显然,dlopen是属于libc.so下的,那么我们遍历plt结构即可:

static Elf32_Addr get_func_plt_addr(const char *lib_path, const char *func_name) {
	Elf32_Ehdr *ehdr;
	Elf32_Shdr *shdr;
	Elf32_Phdr *phdr;
	uint8_t *mem;
	int fd;
	struct stat st;
	int i, j;

	if ( (fd = open(lib_path, O_RDONLY)) < 0) {
		perror("open");
		exit(1);
	}

	if (fstat(fd, &st) < 0) {
		perror("fstat");
		exit(1);
	}

	mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
	if (mem == MAP_FAILED) {
		perror("mmap");
		exit(1);
	}

	if (mem[0] != 0x7f && strcmp(&mem[1], "ELF")) {
		fprintf(stderr,"It's not an ELF\n");
		exit(1);
	}

	ehdr = (Elf32_Ehdr *)mem;
	shdr = (Elf32_Shdr *)(mem + ehdr->e_shoff);
	phdr = (Elf32_Phdr *)(mem + ehdr->e_phoff);
	const char *shstrtab = (const char *)&mem[shdr[ehdr->e_shstrndx].sh_offset];

	Elf32_Sym *symtab;
	const char *strtab;
	Elf32_Addr plt_addr;
	Elf32_Off dlopen_off;

	Elf32_Rel *rel;
	int k;
	const char *sh_name;
	int rel_index;
	for (i = 0; i < ehdr->e_shnum; i++) {
		sh_name = &shstrtab[shdr[i].sh_name];
		if (strcmp(sh_name,".plt") == 0) {
			plt_addr = shdr[i].sh_addr;
		}
		if (strcmp(sh_name,".rel.plt") == 0) {
			rel = (Elf32_Rel *)&mem[shdr[i].sh_offset];
			Elf32_Rel *relp = rel;
			for (k = 0; k < shdr[i].sh_size / sizeof(Elf32_Rel); k++) {
				if (ELF32_R_SYM(relp->r_info) == j) {
					rel_index = relp - rel;
				}
				relp++;
			}
		}
		if (shdr[i].sh_type == SHT_SYMTAB || shdr[i].sh_type == SHT_DYNSYM) {
			symtab = (Elf32_Sym *)&mem[shdr[i].sh_offset];
			strtab = (const char *)&mem[shdr[shdr[i].sh_link].sh_offset];
			for (j = 0; j < shdr[i].sh_size / sizeof(Elf32_Sym); j++) {
				if (strcmp(&strtab[symtab->st_name],func_name) == 0) {
					dlopen_off = symtab->st_value;
					break;
				}
				symtab++;
			}
		}
	}

	Elf32_Addr result = (rel_index + 1) * 16 + plt_addr; // 注意: 每个plt 16字节且加上plt0 

	return result + lib_base_addr;

}

注意:在plt下,每项都占16个字节。而rel_index不包括plt0的占位,故相对地址偏移为: (rel_index + 1) * 16 + plt_base_address

我们知道call指令占5个字节,而call的实际编码为e8 (当前指令的地址 + 5)- dlopen@plt的地址,这样,就可以写出下列搜索代码:

long search(long start_addr,long call_path) {
	// 0x460 e8 offset
	int i = 0;
	const char *func = (const char *)start_addr;
	long dst_path;
	long offset;
	long mem_off;

	call_path = call_path - lib_base_addr;
	while (1) {
		if ((func[i] & 0xff) == 0xe8) { // call
			// call_path = 0x460
			// dst_path = start_addr + i + 5;
			// offset = call_path - dst_path
			dst_path = i + 5 + start_addr;
			dst_path = dst_path - lib_base_addr;
			offset = call_path - dst_path;
			mem_off = *(long *)&func[i + 1];

			if (mem_off == offset) {
				printf("-----------find mem_off = 0x%lx---------------\n", (unsigned long)mem_off);
				break;
			}
		}
		++i;
	}
	return start_addr + i;
}

最后完整的代码见: https://github.com/liuyx/inline-hook




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值