起因
在测试某加固过程中,由于so进行了加壳(upx变种),导致常规的gothook,无法hook got表函数。
常规的gothook 方法如下
/**
* \brief 注意使用gothook so必须在maps中能找到内存地址
* \param path
*/
GotHook::GotHook(const char * path)
{
//获取so内存加载地址
pBase = get_module_base(path);
LOGI("%s base:%p", path, pBase);
get_elf_info((Elf32_Addr)pBase, &einfo);
}
void GotHook::hookGotFun(const char * name,void* fun_ptr)
{
long tmpaddr = find_sym_in_rel(&einfo, name);
int pagesize = getpagesize();
long startAddress = tmpaddr & ~(pagesize - 1);
if (mprotect((void*)startAddress, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
{
LOGE("mprotect failed");
}
*(int *)tmpaddr = (int)fun_ptr;
}
void* GotHook::get_module_base(const char* module_name)
{
FILE *fp;
long addr = 0;
char *pch;
char filename[32];
char line[1024];
int pid = getpid();
if (pid < 0)
{
/* self process */
snprintf(filename, sizeof(filename), "/proc/self/maps");
}
else
{
snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
}
//打开maps文件
fp = fopen(filename, "r");
//获取其内存地址
if (fp != NULL)
{
while (fgets(line, sizeof(line), fp))
{
if (strstr(line, module_name))
{
pch = strtok(line, "-");
addr = strtoul(pch, NULL, 16);
//if ( addr == 0x8000 )
// addr = 0;
break;
}
}
fclose(fp);
}
return (void *)addr;
}
unsigned long GotHook::find_sym_in_rel(elf_info* einfo, const char* sym_name)
{
Elf32_Rel rel;
Elf32_Sym sym;
unsigned int i;
char *str = NULL;
unsigned long ret;
struct dyn_info dinfo;
LOGI("find sym in rel %p %s \n", (void*)einfo->base, sym_name);
get_dyn_info(einfo, &dinfo);
for (i = 0; i < dinfo.nrels; i++) {
memcpy(&rel, (void*)((unsigned long)(dinfo.jmprel + i * sizeof(Elf32_Rel))), sizeof(Elf32_Rel));
if (DEBUG)
{
LOGI("rel addr %p\n", &rel);
}
if (ELF32_R_SYM(rel.r_info)) {
memcpy(&sym, (void*)(dinfo.symtab + ELF32_R_SYM(rel.r_info) * sizeof(Elf32_Sym)), sizeof(Elf32_Sym));
str = (char*)(dinfo.strtab + sym.st_name);
if (DEBUG)
{
LOGI(" str-> %s\n", str);
}
if (strcmp(str, sym_name) == 0) {
break;
}
// free(str);
}
}
if (i == dinfo.nrels)
ret = 0;
else {
ret = (unsigned long)((IS_DYN(einfo) ? (char*)einfo->base : 0) + rel.r_offset);
}
return ret;
}
void GotHook::get_elf_info(Elf32_Addr base, elf_info * einfo)
{
int i = 0;
//获取pid
einfo->pid = getpid();
//内存地址
einfo->base = (void*)base;
memcpy(&einfo->ehdr, einfo->base, sizeof(Elf32_Ehdr));
einfo->phdr_addr = (Elf32_Addr)((char*)einfo->base + einfo->ehdr.e_phoff);
memcpy(&einfo->phdr, (void*)einfo->phdr_addr, sizeof(Elf32_Phdr));
if (DEBUG)
{
LOGI("dump %s %d phdr\n", einfo->ehdr, einfo->ehdr.e_phnum);
}
for (i = 0; i < einfo->ehdr.e_phnum; i++) {
Elf32_Phdr phdr;
memcpy(&phdr, (void*)(einfo->phdr_addr + i * sizeof(Elf32_Phdr)), sizeof(Elf32_Phdr));
}
while (einfo->phdr.p_type != PT_DYNAMIC) {
memcpy(&einfo->phdr, (void*)(einfo->phdr_addr += sizeof(Elf32_Phdr)), sizeof(Elf32_Phdr));
}
einfo->dynaddr = (Elf32_Word)(IS_DYN(einfo) ? einfo->base : 0) + einfo->phdr.p_vaddr;
memcpy(&einfo->dyn, (void*)(einfo->dynaddr), sizeof(Elf32_Dyn));
while (einfo->dyn.d_tag != DT_PLTGOT) {
memcpy(&einfo->dyn, (void*)(einfo->dynaddr + i * sizeof(Elf32_Dyn)), sizeof(Elf32_Dyn));
i++;
}
einfo->got =(Elf32_Word)((IS_DYN(einfo) ? (char*)einfo->base : 0) + (Elf32_Word)einfo->dyn.d_un.d_ptr);
memcpy(&einfo->map_addr, (void*)(einfo->got + 4), 4);
}
void GotHook::get_dyn_info(elf_info* einfo, dyn_info* dinfo)
{
Elf32_Dyn dyn;
int i = 0;
if (DEBUG)
{
LOGI("get_dyn_info 0x%08x...\n", einfo->dynaddr);
}
memcpy(&dyn, (void*)(einfo->dynaddr + i * sizeof(Elf32_Dyn)), sizeof(Elf32_Dyn));
i++;
while (dyn.d_tag) {
switch (dyn.d_tag) {
case DT_SYMTAB:
LOGI("DT_SYMTAB");
dinfo->symtab = (Elf32_Addr)(IS_DYN(einfo) ? einfo->base : 0) + dyn.d_un.d_ptr;
break;
case DT_STRTAB:
dinfo->strtab = (Elf32_Addr)(IS_DYN(einfo) ? einfo->base : 0) + dyn.d_un.d_ptr;
LOGI("DT_STRTAB");
break;
case DT_JMPREL:
dinfo->jmprel = (Elf32_Addr)(IS_DYN(einfo) ? einfo->base : 0) + dyn.d_un.d_ptr;
LOGI("DT_JMPREL");
LOGI("jmprel\t %08x\n", dinfo->jmprel);
break;
case DT_PLTRELSZ:
LOGI("DT_PLTRELSZ");
dinfo->totalrelsize = dyn.d_un.d_val;
LOGI("totalrelsize %d\n", dinfo->totalrelsize);
break;
case DT_RELAENT:
LOGI("DT_RELAENT");
dinfo->relsize = dyn.d_un.d_val;
LOGI("relsize %d\n", dinfo->relsize);
break;
case DT_RELENT:
LOGI("DT_RELENT");
dinfo->relsize = dyn.d_un.d_val;
LOGI("relsize2 %d\n", dinfo->relsize);
break;
}
memcpy(&dyn, (void*)(einfo->dynaddr + i * sizeof(Elf32_Dyn)), sizeof(Elf32_Dyn));
i++;
}
if (dinfo->relsize == 0) {
dinfo->relsize = 8;
}
dinfo->nrels = dinfo->totalrelsize / dinfo->relsize;
}
在maps中查找son内存地址,然后进行hook,这种方法对加了“壳”,不起作用,具体原因不太了解。
修改
查看dlopen 函数,根据dlopen返回值,发现其返回值是一个叫“soinfo”的结构体指针。
结构体经过简单精简如下
struct soinfo
{
public:
char name[128];
const Elf32_Phdr* phdr; //Elf32_Phdr 实际内存地址
size_t phnum;
Elf32_Addr entry;
Elf32_Addr base; //SO起始
unsigned size; //内存对齐后占用大小
uint32_t unused1; // DO NOT USE, maintained for compatibility.
Elf32_Dyn* dynamic;; //.dynamic实际内存地址 That's the point.
};
其中比较重要的是“dynamic”,我对elf 加载过程不太熟悉,具体的可以参考下面的文章
ELF Linker学习篇(三)关于链接过程
通过.dynamic段可以拿到重定位好的函数
如下:
bool GotHook::hookAPI19(const char* name, void* funptr)
{
struct soinfo
{
public:
char name[128];
const Elf32_Phdr* phdr; //Elf32_Phdr 实际内存地址
size_t phnum;
Elf32_Addr entry;
Elf32_Addr base; //SO起始
unsigned size; //内存对齐后占用大小
uint32_t unused1; // DO NOT USE, maintained for compatibility.
Elf32_Dyn* dynamic;; //.dynamic实际内存地址 That's the point.
};
//start hook
struct dyn_info dinfo;
soinfo* si = (soinfo*)handle;
for (Elf32_Dyn* d = si->dynamic; d->d_tag != DT_NULL; ++d)
{
switch (d->d_tag)
{
case DT_SYMTAB:
LOGI("DT_SYMTAB");
dinfo.symtab = (Elf32_Addr)(si->base + d->d_un.d_ptr);
break;
case DT_STRTAB:
dinfo.strtab = (Elf32_Addr)(si->base + d->d_un.d_ptr);
LOGI("DT_STRTAB");
break;
case DT_JMPREL:
dinfo.jmprel = (Elf32_Addr)(si->base + d->d_un.d_ptr);
LOGI("DT_JMPREL");
LOGI("jmprel\t %08x\n", dinfo.jmprel);
break;
case DT_PLTRELSZ:
LOGI("DT_PLTRELSZ");
dinfo.totalrelsize = d->d_un.d_val;
LOGI("totalrelsize %d\n", dinfo.totalrelsize);
break;
case DT_RELAENT:
LOGI("DT_RELAENT");
dinfo.relsize = d->d_un.d_val;
LOGI("relsize %d\n", dinfo.relsize);
break;
case DT_RELENT:
LOGI("DT_RELENT");
dinfo.relsize = d->d_un.d_val;
LOGI("relsize2 %d\n", dinfo.relsize);
break;
}
}
if (dinfo.relsize == 0)
{
dinfo.relsize = 8;
}
dinfo.nrels = dinfo.totalrelsize / dinfo.relsize;
Elf32_Rel rel;
Elf32_Sym sym;
int i;
for (i = 0; i < dinfo.nrels; i++)
{
memcpy(&rel, (void*)((unsigned long)(dinfo.jmprel + i * sizeof(Elf32_Rel))), sizeof(Elf32_Rel));
//LOGE("rel addr %p\n", &rel);
if (ELF32_R_SYM(rel.r_info))
{
memcpy(&sym, (void*)(dinfo.symtab + ELF32_R_SYM(rel.r_info) * sizeof(Elf32_Sym)), sizeof(Elf32_Sym));
char* str = (char*)(dinfo.strtab + sym.st_name);
//LOGE(" str-> %s\n", str);
// free(str);
if (strcmp(str, name) == 0)
{
LOGE("[+] found address success");
break;
}
}
}
unsigned long ret;
if (i == dinfo.nrels)
ret = 0;
else
{
ret = (unsigned long)((si->base) + rel.r_offset);
}
if (ret == 0)
{
LOGE("[-] get offset fail");
return false;
}
int pagesize = getpagesize();
long startAddress = ret & ~(pagesize - 1);
//modify memory address to be writable
if (mprotect((void*)startAddress, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
{
LOGE("mprotect failed");
return false;
}
*(int *)ret = (int)funptr;
LOGE("[+] hook success");
return true;
}
做的比较着急,也没具体了解原理,中间可能有不少错误。经过发现是没问题的。其他的慢慢补充
网友评论