上篇实现了GOT表的手动HOOK,这里需要说明一下,GOT表其实包含了导入表和导出表,导出表指将当前动态库的一些函数符号保留,供外部调用,之前针对HOOK的就是这种类型的函数。而针对导入表,这样的做法显然不行的,导入表中的函数实际是在该动态库中调用外部的导出函数。
导入表在对应在动态链接段.got.plt(DT_PLTGOT)指向处,但是每项的详细是和GOT表中的表项对应的,因此,在解析动态链接段时,需要解析DT_JMPREL、DT_SYMTAB,前者指向了每一个导入表表项的偏移地址和相关信息,包括在GOT表中偏移,后者为GOT表。
HOOK时只需要判断找到导入表表项对应的偏移地址,替换内容为需要跳转的地址即可。
基于此,我实现了一个导入表HOOK的示例,调用ReturnGiveValue函数查看LOG日志即可发现HOOK成功。
#include <jni.h>
#include <dlfcn.h>
#include <elf.h>
#include <android/log.h>
#include <sys/mman.h>
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "ror_got_hook", __VA_ARGS__))
#define MEM_PAGE_SIZE 4096
#define MEM_PAGE_MASK (MEM_PAGE_SIZE-1)
#define MEM_PAGE_START(x) ((x) & ~MEM_PAGE_MASK)
#define MEM_PAGE_END(x) MEM_PAGE_START((x) + (MEM_PAGE_SIZE-1))
#define ElfW(type) Elf32_ ## type
char str[] = "return give value";
JNIEXPORT void JNICALL ReturnGiveValue()
{
char logStr[255] = {0};
strncpy(logStr,str,strlen(str));
LOGD("%x",(unsigned int)strncpy);
LOGD("show : %s",logStr);
}
int my_strncpy(char* s1, char* s2, int n)
{
memcpy(s1,"return fake value\0",18);
LOGD("%x",(unsigned int)strncpy);
return 1;
}
int __attribute__((constructor)) gothook()
{
//获取动态库基值
void* testlib = dlopen("/data/local/tmp/libtest.so",RTLD_LAZY);
int nBase = *(int*)(testlib+0x8C);
LOGD("nBase : %x",nBase);
//计算program header table实际地址
ElfW(Ehdr) *header = (ElfW(Ehdr)*)(nBase);
if (memcmp(header->e_ident, "\177ELF", 4) != 0) {
return 0;
}
ElfW(Phdr)* phdr_table = (ElfW(Phdr)*)(nBase + header->e_phoff);
if (phdr_table == 0)
{
LOGD("phdr_table address : 0");
return 0;
}
size_t phdr_count = header->e_phnum;
LOGD("phdr_count : %d", phdr_count);
//遍历program header table,ptype等于PT_DYNAMIC即为dynameic,获取到p_offset
unsigned long dynamicAddr = 0;
unsigned int dynamicSize = 0;
for (int i = 0; i < phdr_count; i++)
{
if (phdr_table[i].p_type == PT_DYNAMIC)
{
dynamicAddr = phdr_table[i].p_vaddr + nBase;
dynamicSize = phdr_table[i].p_memsz;
break;
}
}
LOGD("Dynamic Addr : %x",dynamicAddr);
LOGD("Dynamic Size : %x",dynamicSize);
/*
typedef struct dynamic {
Elf32_Sword d_tag;
union {
Elf32_Sword d_val;
Elf32_Addr d_ptr;
} d_un;
} Elf32_Dyn;
*/
ElfW(Dyn)* dynamic_table = (ElfW(Dyn)*)(dynamicAddr);
unsigned long jmpRelOff = 0;
unsigned long strTabOff = 0;
unsigned long pltRelSz = 0;
unsigned long symTabOff = 0;
int i;
for(i=0;i < dynamicSize / 8;i ++)
{
int val = dynamic_table[i].d_un.d_val;
if (dynamic_table[i].d_tag == DT_JMPREL)
{
jmpRelOff = val;
}
if (dynamic_table[i].d_tag == DT_STRTAB)
{
strTabOff = val;
}
if (dynamic_table[i].d_tag == DT_PLTRELSZ)
{
pltRelSz = val;
}
if (dynamic_table[i].d_tag == DT_SYMTAB)
{
symTabOff = val;
}
}
ElfW(Rel)* rel_table = (ElfW(Rel)*)(jmpRelOff+nBase);
LOGD("jmpRelOff : %x",jmpRelOff);
LOGD("strTabOff : %x",strTabOff);
LOGD("symTabOff : %x",symTabOff);
//遍历查找要hook的导入函数,这里以strncpy做示例
for(i=0;i < pltRelSz / 8;i++)
{
int number = (rel_table[i].r_info >> 8) & 0xffffff;
ElfW(Sym)* symTableIndex = (ElfW(Sym)*)(number*16 + symTabOff + nBase);
char* funcName = (char*)(symTableIndex->st_name + strTabOff + nBase);
// LOGD("Func Name : %s",funcName);
if(memcmp(funcName,"strncpy",7) == 0)
{
void* pstart = (void*)MEM_PAGE_START(((ElfW(Addr))rel_table[i].r_offset + nBase));
mprotect(pstart,MEM_PAGE_SIZE,PROT_READ | PROT_WRITE | PROT_EXEC);
LOGD("r_off : %x",rel_table[i].r_offset + nBase);
LOGD("my_strncpy : %x",my_strncpy);
*(unsigned int*)(rel_table[i].r_offset + nBase) = my_strncpy;
}
}
return 0;
}