[《Redirecting functions in shared ELF libraries》](http://www.codeproject.com/Articles/70302/Redirecting-functions-in-shared-ELF-libraries#_Toc257815978)这篇文章所提供的例子,就是基于链接视图对ELF进行解析的,与基于执行视图进行解析相比,后面的逻辑基本是一样的,关键是要通过segment找到.dynsym、.dynstr、.rel.plt和rel.dyn,以及它们的项数。
首次通过Program Header Table找到类型为PT_DYNAMIC的段,该的内容其实对应.dynamic,这段的内容对应Elf32_Dyn类型的数组,其结构体如下所示:
```
/* Dynamic structure */
typedef struct {
Elf32_Sword d_tag; /* controls meaning of d_val */
union {
Elf32_Word d_val; /* Multiple meanings - see d_tag */
Elf32_Addr d_ptr; /* program virtual address */
} d_un;
} Elf32_Dyn;
```
通过遍历这个数组,我们可以找到所有的需要的信息,我把它们的对应关系列出来:
- DT_HASH -> .hash
- DT_SYMTAB & DT_SYMENT -> .dynsym
- DT_STRTAB & DT_STRSZ -> .dynstr
- PLTREL(决定REL还是RELA) &(DT_REL | DT_RELA) & (DT_RELSZ | DT_RELASZ ) & (DT_RELENT | DT_RELAENT ) -> .rel.dyn
- DT_JMPREL & DT_PLTRELSZ & (DT_RELENT | DT_RELAENT) -> .rel.plt
- FINI_ARRAY & FINI_ARRAYSZ -> .fini_array
- INIT_ARRAY & INIT_ARRAYSZ -> .init_array
这是查找的相关代码:
```
void getElfInfoBySegmentView(ElfInfo &info, const ElfHandle *handle){
info.handle = handle;
info.elf_base = (uint8_t *) handle->base;
info.ehdr = reinterpret_cast<Elf32_Ehdr *>(info.elf_base);
// may be wrong
info.shdr = reinterpret_cast<Elf32_Shdr *>(info.elf_base + info.ehdr->e_shoff);
info.phdr = reinterpret_cast<Elf32_Phdr *>(info.elf_base + info.ehdr->e_phoff);
info.shstr = NULL;
Elf32_Phdr *dynamic = NULL;
Elf32_Word size = 0;
getSegmentInfo(info, PT_DYNAMIC, &dynamic, &size, &info.dyn);
if(!dynamic){
LOGE("[-] could't find PT_DYNAMIC segment");
exit(-1);
}
info.dynsz = size / sizeof(Elf32_Dyn);
Elf32_Dyn *dyn = info.dyn;
for(int i=0; i<info.dynsz; i++, dyn++){
switch(dyn->d_tag){
case DT_SYMTAB:
info.sym = reinterpret_cast<Elf32_Sym *>(info.elf_base + dyn->d_un.d_ptr);
break;
case DT_STRTAB:
info.symstr = reinterpret_cast<const char *>(info.elf_base + dyn->d_un.d_ptr);
break;
case DT_REL:
info.reldyn = reinterpret_cast<Elf32_Rel *>(info.elf_base + dyn->d_un.d_ptr);
break;
case DT_RELSZ:
info.reldynsz = dyn->d_un.d_val / sizeof(Elf32_Rel);
break;
case DT_JMPREL:
info.relplt = reinterpret_cast<Elf32_Rel *>(info.elf_base + dyn->d_un.d_ptr);
break;
case DT_PLTRELSZ:
info.relpltsz = dyn->d_un.d_val / sizeof(Elf32_Rel);
break;
case DT_HASH:
uint32_t *rawdata = reinterpret_cast<uint32_t *>(info.elf_base + dyn->d_un.d_ptr);
info.nbucket = rawdata[0];
info.nchain = rawdata[1];
info.bucket = rawdata + 2;
info.chain = info.bucket + info.nbucket;
break;
}
}
//because .dynsym is next to .dynstr, so we can caculate the symsz simply
info.symsz = ((uint32_t)info.symstr - (uint32_t)info.sym)/sizeof(Elf32_Sym);
}
```
然而,有一个值我无法通过通过PT_DYNAMIC段得到的,那就是.dynsym的项数,我最后通过变通的方法得到的。由于.dynsym和.dynstr两个节区是相邻的,因此它们两个地址相减,即可得到的.dynsym总长度,再除了sizeof(Elf32_Sym)即可得到.dynsym的项数,如果你有更好的方法,请跟我说说。
#ELF Hook
有了上面的介绍之后,写个ELF Hook就很简单的,我把关键代码贴出来:
```
#define R_ARM_ABS32 0x02
#define R_ARM_GLOB_DAT 0x15
#define R_ARM_JUMP_SLOT 0x16
int elfHook(const char *soname, const char *symbol, void *replace_func, void **old_func){
assert(old_func);
assert(replace_func);
assert(symbol);
ElfHandle* handle = openElfBySoname(soname);
ElfInfo info;
getElfInfoBySegmentView(info, handle);
Elf32_Sym *sym = NULL;
int symidx = 0;
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);
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);
if (replaceFunc(addr, replace_func, old_func))
goto fails;
}
}
fails:
closeElfBySoname(handle);
return 0;
}
```
最后是测试的代码:
```
typedef int (*strlen_fun)(const char *);
strlen_fun old_strlen = NULL;
size_t my_strlen(const char *str){
LOGI("strlen was called.");
int len = old_strlen(str);
return len * 2;
}
strlen_fun global_strlen1 = (strlen_fun)strlen;
strlen_fun global_strlen2 = (strlen_fun)strlen;
#define SHOW(x) LOGI("%s is %d", #x, x)
extern "C" jint Java_com_example_allhookinone_HookUtils_elfhook(JNIEnv *env, jobject thiz){
const char *str = "helloworld";
strlen_fun local_strlen1 = (strlen_fun)strlen;
strlen_fun local_strlen2 = (strlen_fun)strlen;
int len0 = global_strlen1(str);
int len1 = global_strlen2(str);
int len2 = local_strlen1(str);
int len3 = local_strlen2(str);
int len4 = strlen(str);
int len5 = strlen(str);
LOGI("hook before:");
SHOW(len0);
SHOW(len1);
SHOW(len2);
SHOW(len3);
SHOW(len4);
SHOW(len5);
elfHook("libonehook.so", "strlen", (void *)my_strlen, (void **)&old_strlen);
len0 = global_strlen1(str);
len1 = global_strlen2(str);
len2 = local_strlen1(str);
len3 = local_strlen2(str);
len4 = strlen(str);
len5 = strlen(str);
LOGI("hook after:");
SHOW(len0);
SHOW(len1);
SHOW(len2);
SHOW(len3);
SHOW(len4);
SHOW(len5);
return 0;
}
```
首次通过Program Header Table找到类型为PT_DYNAMIC的段,该的内容其实对应.dynamic,这段的内容对应Elf32_Dyn类型的数组,其结构体如下所示:
```
/* Dynamic structure */
typedef struct {
Elf32_Sword d_tag; /* controls meaning of d_val */
union {
Elf32_Word d_val; /* Multiple meanings - see d_tag */
Elf32_Addr d_ptr; /* program virtual address */
} d_un;
} Elf32_Dyn;
```
通过遍历这个数组,我们可以找到所有的需要的信息,我把它们的对应关系列出来:
- DT_HASH -> .hash
- DT_SYMTAB & DT_SYMENT -> .dynsym
- DT_STRTAB & DT_STRSZ -> .dynstr
- PLTREL(决定REL还是RELA) &(DT_REL | DT_RELA) & (DT_RELSZ | DT_RELASZ ) & (DT_RELENT | DT_RELAENT ) -> .rel.dyn
- DT_JMPREL & DT_PLTRELSZ & (DT_RELENT | DT_RELAENT) -> .rel.plt
- FINI_ARRAY & FINI_ARRAYSZ -> .fini_array
- INIT_ARRAY & INIT_ARRAYSZ -> .init_array
这是查找的相关代码:
```
void getElfInfoBySegmentView(ElfInfo &info, const ElfHandle *handle){
info.handle = handle;
info.elf_base = (uint8_t *) handle->base;
info.ehdr = reinterpret_cast<Elf32_Ehdr *>(info.elf_base);
// may be wrong
info.shdr = reinterpret_cast<Elf32_Shdr *>(info.elf_base + info.ehdr->e_shoff);
info.phdr = reinterpret_cast<Elf32_Phdr *>(info.elf_base + info.ehdr->e_phoff);
info.shstr = NULL;
Elf32_Phdr *dynamic = NULL;
Elf32_Word size = 0;
getSegmentInfo(info, PT_DYNAMIC, &dynamic, &size, &info.dyn);
if(!dynamic){
LOGE("[-] could't find PT_DYNAMIC segment");
exit(-1);
}
info.dynsz = size / sizeof(Elf32_Dyn);
Elf32_Dyn *dyn = info.dyn;
for(int i=0; i<info.dynsz; i++, dyn++){
switch(dyn->d_tag){
case DT_SYMTAB:
info.sym = reinterpret_cast<Elf32_Sym *>(info.elf_base + dyn->d_un.d_ptr);
break;
case DT_STRTAB:
info.symstr = reinterpret_cast<const char *>(info.elf_base + dyn->d_un.d_ptr);
break;
case DT_REL:
info.reldyn = reinterpret_cast<Elf32_Rel *>(info.elf_base + dyn->d_un.d_ptr);
break;
case DT_RELSZ:
info.reldynsz = dyn->d_un.d_val / sizeof(Elf32_Rel);
break;
case DT_JMPREL:
info.relplt = reinterpret_cast<Elf32_Rel *>(info.elf_base + dyn->d_un.d_ptr);
break;
case DT_PLTRELSZ:
info.relpltsz = dyn->d_un.d_val / sizeof(Elf32_Rel);
break;
case DT_HASH:
uint32_t *rawdata = reinterpret_cast<uint32_t *>(info.elf_base + dyn->d_un.d_ptr);
info.nbucket = rawdata[0];
info.nchain = rawdata[1];
info.bucket = rawdata + 2;
info.chain = info.bucket + info.nbucket;
break;
}
}
//because .dynsym is next to .dynstr, so we can caculate the symsz simply
info.symsz = ((uint32_t)info.symstr - (uint32_t)info.sym)/sizeof(Elf32_Sym);
}
```
然而,有一个值我无法通过通过PT_DYNAMIC段得到的,那就是.dynsym的项数,我最后通过变通的方法得到的。由于.dynsym和.dynstr两个节区是相邻的,因此它们两个地址相减,即可得到的.dynsym总长度,再除了sizeof(Elf32_Sym)即可得到.dynsym的项数,如果你有更好的方法,请跟我说说。
#ELF Hook
有了上面的介绍之后,写个ELF Hook就很简单的,我把关键代码贴出来:
```
#define R_ARM_ABS32 0x02
#define R_ARM_GLOB_DAT 0x15
#define R_ARM_JUMP_SLOT 0x16
int elfHook(const char *soname, const char *symbol, void *replace_func, void **old_func){
assert(old_func);
assert(replace_func);
assert(symbol);
ElfHandle* handle = openElfBySoname(soname);
ElfInfo info;
getElfInfoBySegmentView(info, handle);
Elf32_Sym *sym = NULL;
int symidx = 0;
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);
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);
if (replaceFunc(addr, replace_func, old_func))
goto fails;
}
}
fails:
closeElfBySoname(handle);
return 0;
}
```
最后是测试的代码:
```
typedef int (*strlen_fun)(const char *);
strlen_fun old_strlen = NULL;
size_t my_strlen(const char *str){
LOGI("strlen was called.");
int len = old_strlen(str);
return len * 2;
}
strlen_fun global_strlen1 = (strlen_fun)strlen;
strlen_fun global_strlen2 = (strlen_fun)strlen;
#define SHOW(x) LOGI("%s is %d", #x, x)
extern "C" jint Java_com_example_allhookinone_HookUtils_elfhook(JNIEnv *env, jobject thiz){
const char *str = "helloworld";
strlen_fun local_strlen1 = (strlen_fun)strlen;
strlen_fun local_strlen2 = (strlen_fun)strlen;
int len0 = global_strlen1(str);
int len1 = global_strlen2(str);
int len2 = local_strlen1(str);
int len3 = local_strlen2(str);
int len4 = strlen(str);
int len5 = strlen(str);
LOGI("hook before:");
SHOW(len0);
SHOW(len1);
SHOW(len2);
SHOW(len3);
SHOW(len4);
SHOW(len5);
elfHook("libonehook.so", "strlen", (void *)my_strlen, (void **)&old_strlen);
len0 = global_strlen1(str);
len1 = global_strlen2(str);
len2 = local_strlen1(str);
len3 = local_strlen2(str);
len4 = strlen(str);
len5 = strlen(str);
LOGI("hook after:");
SHOW(len0);
SHOW(len1);
SHOW(len2);
SHOW(len3);
SHOW(len4);
SHOW(len5);
return 0;
}
```
从打印结果可以发现,local_strlen1和local_strlen2正所上面所说,并没有受影响,但如果函数再次被调用,则生效了,原因不解析了。测试结果就不发了,留给你们试吧。
#GitHup地址
完整代码,见https://github.com/boyliang/AllHookInOne.git*转载请注明来自看雪论坛@PEdiy.com