AllHookInOne 中的hook基本流程

一、导入表(GOT HOOK)

熟悉 ELF 结构的读者都知道,SO引用外部函数的时候,在编译时会将外部函数的地址以 Stub 的形式存放在.GOT 表中,加载时 linker 再进行重定位,即将真实的外部函数写到此 stub 中。

HOOK 的思路就是:替换GOT表中的外部函数地址。可以理解为hook导入函数。

具体流程:

1.注入进程

2.可能有读者想到马上就是读取并解析 SO 的结构,找到外部函数对应在GOT 表中的存放地址。在 http://bbs.pediy.com/showthread.php?t=194053中已经讨论 dlopen 返回的是 solist,已经包含 SO 信息。(直接通过 SOLIST 实现替换 HOOK,代码量就很小了)

导入表 HOOK 的实现是最简单的了,但也不难看出,导入表的 HOOK 功能是很有限的。

例举两点:

1. 导入表 HOOK 对进程通过 dlopen 动态获得并调用外部符号是无效的。

2. 导入表 HOOK 只能影响被注入进程。

 

二、AllHookInOne基本流程

主要原理:通过解析映射到内存中的elf的结构,解析出got,然后进行hook重定位替换。其中必须要基于执行视图(Execution View)进行符号解析;

ELF文件格式是基于链接视图(Linking View,链接视图是基于节(Section)对ELF进行解析的。然而动态链接库在加载的过程中,linker只关注ELF中的段(Segment)信息。因此ELF中的节信息被完全篡改或者甚至删除掉,并不会影响linker的加载过程,这样做可以防止静态分析工具(比如IDAreadelf等)对其进行分析,一般加过壳的ELF文件都会有这方面的处理。

对于这种ELF文件,如果要实现hook功能,则必须要基于执行视图(Execution View)进行符号解析;

 

AllHookInOne主要流程方法有:

1从给定的so中获取基址,获取so句柄ElfHandle

ElfHandle* handle = openElfBySoname(soname);


2segment视图获取elf信息(即加载到内存的so)

getElfInfoBySegmentView(info, handle);


3根据符号名寻找函数地址Sym

findSymByName(info, symbol, &sym, &symidx);

 

4遍历链表,进行一次替换relplt表函数地址操作,其中需要使用mprotect修改访问内存,然后调用系统指令 清除缓存

replaceFunc(addr, replace_func, old_func)

 

5遍历链表,进行一次替换reldyn表函数地址操作,其中需要使用mprotect修改访问内存,然后调用系统指令 清除缓存

replaceFunc(addr, replace_func, old_func))

 

6、释放资源,关闭elf句柄  closeElfBySoname(handle);

参考:https://github.com/boyliang/AllHookInOne

/*
 * elfhook.cpp
 *
 *  Created on: 2014年10月9日
 *      Author: boyliang
 */

#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/syscall.h>

#include "common.h"
#include "elfutils.h"
#include "elfio.h"


#define PAGE_START(addr) (~(getpagesize() - 1) & (addr))

//使用mprotect修改访问内存
static int modifyMemAccess(void *addr, int prots){
	void *page_start_addr = (void *)PAGE_START((uint32_t)addr);
	return mprotect(page_start_addr, getpagesize(), prots);
}

//调用系统指令 清除缓存
static int clearCache(void *addr, size_t len){
	void *end = (uint8_t *)addr + len;
	syscall(0xf0002, addr, end);
}

//替换函数地址。
static int replaceFunc(void *addr, void *replace_func, void **old_func){
	int res = 0;

	if(*(void **)addr == replace_func){
		LOGW("addr %p had been replace.", addr);
		goto fails;
	}

	if(!*old_func){
		*old_func = *(void **)addr;
	}

	if(modifyMemAccess((void *)addr, PROT_EXEC|PROT_READ|PROT_WRITE)){
		LOGE("[-] modifymemAccess fails, error %s.", strerror(errno));
		res = 1;
		goto fails;
	}

	*(void **)addr = replace_func;
	clearCache(addr, getpagesize());
	LOGI("[+] old_func is %p, replace_func is %p, new_func %p.", *old_func, replace_func, *(uint32_t *)addr);

	fails:
	return res;
}

#define R_ARM_ABS32 0x02
#define R_ARM_GLOB_DAT 0x15
#define R_ARM_JUMP_SLOT 0x16

//http://blog.csdn.net/L173864930/article/details/40507359
int elfHook(const char *soname, const char *symbol, void *replace_func, void **old_func){
	assert(old_func);
	assert(replace_func);
	assert(symbol);

	//从给定的so中获取基址,获取so句柄ElfHandle
	ElfHandle* handle = openElfBySoname(soname);
	ElfInfo info;
	//从segment视图获取elf信息(即加载到内存的so)
	getElfInfoBySegmentView(info, handle);


	Elf32_Sym *sym = NULL;
	int symidx = 0;

	//根据符号名寻找函数地址Sym
	findSymByName(info, symbol, &sym, &symidx);

	if(!sym){
		LOGE("[-] Could not find symbol %s", symbol);
		goto fails;
	}else{
		LOGI("[+] sym %p, symidx %d.", sym, symidx);
	}

	for (int i = 0; i < info.relpltsz; i++) {
		Elf32_Rel& rel = info.relplt[i];
		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);
			//进行一次替换relplt表函数地址操作,其中需要使用mprotect修改访问内存,然后调用系统指令 清除缓存
			if (replaceFunc(addr, replace_func, old_func))
				goto fails;

			//only once
			break;
		}
	}

	for (int i = 0; i < info.reldynsz; i++) {
		Elf32_Rel& rel = info.reldyn[i];
		if (ELF32_R_SYM(rel.r_info) == symidx &&
				(ELF32_R_TYPE(rel.r_info) == R_ARM_ABS32
						|| ELF32_R_TYPE(rel.r_info) == R_ARM_GLOB_DAT)) {

			void *addr = (void *) (info.elf_base + rel.r_offset);
			//进行一次替换reldyn表函数地址操作,其中需要使用mprotect修改访问内存,然后调用系统指令 清除缓存
			if (replaceFunc(addr, replace_func, old_func))
				goto fails;
		}
	}

	fails:
	closeElfBySoname(handle);//释放资源,关闭elf句柄
	return 0;
}


 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值