AllHookInOne 中的hook基本流程

来源:互联网 发布:推荐系统算法 编辑:程序博客网 时间:2024/04/30 12:11

一、导入表(GOTHOOK)

熟悉 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/40507359int elfHook(const char *soname, const char *symbol, void *replace_func, void **old_func){assert(old_func);assert(replace_func);assert(symbol);//从给定的so中获取基址,获取so句柄ElfHandleElfHandle* handle = openElfBySoname(soname);ElfInfo info;//从segment视图获取elf信息(即加载到内存的so)getElfInfoBySegmentView(info, handle);Elf32_Sym *sym = NULL;int symidx = 0;//根据符号名寻找函数地址SymfindSymByName(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 oncebreak;}}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 0
原创粉丝点击