Android中GOT表HOOK手动实现续

上篇实现了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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值